Files
AfiS-Aktensystem/web/app/globals.css
T
Harlekin addef20e16 Phase 9 abgeschlossen: Übersicht, Beobachtungsliste, helles Thema, Messung
Die Übersicht beantwortet vier Fragen, die man morgens hat: woran arbeite ich,
was ist fällig, wo hat jemand geschrieben, was hat sich getan. Jede Liste
läuft durch visibleCaseWhere - das ist der Ort, an dem eine unachtsame
Abfrage am ehesten eine Akte verrät. Ein Test prüft jede einzeln gegen eine zu
hoch eingestufte Akte. Der bisherige Inhalt ("Dein Zugang") steht jetzt unter
/zugang.

Beobachten ist eine Merkliste, kein Recht: nur was sichtbar ist, lässt sich
merken, und steigt eine Akte später über die eigene Stufe, verschwindet sie
aus der Liste, ohne dass jemand aufräumt.

Das helle Thema tauscht nur die semantische Ebene; die Grundpalette bleibt.
Die Klassifizierungsfarben sind für hellen Grund eine Spur dunkler, im Farbton
aber dieselben - eine Stufe muss in beiden Themen wiedererkennbar sein. Drei
Zustände, "wie das System" ist der Auslieferungszustand, und ein Skript im
Kopf setzt die Wahl, bevor das erste Mal gemalt wird.

Die Messung misst nicht den Nutzer. web-vitals schickt Zahlen und das
Routenmuster, keine Kennung und keinen konkreten Pfad: wer wann welche Akte
geöffnet hat, steht im Zugriffsprotokoll, und eine zweite Spur daneben wäre
eine Überwachungsfunktion ohne Auftrag. Ausgewertet wird Median und 75.
Perzentil, nicht der Mittelwert.

Mobil: die breiten Tabellen scrollen in sich, statt die Seite breiter zu
machen. Ein e2e-Test hält fest, dass keine Seite waagerecht überläuft.

scripts/restore-test.sh spielt eine Sicherung in eine Wegwerf-Datenbank und
prüft ausdrücklich, dass Regeln und Trigger mitkommen: eine Wiederherstellung
ohne entry_no_delete wäre eine Datenbank ohne die Zusicherung, um derentwillen
das System gebaut wird. Einmal durchgespielt, alles angekommen.

186 Tests im Web-Paket, 17 in der Datenbank, 7 im Worker, 54 e2e.
Grösste Route 116,2 von 120 kB.
2026-09-03 10:28:50 +02:00

