feat(frontend): add Tabs primitive + useHashTab hook

- useHashTab: pure-TS hook reading window.location.hash, falls back to
  defaultId, hashchange listener cleaned up on unmount
- Tabs: <Tabs items activeId onChange> with tab-underline / tab-underline-active
  recipes and count-pill (rounded-pill exception per DESIGN.md)
- index.css: tab-underline*, tab-count-pill*, alert-*, table-compact recipes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Knacky
2026-06-21 22:10:59 +02:00
parent 89fb38b107
commit bca39dcca7
3 changed files with 112 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
import { useCallback, useEffect, useState } from 'react';
function readHash(defaultId: string): string {
const hash = window.location.hash.slice(1); // strip leading '#'
return hash || defaultId;
}
export function useHashTab(defaultId: string): [string, (id: string) => void] {
const [activeId, setActiveId] = useState<string>(() => readHash(defaultId));
useEffect(() => {
function onHashChange() {
setActiveId(readHash(defaultId));
}
window.addEventListener('hashchange', onHashChange);
return () => window.removeEventListener('hashchange', onHashChange);
}, [defaultId]);
const navigate = useCallback((id: string) => {
window.location.hash = id;
setActiveId(id);
}, []);
return [activeId, navigate];
}