|
64 | 64 | <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"> |
65 | 65 |
|
66 | 66 | <!-- Tailwind CSS CDN --> |
| 67 | + <link rel="preconnect" href="https://cdn.tailwindcss.com"> |
67 | 68 | <script src="https://cdn.tailwindcss.com"></script> |
68 | 69 | <script> |
69 | 70 | tailwind.config = { |
|
82 | 83 | </script> |
83 | 84 |
|
84 | 85 | <!-- Prism.js for syntax highlighting --> |
| 86 | + <link rel="preconnect" href="https://cdnjs.cloudflare.com"> |
85 | 87 | <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet" /> |
86 | 88 |
|
87 | 89 | <style> |
88 | 90 | html { |
89 | 91 | scroll-behavior: smooth; |
90 | 92 | } |
91 | 93 |
|
| 94 | + section[id] { |
| 95 | + scroll-margin-top: 4.5rem; |
| 96 | + } |
| 97 | + |
92 | 98 | /* Skip link for accessibility */ |
93 | 99 | .skip-link { |
94 | 100 | position: absolute; |
|
136 | 142 |
|
137 | 143 | .tab-button { |
138 | 144 | border-bottom: 3px solid transparent; |
139 | | - transition: all 0.3s ease; |
| 145 | + transition: color 0.3s ease, border-color 0.3s ease; |
140 | 146 | } |
141 | 147 |
|
142 | 148 | .tab-button.active { |
|
171 | 177 | color: white; |
172 | 178 | font-size: 0.875rem; |
173 | 179 | cursor: pointer; |
174 | | - transition: all 0.2s; |
| 180 | + transition: background-color 0.2s, border-color 0.2s, color 0.2s; |
175 | 181 | } |
176 | 182 |
|
177 | 183 | .copy-button:hover { |
|
182 | 188 | background: #10b981; |
183 | 189 | border-color: #10b981; |
184 | 190 | } |
| 191 | + |
| 192 | + h1, |
| 193 | + h2, |
| 194 | + h3 { |
| 195 | + text-wrap: balance; |
| 196 | + } |
| 197 | + |
| 198 | + a:focus-visible, |
| 199 | + button:focus-visible { |
| 200 | + outline: 3px solid #2563eb; |
| 201 | + outline-offset: 2px; |
| 202 | + } |
| 203 | + |
| 204 | + @media (prefers-reduced-motion: reduce) { |
| 205 | + html { |
| 206 | + scroll-behavior: auto; |
| 207 | + } |
| 208 | + |
| 209 | + .hover-lift, |
| 210 | + .tab-button, |
| 211 | + .copy-button { |
| 212 | + transition: none; |
| 213 | + } |
| 214 | + } |
185 | 215 | </style> |
186 | 216 | </head> |
187 | 217 | <body class="font-sans antialiased bg-white text-gray-900"> |
|
256 | 286 | const tabButtons = document.querySelectorAll('.tab-button'); |
257 | 287 | const tabContents = document.querySelectorAll('.tab-content'); |
258 | 288 |
|
| 289 | + const setActiveTab = (targetTabId) => { |
| 290 | + const targetButton = document.querySelector(`[data-tab="${targetTabId}"]`); |
| 291 | + const targetContent = document.getElementById(targetTabId); |
| 292 | + |
| 293 | + if (!targetButton || !targetContent) { |
| 294 | + return; |
| 295 | + } |
| 296 | + |
| 297 | + // Remove active class and aria-selected from all buttons and contents |
| 298 | + tabButtons.forEach(btn => { |
| 299 | + btn.classList.remove('active'); |
| 300 | + btn.setAttribute('aria-selected', 'false'); |
| 301 | + }); |
| 302 | + tabContents.forEach(content => content.classList.remove('active')); |
| 303 | + |
| 304 | + // Add active class and aria-selected to clicked button and corresponding content |
| 305 | + targetButton.classList.add('active'); |
| 306 | + targetButton.setAttribute('aria-selected', 'true'); |
| 307 | + targetContent.classList.add('active'); |
| 308 | + }; |
| 309 | + |
| 310 | + const initialHash = window.location.hash.replace('#', ''); |
| 311 | + if (initialHash) { |
| 312 | + setActiveTab(initialHash); |
| 313 | + } |
| 314 | + |
259 | 315 | tabButtons.forEach(button => { |
260 | 316 | button.addEventListener('click', function() { |
261 | 317 | const targetTab = this.getAttribute('data-tab'); |
262 | | - |
263 | | - // Remove active class and aria-selected from all buttons and contents |
264 | | - tabButtons.forEach(btn => { |
265 | | - btn.classList.remove('active'); |
266 | | - btn.setAttribute('aria-selected', 'false'); |
267 | | - }); |
268 | | - tabContents.forEach(content => content.classList.remove('active')); |
269 | | - |
270 | | - // Add active class and aria-selected to clicked button and corresponding content |
271 | | - this.classList.add('active'); |
272 | | - this.setAttribute('aria-selected', 'true'); |
273 | | - document.getElementById(targetTab).classList.add('active'); |
| 318 | + setActiveTab(targetTab); |
| 319 | + window.location.hash = targetTab; |
274 | 320 | }); |
275 | 321 | }); |
276 | 322 |
|
|
0 commit comments