609 lines
14 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.
*
* Es tauscht **nur die semantische Ebene**, nie die Grundpalette und nie die
* Klassifizierungsfarben: eine Stufe muss in beiden Themen dieselbe Farbe
* haben, sonst lernt man sie zweimal. Siehe docs/10-design-system.md.
*
* Kein warmes Cremeweiss, sondern ein leicht kühles Papierweiss — dieselbe
* Anmutung wie das dunkle Thema, nur andersherum. Die Kontraste sind auf
* mindestens 4,5:1 für Fliesstext ausgelegt. */
[data-theme='light'] {
--color-surface: #eceeea;
--color-panel: #e2e5e1;
--color-raised: #f7f8f5;
--color-line: #b0b6ae;
--color-text: #1b1f21;
--color-text-dim: #4d5551;
--color-accent: #35586a;
}
/* Die Klassifizierungsfarben sind für hellen Grund eine Spur dunkler, damit
Stempel und Kante lesbar bleiben. Der Farbton bleibt derselbe. */
[data-theme='light'] {
--color-stamp-0: #6b716d;
--color-stamp-1: #4a6577;
--color-stamp-2: #34694f;
--color-stamp-3: #856024;
--color-stamp-4: #8d3c28;
--color-stamp-5: #5c4270;
}
/* Ohne ausdrückliche Wahl folgt die Anwendung dem System. Der Auslieferungs-
zustand bleibt dunkel: `prefers-color-scheme: light` muss gesetzt sein. */
@media (prefers-color-scheme: light) {
:root:not([data-theme='dark']) {
--color-surface: #eceeea;
--color-panel: #e2e5e1;
--color-raised: #f7f8f5;
--color-line: #b0b6ae;
--color-text: #1b1f21;
--color-text-dim: #4d5551;
--color-accent: #35586a;
--color-stamp-0: #6b716d;
--color-stamp-1: #4a6577;
--color-stamp-2: #34694f;
--color-stamp-3: #856024;
--color-stamp-4: #8d3c28;
--color-stamp-5: #5c4270;
}
}
@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);
}
/* ---------------------------------------------------------------------
Das Aktenblatt.
Bewusst **ausserhalb** des Design-Systems der Oberflaeche: schwarz auf
weiss, harte Rahmen, Massangaben in Millimetern. Es ist kein Bildschirm-
element, sondern ein Formular, das gedruckt und weitergereicht wird und
auf Papier genauso aussehen muss wie hier. Deshalb steht hier kein
einziger Farb-Token der Anwendung.
Nachgebaut nach dem Formular, das die Personalabteilung bislang von Hand
ausgefuellt hat. Siehe docs/13-entscheidungen.md.
--------------------------------------------------------------------- */
.sheet {
width: 210mm;
min-height: 297mm;
box-sizing: border-box;
margin: 0 auto;
padding: 10mm;
background: #ffffff;
color: #000000;
font-family: var(--font-sans, sans-serif);
}
.sheet-frame {
border: 4px solid #000;
min-height: calc(297mm - 20mm);
display: flex;
flex-direction: column;
}
.sheet-head {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
padding: 15px;
border-bottom: 4px solid #000;
}
.sheet-head h1 {
margin: 0 0 5px;
font-size: 28px;
font-weight: 900;
letter-spacing: 1px;
line-height: 1.1;
text-transform: uppercase;
color: #000;
}
.sheet-head h2 {
margin: 0;
font-size: 14px;
font-weight: 900;
letter-spacing: 2px;
text-transform: uppercase;
color: #000;
}
.sheet-mark {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
}
/* Rein dekorativ, deshalb aria-hidden im Markup. */
.sheet-barcode {
width: 140px;
height: 25px;
background: repeating-linear-gradient(
90deg,
#000,
#000 2px,
#fff 2px,
#fff 5px,
#000 5px,
#000 8px,
#fff 8px,
#fff 10px
);
}
.sheet-classbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
padding: 10px 15px;
border-bottom: 4px solid #000;
color: #fff;
font-size: 14px;
font-weight: 900;
text-transform: uppercase;
}
.sheet-grid {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.sheet-grid td {
border: 2px solid #000;
padding: 10px;
vertical-align: top;
word-wrap: break-word;
overflow-wrap: anywhere;
}
.sheet-label {
font-size: 10px;
font-weight: 900;
text-transform: uppercase;
margin-bottom: 4px;
color: #000;
}
.sheet-value {
font-size: 14px;
font-weight: 700;
min-height: 20px;
color: #000;
}
.sheet-value-emphasis {
color: #7f1d1d;
}
.sheet-photo {
width: 120px;
height: 160px;
margin: 0 auto;
border: 2px solid #000;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
font-size: 10px;
font-weight: 700;
color: #555;
position: relative;
overflow: hidden;
}
.sheet-photo img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.sheet-signature {
border-bottom: 2px solid #000;
min-height: 30px;
display: flex;
align-items: flex-end;
font-family: 'Times New Roman', Times, serif;
font-style: italic;
font-size: 18px;
color: #000;
}
.sheet-foot {
margin-top: auto;
border-top: 4px solid #000;
padding: 10px 15px;
display: flex;
justify-content: space-between;
gap: 1rem;
font-family: var(--font-mono, monospace);
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
color: #000;
}
/* Schmale Fenster: das Blatt behaelt seine Proportionen und wird als Ganzes
verkleinert, statt umzubrechen. Ein umgebrochenes Formular waere ein
anderes Formular. */
@media (max-width: 230mm) {
.sheet-scroll {
overflow-x: auto;
}
}
/* ---------------------------------------------------------------------
Die Druckansicht einer Akte.
Schwarz auf weiss wie das Aktenblatt, aber im Aufbau eines Berichts:
Deckblatt, Kopfdaten, Abschnitte, Verlauf, Anhangsverzeichnis. Das
Wasserzeichen setzt der PDF-Dienst in Kopf- und Fusszeile jeder Seite.
Ein Export darf nie sauberer aussehen als die Akte ist: geänderte
Einträge sind gekennzeichnet, zurückgezogene stehen als Grabstein, und
gesperrte Felder erscheinen als Balken mit Vermerk statt zu fehlen.
Siehe docs/08-suche-und-export.md.
--------------------------------------------------------------------- */
.print-sheet {
max-width: 180mm;
margin: 0 auto;
padding: 12mm 0;
background: #fff;
color: #000;
font-size: 10pt;
line-height: 1.45;
/* Am Bildschirm auf schmalen Geraeten: die Tabellen darin scrollen in
sich, statt die Seite breiter zu machen. Auf Papier greift das nicht. */
overflow-x: auto;
}
.print-meta {
min-width: 0;
}
.print-sheet h1 {
font-size: 18pt;
font-weight: 900;
margin: 0 0 2mm;
color: #000;
}
.print-sheet h2 {
font-size: 12pt;
font-weight: 900;
margin: 8mm 0 2mm;
padding-bottom: 1mm;
border-bottom: 1.5pt solid #000;
color: #000;
break-after: avoid;
}
.print-sheet h3 {
font-size: 10pt;
font-weight: 900;
margin: 4mm 0 1mm;
color: #000;
break-after: avoid;
}
.print-cover {
border: 3pt solid #000;
padding: 8mm;
margin-bottom: 8mm;
}
.print-stamp {
display: inline-block;
border: 2pt solid #000;
padding: 1mm 3mm;
font-weight: 900;
letter-spacing: 1pt;
text-transform: uppercase;
}
.print-meta {
width: 100%;
border-collapse: collapse;
margin-top: 3mm;
}
.print-meta th,
.print-meta td {
border: 0.75pt solid #000;
padding: 1.5mm 2mm;
text-align: left;
vertical-align: top;
font-size: 9pt;
}
.print-meta th {
width: 38mm;
font-weight: 900;
}
/* Ein Eintrag soll nicht über den Seitenrand zerrissen werden. */
.print-entry {
break-inside: avoid;
padding: 2mm 0;
border-bottom: 0.5pt dotted #666;
}
.print-label {
font-size: 8pt;
font-weight: 900;
text-transform: uppercase;
color: #000;
}
.print-note {
font-size: 8pt;
color: #444;
}
/* Die Schwärzung im Druck: ein voller Balken, kein Weglassen. */
.print-redacted {
display: inline-block;
min-width: 30mm;
background: #000;
color: #000;
user-select: none;
}
}
/* Drucken: nur das Blatt, ohne Rahmenwerk der Anwendung. Kopf- und Fusszeile
mit dem Wasserzeichen setzt der PDF-Dienst, siehe docs/08. */
@media print {
.no-print {
display: none !important;
}
body {
background: #fff !important;
}
.sheet {
margin: 0;
padding: 0;
width: auto;
}
.sheet-frame {
min-height: auto;
}
.sheet-classbar,
.print-redacted,
.print-stamp {
/* Sonst drucken die Flächen weiss auf weiss. */
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
}
.print-sheet {
max-width: none;
padding: 0;
}
}
@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;
}
}