style(web): Modul-Kacheln einfarbig, Akzent nur fuer das Modul im Fokus

Statt bunter Modulfarben neutrale graue Kacheln; gelb (persoenliche
Akzentfarbe) nur fuer den gewaehlten Eintrag der Seitenleiste und den
Seitenkopf des Moduls. Schrift auf Akzentflaechen wird jetzt je nach
Akzentfarbe hell oder dunkel gewaehlt (readableOnAccent). Anmelde-
Mosaik und Dashboard-Hintergruende nur noch in Gelb, Grau, Graphit;
Salbei ersetzt durch Kiesel.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-26 13:09:05 +02:00
parent 76520b2010
commit 228112014c
9 changed files with 138 additions and 91 deletions
@@ -20,7 +20,7 @@ export function PageHeader({ title, description, actions, moduleSlug, className
return (
<div className={`flex flex-wrap items-start justify-between gap-x-6 gap-y-3 ${className}`}>
<div className="flex min-w-0 items-center gap-3">
{moduleSlug && <ModuleTile slug={moduleSlug} size={36} />}
{moduleSlug && <ModuleTile slug={moduleSlug} size={36} tone="accent" />}
<div className="min-w-0">
<h1 className="text-2xl font-semibold text-foreground">{title}</h1>
{description && <p className="mt-0.5 text-sm text-muted-foreground">{description}</p>}
+2 -2
View File
@@ -170,7 +170,7 @@ export function Sidebar() {
<li key={mod.id}>
<Link href={href} className={itemClass(active)} aria-label={mod.name} title={mod.name}>
{active && <SelectionPill />}
<ModuleTile slug={mod.slug} size={20} />
<ModuleTile slug={mod.slug} size={20} tone={active ? 'accent' : 'neutral'} />
</Link>
</li>
);
@@ -220,7 +220,7 @@ export function Sidebar() {
<li key={mod.id}>
<Link href={href} className={itemClass(active)}>
{active && <SelectionPill />}
<ModuleTile slug={mod.slug} size={20} />
<ModuleTile slug={mod.slug} size={20} tone={active ? 'accent' : 'neutral'} />
<span className="truncate">{mod.name}</span>
</Link>
</li>
@@ -1,5 +1,5 @@
import type { ReactNode } from 'react';
import { getModuleIdentity, type ModuleIconId } from '@/lib/module-identity';
import { getModuleIcon, type ModuleIconId } from '@/lib/module-identity';
const GLYPHS: Record<ModuleIconId, ReactNode> = {
radar: (
@@ -56,27 +56,36 @@ interface ModuleTileProps {
slug: string;
/** Kantenlaenge in Pixeln (Seitenleiste 20, Karten 40, Seitenkopf 36). */
size?: number;
/**
* `neutral` (Vorgabe): graue Flaeche, dunkelgraues Symbol.
* `accent`: Akzentflaeche mit passender Schrift — fuer das Modul im Fokus.
*/
tone?: 'neutral' | 'accent';
className?: string;
}
/**
* App-Kachel eines Moduls: abgerundetes Quadrat in der Modulfarbe mit weissem
* Symbol (siehe lib/module-identity.ts). Rein dekorativ — der Modulname steht
* immer als Text daneben.
* App-Kachel eines Moduls: abgerundetes Quadrat mit Symbol (siehe
* lib/module-identity.ts). Kontrast Symbol/Flaeche: neutral hell 7,4:1,
* dunkel 8,9:1; Akzent ueber `--primary-foreground`, das fuer jede
* persoenliche Akzentfarbe hell oder dunkel gewaehlt wird (lib/color.ts).
* Rein dekorativ — der Modulname steht immer als Text daneben.
*/
export function ModuleTile({ slug, size = 20, className = '' }: ModuleTileProps) {
const { color, icon } = getModuleIdentity(slug);
export function ModuleTile({ slug, size = 20, tone = 'neutral', className = '' }: ModuleTileProps) {
const icon = getModuleIcon(slug);
const glyph = Math.round(size * 0.6);
return (
<span
aria-hidden="true"
data-module-tile={slug}
className={`inline-flex shrink-0 items-center justify-center text-white ${className}`}
data-tone={tone}
className={`inline-flex shrink-0 items-center justify-center ${
tone === 'accent' ? 'bg-primary text-primary-foreground' : 'bg-tile text-tile-foreground'
} ${className}`}
style={{
width: size,
height: size,
borderRadius: Math.max(4, Math.round(size * 0.24)),
backgroundColor: color,
}}
>
<svg
@@ -86,7 +95,7 @@ export function ModuleTile({ slug, size = 20, className = '' }: ModuleTileProps)
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={size <= 24 ? 2.4 : 2}
strokeWidth={size <= 24 ? 2.2 : 1.9}
strokeLinecap="round"
strokeLinejoin="round"
>