import { useEffect, useState } from 'react'; import { biergartenThemes, defaultThemeName, isBiergartenTheme, themeStorageKey, type ThemeName, } from '../lib/themes'; import type { Route } from './+types/theme'; export function meta({}: Route.MetaArgs) { return [ { title: 'Theme | The Biergarten App' }, { name: 'description', content: 'Theme guide and switcher for The Biergarten App', }, ]; } function applyTheme(theme: ThemeName) { document.documentElement.setAttribute('data-theme', theme); localStorage.setItem(themeStorageKey, theme); } export default function ThemePage() { const [selectedTheme, setSelectedTheme] = useState(() => { if (typeof window === 'undefined') { return defaultThemeName; } const savedTheme = localStorage.getItem(themeStorageKey); return isBiergartenTheme(savedTheme) ? savedTheme : defaultThemeName; }); useEffect(() => { applyTheme(selectedTheme); }, [selectedTheme]); const activeTheme = biergartenThemes.find((theme) => theme.value === selectedTheme) ?? biergartenThemes[0]; return (

Theme Guide

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.

Active theme: {activeTheme.label} — {activeTheme.vibe}

Theme switcher

Pick a theme and preview it immediately.

{biergartenThemes.map((theme) => { const checked = selectedTheme === theme.value; return ( ); })}

Brand colors

Primary
Secondary
Accent
Neutral

Status colors

Info
Success
Warning
Error

Core style outline

  • Warm serif headings paired with clear sans-serif body text
  • Rounded, tactile surfaces with subtle depth and grain
  • Semantic token usage to keep contrast consistent in both themes

Component preview

Theme tokens are applied consistently.
Use semantic colors over hard-coded color values.
); }