Iteration Teil A: Dark-Theme (zentrale Token-Umstellung)

Referenz: docs/ISMS-Abhaengigkeitskarte-GEFIM.html

- Zentrale Theme-Definition in globals.css auf das dunkle GEFIM-Muster:
  Flächen (--bg-0/1, --panel, --elevated), Text (--txt/--muted),
  Marke (aufgehelltes Violett #7d6fd6), Status (--ok/warn/risk/info),
  Verläufe (--grad-primary/--grad-critical), Glow-Ambiente auf dem Body,
  Fokus-Ring, Punktraster-Utility
- Alle shadcn-Tokens (background/card/popover/primary/muted/border/
  sidebar …) auf Dunkelwerte gemappt; neue semantische Flächen-Tokens
  (panel/elevated/surface-soft/band/band-text/band-brd) ergänzt
- Hardcodierte Hex projektweit auf Tokens umgestellt: Panel-/Band-
  Flächen, Status-Pillen (Alpha-Tints), Tags, Trend-/Prioritätsfarben,
  Kanban, Login-Fehler, Topbar, Filter-Tabs
- GEFIM-Logo als weißes Negativ auf Dunkel (Sidebar + Login)
- Ampel-Segmentbalken/Score-Skala bleiben als semantische Statusfarben

Dark ist Default; Light-Mode später über dieselben Token-Namen möglich.
Verifiziert: Dashboard, Risiko-Detail, Asset-Bearbeiten dunkel & lesbar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Martin
2026-07-03 08:55:35 +02:00
co-authored by Claude Opus 4.8
parent 1b13af46db
commit 7afd7f6acf
14 changed files with 379 additions and 107 deletions
+3 -1
View File
@@ -70,6 +70,8 @@ export default async function AppLayout({
width={150}
height={53}
priority
// Negativ-/Weiß-Darstellung auf dunklem Grund
className="brightness-0 invert"
/>
</Link>
</div>
@@ -95,7 +97,7 @@ export default async function AppLayout({
</aside>
<div className="flex min-w-0 flex-1 flex-col">
<header className="sticky top-0 z-10 flex items-center gap-4 border-b bg-white/90 px-6 py-2.5 backdrop-blur-md">
<header className="sticky top-0 z-10 flex items-center gap-4 border-b bg-[var(--panel)] px-6 py-2.5 backdrop-blur-md">
<form
method="GET"
action="/assets"
+3 -3
View File
@@ -23,9 +23,9 @@ const MEASURE_INCLUDE = {
} as const;
const PRIORITY_CLASS = {
LOW: "bg-[#eef0f4] text-[#7a7b86]",
MEDIUM: "bg-[#e7f1fa] text-[#3a86c8]",
HIGH: "bg-[#fbe7e7] text-[#d64c4c]",
LOW: "bg-[rgba(139,147,173,0.16)] text-muted-foreground",
MEDIUM: "bg-[rgba(90,169,230,0.16)] text-[var(--info)]",
HIGH: "bg-[rgba(255,107,107,0.16)] text-[var(--risk)]",
} as const;
export default async function MeasuresPage({
+1 -1
View File
@@ -178,7 +178,7 @@ export default async function RisksPage({
key={r.id}
href={`/risks?detail=${r.id}`}
title={r.title}
className="rounded-md bg-white/85 px-1.5 py-0.5 font-heading text-[10.5px] font-bold text-foreground shadow-sm hover:bg-white"
className="rounded-md bg-[var(--elevated)] px-1.5 py-0.5 font-heading text-[10.5px] font-bold text-foreground shadow-sm hover:bg-[var(--elevated)]"
>
{riskRef(r.refNo)}
</Link>
+96 -72
View File
@@ -39,6 +39,18 @@
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
/* Semantische Zusatzflächen (dunkles GEFIM-Theme) */
--color-panel: var(--panel);
--color-panel-brd: var(--panel-brd);
--color-elevated: var(--elevated);
--color-surface-soft: var(--surface-soft);
--color-band: var(--band);
--color-band-text: var(--band-text);
--color-band-brd: var(--band-brd);
--color-ok: var(--ok);
--color-warn: var(--warn);
--color-risk: var(--risk);
--color-info: var(--info);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
@@ -48,85 +60,91 @@
--radius-4xl: calc(var(--radius) * 2.6);
}
/* GEFIM-Farbwelt aus dem Mockup (docs/ISMS-Prototyp-GEFIM.html) */
/*
* Dunkles GEFIM-Theme (Default) — zentrale Token-Definition.
* Referenz: docs/ISMS-Abhaengigkeitskarte-GEFIM.html.
* Ein späterer Light-Mode kann über dieselben Token-Namen unter .light ergänzt werden.
*/
:root {
--gefim-violet: #5d52a3;
--gefim-magenta: #812d80;
/* Marke / Verläufe */
--gefim-violet: #7d6fd6;
--gefim-violet-deep: #5d52a3;
--gefim-blue: #8dc4e0;
--gefim-violet-050: #f2f0f9;
--gefim-violet-100: #e6e2f3;
--gefim-blue-050: #eef7fb;
--grad: linear-gradient(135deg, #5d52a3 0%, #812d80 45%, #8dc4e0 100%);
--grad-soft: linear-gradient(135deg, #5d52a3 0%, #8dc4e0 100%);
--gefim-magenta: #b45bb0;
--grad: var(--grad-primary);
--grad-primary: linear-gradient(135deg, #5d52a3 0%, #7d6fd6 100%);
--grad-soft: linear-gradient(135deg, #5d52a3 0%, #7d6fd6 100%);
--grad-critical: linear-gradient(90deg, #ff6b6b 0%, #b45bb0 100%);
--background: #f5f6fa;
--foreground: #3b3b3a; /* ink */
--card: #ffffff;
--card-foreground: #3b3b3a;
--popover: #ffffff;
--popover-foreground: #3b3b3a;
--primary: #5d52a3;
/* Status */
--ok: #39c07f;
--warn: #f0ad4e;
--risk: #ff6b6b;
--info: #5aa9e6;
/* Flächen */
--background: #0e1220;
--foreground: #e8ecf7;
--card: #141a2e;
--card-foreground: #e8ecf7;
--popover: #1a2138;
--popover-foreground: #e8ecf7;
--primary: #7d6fd6;
--primary-foreground: #ffffff;
--secondary: #f2f0f9; /* violet-050 */
--secondary-foreground: #5d52a3;
--muted: #f1f2f7;
--muted-foreground: #7a7b86;
--accent: #f2f0f9;
--accent-foreground: #5d52a3;
--destructive: #d64c4c;
--border: #e7e8ef;
--input: #e7e8ef;
--ring: #5d52a3;
--chart-1: #5d52a3;
--secondary: #1a2138;
--secondary-foreground: #cfc9ee;
--muted: #1a2138;
--muted-foreground: #8b93ad;
--accent: #1e2640;
--accent-foreground: #cfc9ee;
--destructive: #ff6b6b;
--border: rgba(120, 135, 180, 0.18);
--input: rgba(120, 135, 180, 0.22);
--ring: #7d6fd6;
--chart-1: #7d6fd6;
--chart-2: #8dc4e0;
--chart-3: #812d80;
--chart-4: #2e9e6b;
--chart-5: #e0982e;
--radius: 0.7rem; /* Buttons ~10px, Karten (xl) ~16px wie im Mockup */
--sidebar: #ffffff;
--sidebar-foreground: #4b4c57;
--sidebar-primary: #5d52a3;
--sidebar-primary-foreground: #ffffff;
--sidebar-accent: #e6e2f3; /* violet-100 für aktive Einträge */
--sidebar-accent-foreground: #5d52a3;
--sidebar-border: #e7e8ef;
--sidebar-ring: #5d52a3;
--shadow-card: 0 1px 3px rgba(30, 25, 60, 0.06), 0 6px 20px rgba(30, 25, 60, 0.06);
}
--chart-3: #b45bb0;
--chart-4: #39c07f;
--chart-5: #f0ad4e;
--radius: 0.7rem;
/* Dark-Mode-Werte folgen kontrolliert später (kein prefers-color-scheme-Automatismus) */
.dark {
--background: #1c1b22;
--foreground: #ececf1;
--card: #26242e;
--card-foreground: #ececf1;
--popover: #26242e;
--popover-foreground: #ececf1;
--primary: #8b7fd4;
--primary-foreground: #1c1b22;
--secondary: #2e2b3a;
--secondary-foreground: #c9c3ec;
--muted: #2e2b3a;
--muted-foreground: #9a99a6;
--accent: #2e2b3a;
--accent-foreground: #c9c3ec;
--destructive: #e06c6c;
--border: rgba(255, 255, 255, 0.1);
--input: rgba(255, 255, 255, 0.15);
--ring: #8b7fd4;
--sidebar: #26242e;
--sidebar-foreground: #ececf1;
--sidebar-primary: #8b7fd4;
--sidebar-primary-foreground: #1c1b22;
--sidebar-accent: #2e2b3a;
--sidebar-accent-foreground: #c9c3ec;
--sidebar-border: rgba(255, 255, 255, 0.1);
--sidebar-ring: #8b7fd4;
/* Panels / Glas / erhöhte Flächen */
--panel: rgba(30, 38, 64, 0.72);
--panel-brd: rgba(120, 135, 180, 0.18);
--elevated: #1a2138;
/* weiche Akzentkarte (früher #faf9fd) */
--surface-soft: rgba(125, 111, 214, 0.08);
/* Info-Band (früher #f2f0f9 / #e6e2f3 / #5a4e86) */
--band: rgba(125, 111, 214, 0.1);
--band-text: #c3bdec;
--band-brd: rgba(125, 111, 214, 0.22);
/* Sidebar / Topbar */
--sidebar: #141a2e;
--sidebar-foreground: #b6bdd4;
--sidebar-primary: #7d6fd6;
--sidebar-primary-foreground: #ffffff;
--sidebar-accent: rgba(125, 111, 214, 0.18);
--sidebar-accent-foreground: #cfc9ee;
--sidebar-border: rgba(120, 135, 180, 0.18);
--sidebar-ring: #7d6fd6;
--focus-ring: 0 0 0 3px rgba(125, 111, 214, 0.35);
--radius-card: 16px;
--radius-ctrl: 10px;
--dot-grid: radial-gradient(rgba(140, 155, 200, 0.1) 1px, transparent 1px);
--glow:
radial-gradient(1200px 700px at 20% -10%, #1c2340 0%, transparent 60%),
radial-gradient(900px 600px at 100% 110%, #1a2138 0%, transparent 55%);
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 28px rgba(0, 0, 0, 0.28);
}
body {
font-family: var(--font-sans), "Open Sans", system-ui, sans-serif;
font-size: 14px;
background-color: var(--background);
background-image: var(--glow);
background-attachment: fixed;
}
@layer base {
@@ -134,7 +152,7 @@ body {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
@apply text-foreground;
}
html {
@apply font-sans;
@@ -149,14 +167,20 @@ body {
}
@layer utilities {
/* Verlaufs-Flächen wie im Mockup (.btn.primary, Avatare, Akzente) */
.bg-grad {
background: var(--grad);
background: var(--grad-primary);
}
.bg-grad-soft {
background: var(--grad-soft);
background: var(--grad-primary);
}
.bg-grad-critical {
background: var(--grad-critical);
}
.shadow-card {
box-shadow: var(--shadow-card);
}
.dot-grid {
background-image: var(--dot-grid);
background-size: 26px 26px;
}
}
+2 -1
View File
@@ -46,13 +46,14 @@ export default async function LoginPage({
width={170}
height={60}
priority
className="brightness-0 invert"
/>
<h2 className="mt-4 text-sm font-normal text-muted-foreground">{t("subtitle")}</h2>
{error && (
<p
role="alert"
className="mt-4 rounded-lg bg-[#fbe4e4] px-3 py-2 text-sm text-[#d64c4c]"
className="mt-4 rounded-lg bg-[rgba(255,107,107,0.16)] px-3 py-2 text-sm text-[var(--risk)]"
>
{t("error")}
</p>