Design-Tokens aus docs/10-design-system.md als CSS-Variablen: Grundpalette, die sechs Klassifizierungsfarben, Eckenradius durchgehend 2 px, keine Schatten ausser dem Fokusring. Der Schwärzungsbalken mit dem einmaligen Durchwischen respektiert prefers-reduced-motion. Vier Tests halten die Tokens gegen das Dokument. IBM Plex selbst gehostet als woff2, latin und latin-ext getrennt über unicode-range, font-display: swap, immutable-Cache. Alle Oberflächentexte in lib/i18n/de.ts. Ein Test durchsucht app/ und components/ nach Umlauten im JSX und schlägt fehl, wenn dort deutscher Text steht. Er hat beim ersten Lauf gleich einen Verstoss gefunden. /api/health prüft Datenbank, Discord-Gateway und ClamAV. Die Gesamtbewertung fällt nur auf "down", wenn die Datenbank weg ist: die anderen Dienste haben eigene Healthchecks im Stack, und ein ausgefallener Virenscanner soll nicht den Web-Container als tot markieren. Strikte CSP mit Nonce, kein unsafe-inline für Skripte. Die erste Route liegt bei 100 kB JavaScript nach Kompression, Budget sind 120 kB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
231 lines
6.0 KiB
CSS
231 lines
6.0 KiB
CSS
@import 'tailwindcss';
|
|
@import './fonts.css';
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
Design-Tokens. Fachliche Grundlage: docs/10-design-system.md.
|
|
Bezugspunkt ist eine Registratur im Nachtdienst: lackierter Stahl,
|
|
Aktenpapier, Stempelfarbe. Die einzige Farbe kommt von der Klassifizierung.
|
|
--------------------------------------------------------------------------- */
|
|
|
|
@theme {
|
|
/* Grundpalette, dunkel ist der Auslieferungszustand */
|
|
--color-ink-900: #15181a; /* Grundflaeche, kuehles Anthrazit, leicht gruenstichig */
|
|
--color-ink-800: #1d2124; /* Panel */
|
|
--color-ink-700: #262b2f; /* erhoehte Flaeche, Eingabefelder */
|
|
--color-ink-500: #3a4147; /* Linien und Rahmen */
|
|
--color-paper: #e4e7e4; /* Primaertext, papierfarbenes Off-White */
|
|
--color-paper-dim: #939c99; /* Sekundaertext */
|
|
--color-steel: #7a97a6; /* interaktiv: Links, Fokus, aktive Zustaende */
|
|
|
|
/* Klassifizierungsfarben. Das einzige Buntsystem, nirgends sonst benutzt. */
|
|
--color-stamp-0: #8a918d; /* Offen, Grau */
|
|
--color-stamp-1: #6d8a9c; /* Intern, Stahlblau */
|
|
--color-stamp-2: #4e8f72; /* Vertraulich, Gruenspan */
|
|
--color-stamp-3: #b08637; /* Geheim, Messing */
|
|
--color-stamp-4: #b4543c; /* Streng geheim, Oxidrot */
|
|
--color-stamp-5: #7e5e96; /* Leitungssache, Tiefviolett */
|
|
|
|
/* Semantische Ebene. Das helle Thema tauscht nur diese Werte. */
|
|
--color-surface: var(--color-ink-900);
|
|
--color-panel: var(--color-ink-800);
|
|
--color-raised: var(--color-ink-700);
|
|
--color-line: var(--color-ink-500);
|
|
--color-text: var(--color-paper);
|
|
--color-text-dim: var(--color-paper-dim);
|
|
--color-accent: var(--color-steel);
|
|
|
|
/* Schrift. IBM Plex, selbst gehostet, drei Rollen aus einer Familie. */
|
|
--font-sans: 'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif;
|
|
--font-condensed: 'IBM Plex Sans Condensed', 'IBM Plex Sans', ui-sans-serif, sans-serif;
|
|
--font-mono: 'IBM Plex Mono', ui-monospace, monospace;
|
|
|
|
/* Skala 12 / 13 / 15 / 18 / 24 px */
|
|
--text-2xs: 0.75rem; /* 12 */
|
|
--text-xs: 0.8125rem; /* 13 */
|
|
--text-sm: 0.9375rem; /* 15 */
|
|
--text-base: 0.9375rem; /* 15, Fliesstext */
|
|
--text-lg: 1.125rem; /* 18 */
|
|
--text-xl: 1.5rem; /* 24 */
|
|
|
|
--text-2xs--line-height: 1.4;
|
|
--text-xs--line-height: 1.45;
|
|
--text-sm--line-height: 1.6;
|
|
--text-base--line-height: 1.6;
|
|
--text-lg--line-height: 1.45;
|
|
--text-xl--line-height: 1.3;
|
|
|
|
/* Struktur: Eckenradius 2 px, ueberall gleich. Akten sind nicht abgerundet. */
|
|
--radius-none: 0px;
|
|
--radius-sm: 2px;
|
|
--radius-DEFAULT: 2px;
|
|
--radius-md: 2px;
|
|
--radius-lg: 2px;
|
|
--radius-xl: 2px;
|
|
|
|
/* Keine Schatten ausser dem Fokusring. */
|
|
--shadow-sm: none;
|
|
--shadow-DEFAULT: none;
|
|
--shadow-md: none;
|
|
--shadow-lg: none;
|
|
|
|
/* Zeilenlaenge hoechstens 72 Zeichen */
|
|
--container-measure: 72ch;
|
|
|
|
/* Die 3 px breite Klassifizierungskante links am Objekt */
|
|
--spacing-edge: 3px;
|
|
}
|
|
|
|
/* Helles Thema. Verpflichtend mitzuliefern, ausgebaut in Phase 9. */
|
|
[data-theme='light'] {
|
|
--color-surface: #eceeea;
|
|
--color-panel: #e2e5e1;
|
|
--color-raised: #f4f5f2;
|
|
--color-line: #b9beb8;
|
|
--color-text: #1b1f21;
|
|
--color-text-dim: #545c58;
|
|
--color-accent: #3d5f70;
|
|
}
|
|
|
|
@layer base {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
border-color: var(--color-line);
|
|
}
|
|
|
|
html {
|
|
color-scheme: dark;
|
|
background-color: var(--color-surface);
|
|
}
|
|
|
|
[data-theme='light'] {
|
|
color-scheme: light;
|
|
}
|
|
|
|
body {
|
|
background-color: var(--color-surface);
|
|
color: var(--color-text);
|
|
font-family: var(--font-sans);
|
|
font-size: var(--text-base);
|
|
line-height: 1.6;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
/* Spaltenkoepfe, Stempel und Reiter tragen den schmalen Schnitt.
|
|
Kein Versalsatz: der Stempelcharakter kommt aus Schnitt, Rahmen und Farbe. */
|
|
th,
|
|
legend {
|
|
font-family: var(--font-condensed);
|
|
font-weight: 500;
|
|
text-transform: none;
|
|
}
|
|
|
|
/* Aktenzeichen, Zeitstempel, Hashes und Dienstnummern in Mono. */
|
|
code,
|
|
kbd,
|
|
samp,
|
|
time {
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
h3 {
|
|
font-weight: 500;
|
|
text-wrap: balance;
|
|
}
|
|
|
|
h1 {
|
|
font-size: var(--text-xl);
|
|
}
|
|
h2 {
|
|
font-size: var(--text-lg);
|
|
}
|
|
h3 {
|
|
font-size: var(--text-base);
|
|
}
|
|
|
|
a {
|
|
color: var(--color-accent);
|
|
text-underline-offset: 2px;
|
|
}
|
|
|
|
/* Sichtbarer Fokusring in --steel, der einzige Schatten im System. */
|
|
:focus-visible {
|
|
outline: 2px solid var(--color-accent);
|
|
outline-offset: 2px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
::selection {
|
|
background-color: color-mix(in srgb, var(--color-accent) 40%, transparent);
|
|
}
|
|
}
|
|
|
|
@layer components {
|
|
/* Der eine mutige Moment: die Schwaerzung. Ein massiver Balken mit feiner
|
|
Rahmenlinie, der beim ersten Erscheinen einmal von links nach rechts
|
|
durchwischt. Sonst bewegt sich in der Oberflaeche nichts von selbst. */
|
|
.redaction {
|
|
display: inline-block;
|
|
position: relative;
|
|
overflow: hidden;
|
|
vertical-align: baseline;
|
|
background-color: var(--color-ink-900);
|
|
border: 1px solid var(--color-line);
|
|
border-radius: 2px;
|
|
color: transparent;
|
|
user-select: none;
|
|
}
|
|
|
|
.redaction::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
background-color: var(--color-ink-700);
|
|
transform-origin: left center;
|
|
animation: redaction-wipe 150ms ease-out 1 both;
|
|
}
|
|
|
|
@keyframes redaction-wipe {
|
|
from {
|
|
transform: scaleX(1);
|
|
}
|
|
to {
|
|
transform: scaleX(0);
|
|
}
|
|
}
|
|
|
|
/* Zeitachse des Verlaufs: durchgehende senkrechte Linie. Der Chat hat keine.
|
|
Der Unterschied muss ohne Beschriftung erkennbar sein. */
|
|
.timeline {
|
|
position: relative;
|
|
padding-left: 1.5rem;
|
|
}
|
|
|
|
.timeline::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0.4375rem;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 1px;
|
|
background-color: var(--color-line);
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.redaction::after {
|
|
animation: none;
|
|
transform: scaleX(0);
|
|
}
|
|
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
}
|
|
}
|