From 7afd7f6acf40924bd729ac9f694e28c228b3f968 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 3 Jul 2026 08:55:35 +0200 Subject: [PATCH] Iteration Teil A: Dark-Theme (zentrale Token-Umstellung) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- docs/ISMS-Abhaengigkeitskarte-GEFIM.html | 244 +++++++++++++++++++++++ src/app/(app)/layout.tsx | 4 +- src/app/(app)/measures/page.tsx | 6 +- src/app/(app)/risks/page.tsx | 2 +- src/app/globals.css | 168 +++++++++------- src/app/login/page.tsx | 3 +- src/components/asset-modals.tsx | 4 +- src/components/filter-tabs.tsx | 2 +- src/components/kanban-board.tsx | 8 +- src/components/measure-modals.tsx | 2 +- src/components/mockup-ui.tsx | 27 +-- src/components/process-modals.tsx | 6 +- src/components/risk-modals.tsx | 8 +- src/components/ui/table.tsx | 2 +- 14 files changed, 379 insertions(+), 107 deletions(-) create mode 100644 docs/ISMS-Abhaengigkeitskarte-GEFIM.html diff --git a/docs/ISMS-Abhaengigkeitskarte-GEFIM.html b/docs/ISMS-Abhaengigkeitskarte-GEFIM.html new file mode 100644 index 0000000..49c1b0b --- /dev/null +++ b/docs/ISMS-Abhaengigkeitskarte-GEFIM.html @@ -0,0 +1,244 @@ + + + + + +GEFIM ISMS – Abhängigkeitskarte + + + + + + +
+
+
+ GEFIM +
+
Abhängigkeiten & kritische Pfade
+
Prozess „Auftragsabwicklung" · Asset-Verkettung
+
+
+
+ + + + + +
+
+ +
+
+
+
+ +
+
+
+
↔ horizontal scrollbar · Knoten anklicken hebt den zugehörigen Pfad hervor · Icons zeigen den Asset-Typ
+ +
+
Kritischer Pfad
+
Standard-Abhängigkeit
+
Betriebsbereit
+
PrimärErgebnis-Asset
+
SPOFSingle Point of Failure
+
+ +
+
+
SPOFEngstelle erkannt
+

Active Directory trägt 3 kritische Prozesse. Ein Ausfall unterbricht Auftragsabwicklung, Produktion und Rechnungswesen.

+
+
+
PfadKritischster Pfad
+

Auftragsabwicklung → ERP (SAP) → Active Directory → Cloud-Backup

+
+
+
💡 Empfehlung
+

Redundanz für AD, priorisierte Wiederanlaufplanung, Risiko R-008 hochstufen.

