mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 01:54:43 +00:00
Create landing page (#321)
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { useState } from 'react';
|
||||
import { useRouteLoaderData } from 'react-router';
|
||||
import type { loader as rootLoader } from '../../../root';
|
||||
import { biergartenThemes, defaultThemeName, themeCookieName, type ThemeName } from '../themes';
|
||||
|
||||
// The semantic aliases resolve at :root, so the attribute has to land on <html>
|
||||
// rather than a nested element for the whole document to switch.
|
||||
const applyTheme = (theme: ThemeName) => {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
document.cookie = `${themeCookieName}=${theme}; path=/; max-age=${60 * 60 * 24 * 365}; SameSite=Lax`;
|
||||
};
|
||||
|
||||
interface ThemeSegmentedControlProps {
|
||||
/** `short` renders "Lager"; `full` renders "Biergarten Lager". */
|
||||
labels?: 'full' | 'short';
|
||||
className?: string;
|
||||
onChange?: (theme: ThemeName) => void;
|
||||
}
|
||||
|
||||
const ThemeSegmentedControl = ({
|
||||
labels = 'full',
|
||||
className = 'join join-vertical sm:join-horizontal',
|
||||
onChange,
|
||||
}: ThemeSegmentedControlProps) => {
|
||||
const rootTheme = useRouteLoaderData<typeof rootLoader>('root')?.theme ?? defaultThemeName;
|
||||
const [selectedTheme, setSelectedTheme] = useState<ThemeName>(rootTheme);
|
||||
|
||||
return (
|
||||
<div className={className} 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);
|
||||
onChange?.(theme.value);
|
||||
}}
|
||||
/>
|
||||
{labels === 'short' ? theme.shortLabel : theme.label}
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeSegmentedControl;
|
||||
@@ -1,12 +1,8 @@
|
||||
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`;
|
||||
};
|
||||
import { biergartenThemes, defaultThemeName, type ThemeName } from '../themes';
|
||||
import ThemeSegmentedControl from './ThemeSegmentedControl';
|
||||
|
||||
const ThemeSwitcher = () => {
|
||||
const rootTheme = useRouteLoaderData<typeof rootLoader>('root')?.theme ?? defaultThemeName;
|
||||
@@ -39,35 +35,7 @@ const ThemeSwitcher = () => {
|
||||
<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>
|
||||
<ThemeSegmentedControl onChange={setSelectedTheme} />
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
|
||||
@@ -4,6 +4,8 @@ export type ThemeName =
|
||||
export interface ThemeOption {
|
||||
value: ThemeName;
|
||||
label: string;
|
||||
/** Used where the segmented control is narrow, such as the landing page. */
|
||||
shortLabel: string;
|
||||
vibe: string;
|
||||
}
|
||||
|
||||
@@ -14,21 +16,25 @@ export const biergartenThemes: ThemeOption[] = [
|
||||
{
|
||||
value: 'biergarten-lager',
|
||||
label: 'Biergarten Lager',
|
||||
shortLabel: 'Lager',
|
||||
vibe: 'Muted parchment, mellow amber, daytime beer garden',
|
||||
},
|
||||
{
|
||||
value: 'biergarten-stout',
|
||||
label: 'Biergarten Stout',
|
||||
shortLabel: 'Stout',
|
||||
vibe: 'Charred barrel, deep roast, cozy evening cellar',
|
||||
},
|
||||
{
|
||||
value: 'biergarten-cassis',
|
||||
label: 'Biergarten Cassis',
|
||||
shortLabel: 'Cassis',
|
||||
vibe: 'Blackberry barrel, sour berry dark, vivid night market',
|
||||
},
|
||||
{
|
||||
value: 'biergarten-weizen',
|
||||
label: 'Biergarten Weizen',
|
||||
shortLabel: 'Weizen',
|
||||
vibe: 'Ultra-light young barley, green undertone, bright spring afternoon',
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user