feat: core layout, UI atoms, homepage + behaviour modules
- UI atoms: Chip, Button, SplitCvButton, LangSwitch, ThemeToggle, Card, SectionLabel, TraceMotif, FramedImage - core: Base/Seo/Header/MobileNav/Footer/HintBar/SkipLink/ThemeScript - homepage sections: Hero, ProofStrip, SkillsGrid, ProjectCards, ExperienceTimeline, AboutTeaser, ContactBand + Homepage composition (EN/NO) - behaviour modules: theme, nav (overlay/hint/copy/header), observer (reveal/spy), lightbox, form — progressive enhancement, fail-silent - SEO head component: meta, hreflang from slug map, JSON-LD, OG references - move content data to src/data (avoid Astro reserved content-collection folder) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Theme toggle (A6). The no-flash init in ThemeScript already set data-theme before
|
||||
paint; this only wires the button, persists the choice, and keeps the aria-label
|
||||
describing the *next* action. Fail-silent if the button is absent.
|
||||
*/
|
||||
const html = document.documentElement;
|
||||
const btn = document.getElementById('theme-toggle');
|
||||
|
||||
function labelFor(theme: string): string {
|
||||
if (!btn) return '';
|
||||
return theme === 'dark'
|
||||
? (btn.dataset.toLight ?? '')
|
||||
: (btn.dataset.toDark ?? '');
|
||||
}
|
||||
|
||||
if (btn) {
|
||||
btn.setAttribute('aria-label', labelFor(html.dataset.theme ?? 'dark'));
|
||||
btn.addEventListener('click', () => {
|
||||
const next = html.dataset.theme === 'light' ? 'dark' : 'light';
|
||||
html.dataset.theme = next;
|
||||
try {
|
||||
localStorage.setItem('theme', next);
|
||||
} catch {
|
||||
/* private mode — ignore */
|
||||
}
|
||||
btn.setAttribute('aria-label', labelFor(next));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user