feat(ui): Dunkelmodus fuer Shop und Admin mit Umschalter

Rollen-Farben statt reiner Markenfarben, damit hell und dunkel aus einem
Satz Klassen entstehen:
- heading (Ueberschriften, Links, Rahmen): hell Waldgruen, dunkel Creme
- surface (abgesetzte Flaeche): hell Creme, dunkel angehobenes Dunkelgruen
- forest bleibt die dunkle Markenflaeche (Hero, Baender, Knoepfe,
  Admin-Leiste), cream der helle Text darauf
- honey-deep als fester Honigton fuer Flaechen; honey-dark (Text) wird
  im Dunkelmodus heller und bleibt lesbar
Codemod ueber src: text-/border-forest -> *-heading, bg-cream ->
bg-surface, bg-honey-dark -> bg-honey-deep. Im hellen Modus haben die
Rollen exakt die bisherigen Werte - das Erscheinungsbild bleibt gleich.

Umschalter (components/theme-toggle.tsx) Automatisch -> Hell -> Dunkel in
Kopfzeile, Mobilmenue und Admin-Leiste. Die Wahl liegt im Cookie "theme"
(nur light/dark, geprueft in lib/theme.ts); der Server setzt data-theme
schon beim Ausliefern, nichts blitzt hell auf. "Automatisch" folgt der
Systemeinstellung. Gewaehlter und automatischer Dunkelblock in
globals.css werden per Test auf Gleichheit geprueft.

Logo, Biene und Select-Pfeil nutzen die Rollenfarben. In der Kopfzeile
steht "Honig entdecken" zwischen 1280 und 1535 px nicht mehr doppelt
neben "Shop" (sonst stiess die Navigation an), der Slogan erscheint erst
ab 1680 px.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-26 06:12:44 +02:00
1 parent b77516b9f5
commit 6950657c91
95 files changed
+514 -272

No files matched your search

+9 -9
View File
@@ -109,8 +109,8 @@ export default async function ShopPage(props: PageProps<"/shop">) {
aria-current={isActive ? "page" : undefined}
className={`inline-flex h-11 items-center gap-2 px-5 text-sm tracking-wide-caps border transition-[color,background-color,border-color] duration-300 ${
isActive
? "bg-forest text-cream border-forest"
: "border-ink/15 text-ink/70 hover:border-forest hover:text-forest"
? "bg-forest text-cream border-heading"
: "border-ink/15 text-ink/70 hover:border-heading hover:text-heading"
}`}
>
{isActive ? <HexMark className="h-2.5 w-2.5 text-honey-light" /> : null}
@@ -151,7 +151,7 @@ export default async function ShopPage(props: PageProps<"/shop">) {
name="q"
defaultValue={query ?? ""}
placeholder={t.nav.searchPlaceholder}
className="h-11 w-60 border border-ink/15 bg-background pl-10 pr-3 transition-colors placeholder:text-ink/45 hover:border-ink/35 focus:border-forest focus:outline-none"
className="h-11 w-60 border border-ink/15 bg-background pl-10 pr-3 transition-colors placeholder:text-ink/45 hover:border-ink/35 focus:border-heading focus:outline-none"
/>
</span>
<label htmlFor="sortierung" className="ml-2 text-xs tracking-wide-caps text-ink/60">
@@ -162,7 +162,7 @@ export default async function ShopPage(props: PageProps<"/shop">) {
id="sortierung"
name="sortierung"
defaultValue={sort ?? ""}
className="h-11 appearance-none border border-ink/15 bg-background pl-3 pr-9 transition-colors hover:border-ink/35 focus:border-forest focus:outline-none"
className="h-11 appearance-none border border-ink/15 bg-background pl-3 pr-9 transition-colors hover:border-ink/35 focus:border-heading focus:outline-none"
>
{sortOptions.map((option) => (
<option key={option.value} value={option.value}>
@@ -183,7 +183,7 @@ export default async function ShopPage(props: PageProps<"/shop">) {
</span>
<button
type="submit"
className="h-11 bg-forest px-5 text-xs tracking-wide-caps text-cream transition-colors hover:bg-honey-dark"
className="h-11 bg-forest px-5 text-xs tracking-wide-caps text-cream transition-colors hover:bg-honey-deep"
>
{t.shop.apply}
</button>
@@ -198,8 +198,8 @@ export default async function ShopPage(props: PageProps<"/shop">) {
aria-pressed={onlyAvailable}
className={`ml-auto inline-flex h-11 items-center gap-2.5 px-4 text-xs tracking-wide-caps border transition-colors ${
onlyAvailable
? "bg-forest text-cream border-forest"
: "border-ink/15 text-ink/70 hover:border-forest hover:text-forest"
? "bg-forest text-cream border-heading"
: "border-ink/15 text-ink/70 hover:border-heading hover:text-heading"
}`}
>
{/* Kleiner Schalter statt reinem Textknopf — der Zustand ist auf einen Blick lesbar. */}
@@ -208,7 +208,7 @@ export default async function ShopPage(props: PageProps<"/shop">) {
className={`relative h-4 w-7 rounded-full transition-colors ${onlyAvailable ? "bg-honey" : "bg-ink/15"}`}
>
<span
className={`absolute top-0.5 h-3 w-3 rounded-full bg-cream shadow transition-[left] duration-300 ${
className={`absolute top-0.5 h-3 w-3 rounded-full bg-surface shadow transition-[left] duration-300 ${
onlyAvailable ? "left-3.5" : "left-0.5"
}`}
/>
@@ -222,7 +222,7 @@ export default async function ShopPage(props: PageProps<"/shop">) {
{t.shop.searchResults(query, products.length)}{" "}
<Link
href={buildHref({ kategorie: category, sortierung: sort, verfuegbar: onlyAvailable ? "1" : undefined })}
className="underline hover:text-forest"
className="underline hover:text-heading"
>
{t.shop.clearSearch}
</Link>