|
49 | 49 | padding: 0 1.5rem; |
50 | 50 | } |
51 | 51 |
|
| 52 | + .layout { |
| 53 | + position: relative; |
| 54 | + padding-left: 3.75rem; |
| 55 | + } |
| 56 | + |
| 57 | + .toc { |
| 58 | + position: fixed; |
| 59 | + top: 2rem; |
| 60 | + left: 1rem; |
| 61 | + width: 2.25rem; |
| 62 | + max-height: calc(100vh - 4rem); |
| 63 | + overflow-y: auto; |
| 64 | + z-index: 20; |
| 65 | + display: flex; |
| 66 | + flex-direction: column; |
| 67 | + gap: 0.45rem; |
| 68 | + padding: 0.35rem; |
| 69 | + border-radius: 999px; |
| 70 | + background: color-mix(in srgb, var(--bg) 80%, transparent); |
| 71 | + border: 1px solid color-mix(in srgb, var(--border) 50%, transparent); |
| 72 | + transition: width 240ms ease, border-radius 240ms ease, background 240ms ease; |
| 73 | + scrollbar-width: thin; |
| 74 | + } |
| 75 | + |
| 76 | + .toc:hover, |
| 77 | + .toc:focus-within, |
| 78 | + .toc.is-expanded { |
| 79 | + width: min(14rem, calc(100vw - 2rem)); |
| 80 | + border-radius: 1rem; |
| 81 | + background: color-mix(in srgb, var(--bg) 94%, transparent); |
| 82 | + } |
| 83 | + |
| 84 | + .toc-link { |
| 85 | + display: flex; |
| 86 | + align-items: center; |
| 87 | + gap: 0.65rem; |
| 88 | + color: inherit; |
| 89 | + text-decoration: none; |
| 90 | + min-height: 1.5rem; |
| 91 | + border-radius: 999px; |
| 92 | + padding: 0.1rem 0.2rem; |
| 93 | + transition: background-color 180ms ease; |
| 94 | + } |
| 95 | + |
| 96 | + .toc-link:hover { |
| 97 | + text-decoration: none; |
| 98 | + background: color-mix(in srgb, var(--accent) 14%, transparent); |
| 99 | + } |
| 100 | + |
| 101 | + .toc-bar { |
| 102 | + height: 0.34rem; |
| 103 | + border-radius: 999px; |
| 104 | + background: var(--fg-secondary); |
| 105 | + opacity: 0.2; |
| 106 | + flex: 0 0 1.15rem; |
| 107 | + transition: opacity 220ms ease, background-color 220ms ease; |
| 108 | + } |
| 109 | + |
| 110 | + .toc-label { |
| 111 | + white-space: nowrap; |
| 112 | + overflow: hidden; |
| 113 | + text-overflow: ellipsis; |
| 114 | + font-size: 0.82rem; |
| 115 | + letter-spacing: 0.01em; |
| 116 | + opacity: 0; |
| 117 | + transform: translateX(-0.2rem); |
| 118 | + transition: opacity 220ms ease, transform 220ms ease; |
| 119 | + color: var(--fg-secondary); |
| 120 | + pointer-events: none; |
| 121 | + } |
| 122 | + |
| 123 | + .toc:hover .toc-label, |
| 124 | + .toc:focus-within .toc-label, |
| 125 | + .toc.is-expanded .toc-label { |
| 126 | + opacity: 1; |
| 127 | + transform: translateX(0); |
| 128 | + pointer-events: auto; |
| 129 | + } |
| 130 | + |
| 131 | + .toc-link:hover .toc-bar, |
| 132 | + .toc-link:focus-visible .toc-bar, |
| 133 | + .toc-link.is-active .toc-bar { |
| 134 | + opacity: 0.95; |
| 135 | + background: var(--accent); |
| 136 | + } |
| 137 | + |
| 138 | + .toc-link.is-active .toc-label { |
| 139 | + color: var(--fg); |
| 140 | + } |
| 141 | + |
| 142 | + .toc-toggle { |
| 143 | + display: none; |
| 144 | + position: fixed; |
| 145 | + top: 1rem; |
| 146 | + left: 1rem; |
| 147 | + z-index: 30; |
| 148 | + border: 1px solid var(--border); |
| 149 | + background: color-mix(in srgb, var(--bg) 94%, transparent); |
| 150 | + color: var(--fg); |
| 151 | + border-radius: 999px; |
| 152 | + font: inherit; |
| 153 | + font-size: 0.9rem; |
| 154 | + padding: 0.42rem 0.8rem; |
| 155 | + cursor: pointer; |
| 156 | + } |
| 157 | + |
52 | 158 | /* ---- Header ---- */ |
53 | 159 | header { |
54 | 160 | padding: 6rem 0 4rem; |
|
199 | 305 |
|
200 | 306 | /* ---- Responsive ---- */ |
201 | 307 | @media (max-width: 520px) { |
| 308 | + .layout { |
| 309 | + padding-left: 0; |
| 310 | + } |
| 311 | + |
| 312 | + .toc-toggle { |
| 313 | + display: inline-flex; |
| 314 | + align-items: center; |
| 315 | + gap: 0.35rem; |
| 316 | + } |
| 317 | + |
| 318 | + .toc { |
| 319 | + top: 3.8rem; |
| 320 | + left: 1rem; |
| 321 | + width: calc(100vw - 2rem); |
| 322 | + max-height: min(70vh, 28rem); |
| 323 | + border-radius: 1rem; |
| 324 | + padding: 0.6rem; |
| 325 | + transform: translateY(-0.6rem); |
| 326 | + opacity: 0; |
| 327 | + pointer-events: none; |
| 328 | + transition: opacity 220ms ease, transform 220ms ease; |
| 329 | + } |
| 330 | + |
| 331 | + .toc .toc-label { |
| 332 | + opacity: 1; |
| 333 | + transform: none; |
| 334 | + pointer-events: auto; |
| 335 | + } |
| 336 | + |
| 337 | + .toc.is-expanded { |
| 338 | + opacity: 1; |
| 339 | + transform: translateY(0); |
| 340 | + pointer-events: auto; |
| 341 | + } |
| 342 | + |
| 343 | + .toc-link { |
| 344 | + border-radius: 0.75rem; |
| 345 | + } |
| 346 | + |
202 | 347 | header { padding: 4rem 0 3rem; } |
203 | 348 | header h1 { font-size: 3rem; } |
204 | 349 | .features { grid-template-columns: 1fr; } |
|
207 | 352 | </head> |
208 | 353 | <body> |
209 | 354 |
|
| 355 | +<div class="layout"> |
| 356 | + <button class="toc-toggle" id="toc-toggle" type="button" aria-controls="toc" aria-expanded="false">☰ Sections</button> |
| 357 | + <nav class="toc" id="toc" aria-label="Table of contents"></nav> |
| 358 | + |
210 | 359 | <div class="container"> |
211 | 360 |
|
212 | 361 | <header> |
@@ -633,5 +782,70 @@ <h2>Flags reference</h2> |
633 | 782 |
|
634 | 783 | </div> |
635 | 784 |
|
| 785 | +<script> |
| 786 | + const sections = Array.from(document.querySelectorAll('section')); |
| 787 | + const toc = document.getElementById('toc'); |
| 788 | + const tocToggle = document.getElementById('toc-toggle'); |
| 789 | + const prefersTouch = window.matchMedia('(hover: none), (pointer: coarse)').matches; |
| 790 | + |
| 791 | + sections.forEach((section, index) => { |
| 792 | + const heading = section.querySelector('h2'); |
| 793 | + if (!heading) return; |
| 794 | + |
| 795 | + const id = heading.id || heading.textContent.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''); |
| 796 | + heading.id = id || `section-${index + 1}`; |
| 797 | + |
| 798 | + const link = document.createElement('a'); |
| 799 | + link.href = `#${heading.id}`; |
| 800 | + link.className = 'toc-link'; |
| 801 | + link.setAttribute('aria-label', heading.textContent.trim()); |
| 802 | + |
| 803 | + const bar = document.createElement('span'); |
| 804 | + bar.className = 'toc-bar'; |
| 805 | + |
| 806 | + const label = document.createElement('span'); |
| 807 | + label.className = 'toc-label'; |
| 808 | + label.textContent = heading.textContent.trim(); |
| 809 | + |
| 810 | + link.append(bar, label); |
| 811 | + toc.append(link); |
| 812 | + }); |
| 813 | + |
| 814 | + const tocLinks = Array.from(toc.querySelectorAll('.toc-link')); |
| 815 | + |
| 816 | + if (prefersTouch) { |
| 817 | + toc.classList.add('is-expanded'); |
| 818 | + tocToggle.hidden = false; |
| 819 | + tocToggle.addEventListener('click', () => { |
| 820 | + const expanded = toc.classList.toggle('is-expanded'); |
| 821 | + tocToggle.setAttribute('aria-expanded', String(expanded)); |
| 822 | + }); |
| 823 | + |
| 824 | + tocLinks.forEach((link) => { |
| 825 | + link.addEventListener('click', () => { |
| 826 | + toc.classList.remove('is-expanded'); |
| 827 | + tocToggle.setAttribute('aria-expanded', 'false'); |
| 828 | + }); |
| 829 | + }); |
| 830 | + } else { |
| 831 | + tocToggle.hidden = true; |
| 832 | + } |
| 833 | + |
| 834 | + const observer = new IntersectionObserver((entries) => { |
| 835 | + entries.forEach((entry) => { |
| 836 | + const id = entry.target.querySelector('h2')?.id; |
| 837 | + if (!id || !entry.isIntersecting) return; |
| 838 | + |
| 839 | + tocLinks.forEach((link) => { |
| 840 | + link.classList.toggle('is-active', link.getAttribute('href') === `#${id}`); |
| 841 | + }); |
| 842 | + }); |
| 843 | + }, { rootMargin: '-45% 0px -45% 0px', threshold: [0, 1] }); |
| 844 | + |
| 845 | + sections.forEach((section) => observer.observe(section)); |
| 846 | +</script> |
| 847 | + |
| 848 | +</div> |
| 849 | + |
636 | 850 | </body> |
637 | 851 | </html> |
0 commit comments