61 lines
2.1 KiB
TypeScript
61 lines
2.1 KiB
TypeScript
import { useTranslation } from 'react-i18next'
|
||
import { Monitor, Moon, Sun } from 'lucide-react'
|
||
import { useTheme, type ThemePreference } from '@/stores/ThemeProvider'
|
||
import { cn } from '@/lib/utils'
|
||
|
||
/**
|
||
* Tri-state theme switch: Light / Dark / System.
|
||
*
|
||
* <p>Segmented control с тремя иконками. Активный — `--color-accent`
|
||
* background + `--color-on-accent` foreground. Click меняет `preference`
|
||
* в {@link ThemeProvider}, который пишет в localStorage и переключает
|
||
* `data-theme` на html.
|
||
*
|
||
* <p>System icon показывает «follow OS» — auto-update при OS theme change.
|
||
*/
|
||
export function ThemeSwitch() {
|
||
const { t } = useTranslation()
|
||
const { preference, setPreference } = useTheme()
|
||
|
||
const items: { id: ThemePreference; label: string; icon: typeof Sun }[] = [
|
||
{ id: 'light', label: t('theme.light'), icon: Sun },
|
||
{ id: 'dark', label: t('theme.dark'), icon: Moon },
|
||
{ id: 'system', label: t('theme.system'), icon: Monitor },
|
||
]
|
||
|
||
return (
|
||
<div
|
||
role="radiogroup"
|
||
aria-label={t('theme.label')}
|
||
// h-11 = 44px WCAG AAA touch target per DESIGN.md §13.
|
||
// Segmented control: 3 buttons × w-11 = 132px total width.
|
||
className="inline-flex items-center rounded-md border border-line bg-surface h-11 overflow-hidden"
|
||
>
|
||
{items.map((item) => {
|
||
const Icon = item.icon
|
||
const active = preference === item.id
|
||
return (
|
||
<button
|
||
key={item.id}
|
||
type="button"
|
||
role="radio"
|
||
aria-checked={active}
|
||
aria-label={item.label}
|
||
title={item.label}
|
||
onClick={() => setPreference(item.id)}
|
||
className={cn(
|
||
// w-11 paired с parent h-11 → 44×44 per button.
|
||
'inline-flex h-full w-11 items-center justify-center transition-colors',
|
||
active
|
||
? 'bg-accent text-on-accent'
|
||
: 'text-mute hover:text-ink hover:bg-surface-2',
|
||
)}
|
||
>
|
||
<Icon size={13} strokeWidth={2.25} />
|
||
</button>
|
||
)
|
||
})}
|
||
</div>
|
||
)
|
||
}
|