Web-Anwendung: Design-Tokens, Texte, Gesundheitsendpunkt
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.
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
@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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user