+
+
+
+ + + + diff --git a/src/app/(app)/layout.tsx b/src/app/(app)/layout.tsx index 88bebd3..3bace1a 100644 --- a/src/app/(app)/layout.tsx +++ b/src/app/(app)/layout.tsx @@ -70,6 +70,8 @@ export default async function AppLayout({ width={150} height={53} priority + // Negativ-/Weiß-Darstellung auf dunklem Grund + className="brightness-0 invert" /> @@ -95,7 +97,7 @@ export default async function AppLayout({
-
+
{riskRef(r.refNo)} diff --git a/src/app/globals.css b/src/app/globals.css index 668d63a..7d10648 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -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; + } } diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index d7e06e2..c27fbfd 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -46,13 +46,14 @@ export default async function LoginPage({ width={170} height={60} priority + className="brightness-0 invert" />

{t("subtitle")}

{error && (

{t("error")}

diff --git a/src/components/asset-modals.tsx b/src/components/asset-modals.tsx index 0fbaa65..959d3e6 100644 --- a/src/components/asset-modals.tsx +++ b/src/components/asset-modals.tsx @@ -95,7 +95,7 @@ export async function AssetDetailModal({ {t("masterPill")} {t("masterNote")}
-
+
{asset.name} @@ -193,7 +193,7 @@ export async function AssetDetailModal({
{/* Zugeordnete Risiken — Band wie die BIA-Kennzahlen im Prozess-Popup */} -
+
{t("linkedRisks")} {canWrite && ( diff --git a/src/components/filter-tabs.tsx b/src/components/filter-tabs.tsx index 1661c75..00a9d5e 100644 --- a/src/components/filter-tabs.tsx +++ b/src/components/filter-tabs.tsx @@ -16,7 +16,7 @@ export function FilterTabs({ className={cn( "rounded-full border px-3.5 py-[7px] font-heading text-[12.5px] font-semibold transition-colors", tab.active - ? "border-transparent bg-[#e6e2f3] text-[#5d52a3]" + ? "border-transparent bg-[var(--sidebar-accent)] text-[var(--primary)]" : "border-border bg-card text-muted-foreground hover:text-foreground" )} > diff --git a/src/components/kanban-board.tsx b/src/components/kanban-board.tsx index 6c09066..f11b88e 100644 --- a/src/components/kanban-board.tsx +++ b/src/components/kanban-board.tsx @@ -84,12 +84,12 @@ function Column({ ref={setNodeRef} className={cn( "rounded-xl border bg-muted/60 p-2.5 transition-colors", - isOver && "border-[#5d52a3] bg-[#f2f0f9]" + isOver && "border-[var(--primary)] bg-[var(--band)]" )} >
{column.label} - + {column.cards.length}
@@ -149,13 +149,13 @@ function Card({

{card.title}

- + {card.dueLabel ?? "—"} {card.riskCount > 0 && ⚠ {card.riskCount}} {card.ownerInitials && ( - + {card.ownerInitials} )} diff --git a/src/components/measure-modals.tsx b/src/components/measure-modals.tsx index 291b8d5..eafa242 100644 --- a/src/components/measure-modals.tsx +++ b/src/components/measure-modals.tsx @@ -70,7 +70,7 @@ export async function MeasureDetailModal({ } >
-
+
{measure.title} {measure.description && (

{measure.description}

diff --git a/src/components/mockup-ui.tsx b/src/components/mockup-ui.tsx index 58050e8..1b993e7 100644 --- a/src/components/mockup-ui.tsx +++ b/src/components/mockup-ui.tsx @@ -42,9 +42,9 @@ export function KpiCard({ }) { const trendColors = { muted: "text-muted-foreground", - risk: "text-[#d64c4c]", - warn: "text-[#e0982e]", - ok: "text-[#2e9e6b]", + risk: "text-[var(--risk)]", + warn: "text-[var(--warn)]", + ok: "text-[var(--ok)]", }; return (
@@ -74,7 +74,7 @@ function CiaSegment({ level, label }: { level: number; label?: string }) { key={seg} className={cn( "h-4 w-[7px] rounded-[2px]", - seg <= level ? color : "bg-[#e3e5ee]" + seg <= level ? color : "bg-[rgba(120,135,180,0.2)]" )} /> ))} @@ -128,7 +128,7 @@ export function CiaLegend() { export function Tag({ children }: { children: React.ReactNode }) { return ( - + {children} ); @@ -144,7 +144,7 @@ export function OwnerChip({ name, noOwnerLabel }: { name?: string | null; noOwne .toUpperCase(); return ( - + {initials} {name} @@ -152,14 +152,15 @@ export function OwnerChip({ name, noOwnerLabel }: { name?: string | null; noOwne ); } +// Status-Chips: ~16–20 % Alpha der Statusfarbe auf Dunkel (Referenz-Vorgabe) const PILL_TONES = { - ok: "bg-[#e7f6ee] text-[#2e9e6b]", - warn: "bg-[#fbf1de] text-[#a9760f]", - orange: "bg-[#fbdcc0] text-[#c2681a]", - risk: "bg-[#fbe7e7] text-[#d64c4c]", - info: "bg-[#e7f1fa] text-[#3a86c8]", - mut: "bg-[#eef0f4] text-muted-foreground", - violet: "bg-[#e6e2f3] text-[#5d52a3]", + ok: "bg-[rgba(57,192,127,0.16)] text-[var(--ok)]", + warn: "bg-[rgba(240,173,78,0.16)] text-[var(--warn)]", + orange: "bg-[rgba(240,140,60,0.16)] text-[#f0a35a]", + risk: "bg-[rgba(255,107,107,0.16)] text-[var(--risk)]", + info: "bg-[rgba(90,169,230,0.16)] text-[var(--info)]", + mut: "bg-[rgba(139,147,173,0.16)] text-muted-foreground", + violet: "bg-[rgba(125,111,214,0.2)] text-[#c3bdec]", } as const; export function Pill({ diff --git a/src/components/process-modals.tsx b/src/components/process-modals.tsx index 6404a5b..0dd901c 100644 --- a/src/components/process-modals.tsx +++ b/src/components/process-modals.tsx @@ -132,7 +132,7 @@ export async function ProcessDetailModal({ {primary.map((pa) => (
{pa.asset.name} @@ -188,7 +188,7 @@ export async function ProcessDetailModal({
-
+
RTO @@ -214,7 +214,7 @@ export async function ProcessDetailModal({
{process.bia?.notes && ( -

{process.bia.notes}

+

{process.bia.notes}

)}
diff --git a/src/components/risk-modals.tsx b/src/components/risk-modals.tsx index 95cc7dc..67d012f 100644 --- a/src/components/risk-modals.tsx +++ b/src/components/risk-modals.tsx @@ -89,10 +89,10 @@ async function RatingCard({ const format = await getFormatter(); const num = (v: number) => format.number(v, { maximumFractionDigits: 2 }); - const border = accent === "gross" ? "border-l-[#d64c4c]" : "border-l-[#e0982e]"; + const border = accent === "gross" ? "border-l-[var(--risk)]" : "border-l-[var(--warn)]"; return ( -
+
{title}
@@ -202,7 +202,7 @@ export async function RiskDetailModal({ >
-
+
{risk.title} {risk.description && (

{risk.description}

@@ -276,7 +276,7 @@ export async function RiskDetailModal({
{/* Brutto → Rest-Risiko: Farbskala oben, darunter zwei Karten mit Pfeil */} -
+
) {