Files
the-biergarten-app/web/frontend/app/features/theme/components/ThemeSwitcher.tsx
Aaron Po 31da1d557c Handle fetch fail in client app, fix data binding in location data (#243)
* Improve error handling for server rendered routes

* Fix data binding issue for country/state codes (use alias in sql)
2026-08-24 01:09:48 -04:00

78 lines
3.5 KiB
TypeScript

import { useState } from 'react';
import { useRouteLoaderData } from 'react-router';
import type { loader as rootLoader } from '../../../root';
import { biergartenThemes, defaultThemeName, themeCookieName, type ThemeName } from '../themes';
const applyTheme = (theme: ThemeName) => {
document.documentElement.setAttribute('data-theme', theme);
document.cookie = `${themeCookieName}=${theme}; path=/; max-age=${60 * 60 * 24 * 365}; SameSite=Lax`;
};
const ThemeSwitcher = () => {
const rootTheme = useRouteLoaderData<typeof rootLoader>('root')?.theme ?? defaultThemeName;
const [selectedTheme, setSelectedTheme] = useState<ThemeName>(rootTheme);
const activeTheme =
biergartenThemes.find((theme) => theme.value === selectedTheme) ?? biergartenThemes[0];
return (
<>
<section className="card border border-base-300 bg-base-100 shadow-xl">
<div className="card-body gap-4">
<h1 className="card-title text-3xl sm:text-4xl">Theme Guide</h1>
<p className="text-base-content/70">
Four themes, four moods — from the sun-bleached clarity of a Weizen
afternoon to the deep berry dark of a Cassis barrel. Every theme shares the
same semantic token structure so components stay consistent while the
atmosphere shifts completely.
</p>
<div className="alert alert-info alert-soft">
<span>
Active theme: <strong>{activeTheme.label}</strong> — {activeTheme.vibe}
</span>
</div>
</div>
</section>
<section className="card border border-base-300 bg-base-100 shadow-xl">
<div className="card-body gap-4">
<h2 className="card-title text-2xl">Theme switcher</h2>
<p className="text-base-content/70">Pick a theme and preview it immediately.</p>
<div
className="join join-vertical sm:join-horizontal"
role="radiogroup"
aria-label="Theme selector"
>
{biergartenThemes.map((theme) => {
const checked = selectedTheme === theme.value;
return (
<label
key={theme.value}
className={`btn join-item ${checked ? 'btn-primary' : 'btn-outline'}`}
>
<input
type="radio"
name="theme"
value={theme.value}
className="sr-only"
checked={checked}
onChange={() => {
setSelectedTheme(theme.value);
applyTheme(theme.value);
}}
/>
{theme.label}
</label>
);
})}
</div>
</div>
</section>
</>
);
};
export default ThemeSwitcher;