Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| --- | |
| const { class: className, ...props } = Astro.props; | |
| const wrapperClass = ["tabs", className].filter(Boolean).join(" "); | |
| --- | |
| <div class={wrapperClass} {...props}> | |
| <div class="tabs__nav" role="tablist"></div> | |
| <div class="tabs__panels"> | |
| <slot /> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| document.querySelectorAll('.tabs').forEach((tabsEl) => { | |
| const nav = tabsEl.querySelector('.tabs__nav') as HTMLElement; | |
| const panels = Array.from(tabsEl.querySelectorAll(':scope > .tabs__panels > .tab-panel')) as HTMLElement[]; | |
| if (!nav || panels.length === 0) return; | |
| panels.forEach((panel, i) => { | |
| const title = panel.dataset.tabTitle || `Tab ${i + 1}`; | |
| const btn = document.createElement('button'); | |
| btn.className = 'tabs__btn'; | |
| btn.role = 'tab'; | |
| btn.textContent = title; | |
| btn.setAttribute('aria-selected', 'false'); | |
| btn.addEventListener('click', () => activate(i)); | |
| nav.appendChild(btn); | |
| }); | |
| const buttons = Array.from(nav.querySelectorAll('.tabs__btn')) as HTMLButtonElement[]; | |
| function activate(index: number) { | |
| buttons.forEach((b, i) => { | |
| const active = i === index; | |
| b.classList.toggle('tabs__btn--active', active); | |
| b.setAttribute('aria-selected', String(active)); | |
| }); | |
| panels.forEach((p, i) => { | |
| p.hidden = i !== index; | |
| }); | |
| } | |
| // Keyboard navigation | |
| nav.addEventListener('keydown', (e) => { | |
| const current = buttons.findIndex(b => b === document.activeElement); | |
| if (current < 0) return; | |
| let next = current; | |
| if (e.key === 'ArrowRight') next = (current + 1) % buttons.length; | |
| else if (e.key === 'ArrowLeft') next = (current - 1 + buttons.length) % buttons.length; | |
| else if (e.key === 'Home') next = 0; | |
| else if (e.key === 'End') next = buttons.length - 1; | |
| else return; | |
| e.preventDefault(); | |
| buttons[next].focus(); | |
| activate(next); | |
| }); | |
| activate(0); | |
| }); | |
| }); | |
| </script> | |
| <style> | |
| .tabs { | |
| margin: 0 0 var(--spacing-4); | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--table-border-radius); | |
| background: var(--surface-bg); | |
| } | |
| .tabs__nav { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 0; | |
| border-bottom: 1px solid var(--border-color); | |
| overflow-x: auto; | |
| -webkit-overflow-scrolling: touch; | |
| scrollbar-width: none; | |
| } | |
| .tabs__nav::-webkit-scrollbar { | |
| display: none; | |
| } | |
| .tabs__nav :global(.tabs__btn) { | |
| flex: 1 1 0; | |
| padding: var(--spacing-2) var(--spacing-3); | |
| text-align: center; | |
| border: none ; | |
| border-bottom: 2px solid transparent ; | |
| border-radius: 0 ; | |
| background: none ; | |
| font: inherit; | |
| font-size: 0.9em; | |
| font-weight: 600; | |
| color: var(--text-muted) ; | |
| cursor: pointer; | |
| white-space: nowrap; | |
| margin-bottom: -1px; | |
| transition: color 150ms ease, border-color 150ms ease; | |
| filter: none ; | |
| } | |
| .tabs__nav :global(.tabs__btn:hover) { | |
| color: var(--text-color) ; | |
| filter: none ; | |
| } | |
| .tabs__nav :global(.tabs__btn--active) { | |
| color: var(--primary-color) ; | |
| border-bottom-color: var(--primary-color) ; | |
| } | |
| .tabs__nav :global(.tabs__btn:focus-visible) { | |
| outline: 2px solid var(--primary-color); | |
| outline-offset: -2px; | |
| border-radius: 2px; | |
| } | |
| .tabs__panels :global(.tab-panel) { | |
| padding: 8px; | |
| } | |
| .tabs__panels :global(.tab-panel > *:first-child) { | |
| margin-top: 0 ; | |
| } | |
| .tabs__panels :global(.tab-panel > *:last-child) { | |
| margin-bottom: 0 ; | |
| padding-bottom: 0 ; | |
| } | |
| </style> | |