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

+19 -5
View File
@@ -10,6 +10,8 @@ import { HexMark, HoneycombPattern } from "@/components/honeycomb";
import { AdminCommandPalette, type PaletteEntry } from "@/components/admin-command-palette";
import { useI18n } from "@/i18n/i18n-provider";
import type { Dictionary } from "@/i18n/dictionary-type";
import { ThemeToggle } from "@/components/theme-toggle";
import type { ThemeChoice } from "@/lib/theme";
/** Was an einer Seite gerade auf Bearbeitung wartet (Zaehler in der Seitenleiste). */
export type AdminBadges = Partial<Record<string, number>>;
@@ -118,7 +120,7 @@ function NavLinks({
{active ? (
<motion.span
layoutId="admin-nav-active"
className="absolute inset-y-0.5 left-2 right-2 -z-10 bg-cream/10"
className="absolute inset-y-0.5 left-2 right-2 -z-10 bg-surface/10"
transition={{ type: "spring", stiffness: 420, damping: 36 }}
/>
) : null}
@@ -131,7 +133,7 @@ function NavLinks({
<span className="flex-1">{item.label}</span>
{count > 0 ? (
<span
className="min-w-5 rounded-full bg-honey-dark px-1.5 text-center text-[11px] leading-5 text-cream tabular-nums"
className="min-w-5 rounded-full bg-honey-deep px-1.5 text-center text-[11px] leading-5 text-cream tabular-nums"
title={waitingLabel(count)}
>
<span aria-hidden="true">{count}</span>
@@ -175,6 +177,8 @@ function PaletteTrigger({ onClick, label, shortcut }: { onClick: () => void; lab
interface AdminShellProps {
adminName: string;
/** Gewaehlte Darstellung (Cookie), siehe lib/theme.ts. */
themeChoice?: ThemeChoice;
/** Logo fuer dunkle Flaechen, siehe lib/brand.ts. */
logoSrc?: string | null;
badges?: AdminBadges;
@@ -190,7 +194,14 @@ interface AdminShellProps {
* Shop-Kopf- und -Fusszeile blendet das Root-Layout hier aus
* (components/hide-on-admin.tsx).
*/
export function AdminShell({ adminName, logoSrc = null, badges = {}, logoutAction, children }: AdminShellProps) {
export function AdminShell({
adminName,
logoSrc = null,
themeChoice = "system",
badges = {},
logoutAction,
children,
}: AdminShellProps) {
const { t } = useI18n();
const groups = getAdminNavGroups(t);
const pathname = usePathname();
@@ -209,9 +220,10 @@ export function AdminShell({ adminName, logoSrc = null, badges = {}, logoutActio
<div className="px-5 py-4 border-t border-cream/10">
{/* Der Shop-Kopf ist im Admin ausgeblendet (HideOnAdmin) — ohne diesen
Link fuehrte kein Weg zurueck auf die Website. */}
<div className="mb-4 flex items-center justify-between gap-3">
<Link
href="/"
className="group mb-4 inline-flex items-center gap-2 py-1 text-sm text-cream/90 hover:text-cream"
className="group inline-flex items-center gap-2 py-1 text-sm text-cream/90 hover:text-cream"
>
<svg
width="14"
@@ -227,6 +239,8 @@ export function AdminShell({ adminName, logoSrc = null, badges = {}, logoutActio
</svg>
<span className="link-draw">{t.adminShell.backToWebsite}</span>
</Link>
<ThemeToggle initial={themeChoice} className="-mr-2 text-cream/70 hover:text-cream" />
</div>
<p className="text-xs text-cream/60 truncate">{t.adminShell.loggedInAs(adminName)}</p>
<form action={logoutAction} className="mt-2">
<button type="submit" className="text-xs text-cream/80 underline hover:text-cream">
@@ -237,7 +251,7 @@ export function AdminShell({ adminName, logoSrc = null, badges = {}, logoutActio
);
return (
<div className="min-h-screen bg-cream/40 lg:flex">
<div className="min-h-screen bg-surface/40 lg:flex">
<aside className="relative isolate hidden overflow-hidden lg:flex lg:flex-col lg:w-64 lg:shrink-0 lg:sticky lg:top-0 lg:h-screen bg-forest text-cream">
<HoneycombPattern className="-z-10 text-honey-light/[0.06]" size={18} fade="bottom" />
<div className="flex items-center h-16 px-5 border-b border-cream/10">