Files
tessera-ctl/apps/web/src/components/theme-toggle.tsx
T
schalli 6039387857 feat(01-02): portal layout with header, sidebar, theme toggle, locale switcher
- Zustand sidebar store with persist middleware (collapse/expand, mobile open)
- Sticky header with logo, breadcrumb, theme toggle, locale switcher, user avatar
- Collapsible sidebar with Dashboard/Marketplace nav, accordion categories, footer
- Mobile responsive: hamburger menu with overlay sidebar on small screens
- Theme toggle cycling light/dark/system with mounted guard
- Locale switcher setting NEXT_LOCALE cookie with router.refresh
- Empty dashboard state with grid icon, "Keine Widgets aktiv" text, add button
- AppShell composing header + sidebar + responsive main content area
- All user-visible strings via useTranslations (UI-03 compliance)
2026-06-18 10:27:46 +02:00

99 lines
2.7 KiB
TypeScript

'use client';
import { useTheme } from 'next-themes';
import { useTranslations } from 'next-intl';
import { useEffect, useState } from 'react';
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
const t = useTranslations('theme');
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
function cycleTheme() {
if (theme === 'light') {
setTheme('dark');
} else if (theme === 'dark') {
setTheme('system');
} else {
setTheme('light');
}
}
const label = mounted
? theme === 'light'
? t('light')
: theme === 'dark'
? t('dark')
: t('system')
: '';
return (
<button
onClick={cycleTheme}
className="inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-muted hover:text-foreground transition-colors"
aria-label={label}
title={label}
>
{!mounted ? (
<span className="h-5 w-5" />
) : theme === 'light' ? (
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="12" cy="12" r="5" />
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</svg>
) : theme === 'dark' ? (
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
<line x1="8" y1="21" x2="16" y2="21" />
<line x1="12" y1="17" x2="12" y2="21" />
</svg>
)}
</button>
);
}