Design-System¶
Stand: nach T02. Farben, Radien, Grössen und Schriften stehen in den Design-Tokens unter src/theme/.
Die 13 Grundkomponenten aus T01 und die 9 Zustandskomponenten aus T02 sind gebaut und liegen in src/app/ui/.
Regeln für alle Komponenten:
- Farben, Radien und Grössen kommen ausschliesslich aus
var(--og-…). In Komponenten stehen keine Hex-Werte. Einzige Ausnahme sind die beiden Farbwerte insrc/app/ui/qr/qr-colors.ts, weil das PaketqrcodeFarben nur als Strings entgegennimmt. - Die Typografie kommt aus den Mixins in
src/theme/typography.scss(og-type-…), nicht aus eigenen Angaben. - Alle Komponenten sind Standalone, nutzen
ChangeDetectionStrategy.OnPushund haben nur Inputs und Outputs. Sie injizieren keine Services. - Sichtbare Texte sind Deutsch (Schweiz).
Farben¶
Quelle: src/theme/tokens.scss. In Komponenten wird der Token benutzt, nie der Hex-Wert.
Flächen¶
| Token | Wert | Verwendung |
|---|---|---|
--og-bg |
#0F1115 |
Seitenhintergrund |
--og-surface |
#1A1D24 |
Karten, eingehende Bubbles, Icon-Buttons |
--og-surface-2 |
#15181E |
Schrittlisten, Avatar-Grund „ausser Reichweite“ |
--og-bar |
#13161B |
Composer-Leiste unten im Chat |
--og-input |
#0F1115 |
Eingabefeld innerhalb einer Karte |
--og-scrim |
rgba(10, 12, 15, 0.72) |
Abdunklung hinter einem Sheet |
Linien¶
| Token | Wert | Verwendung |
|---|---|---|
--og-line |
#262A33 |
Trennlinien in Listen und Karten, Header-Unterkante |
--og-border |
#2C313B |
Rahmen von Karten, Eingabefeldern, Icon-Buttons |
--og-border-muted |
#3D4350 |
leeres Schritt-Kästchen, Punkte im Leerzustand |
--og-dashed |
#4A5060 |
gestrichelter Avatar (Kontakt ausstehend) |
Text¶
| Token | Wert | Verwendung |
|---|---|---|
--og-text |
#EEF0F5 |
Haupttext |
--og-text-2 |
#C5CAD6 |
Fliesstext, Labels, Vorschau ungelesen |
--og-text-3 |
#959CAB |
Meta, Zeiten, Hilfstexte, Abschnittstitel |
--og-placeholder |
#8A91A0 |
Platzhalter im Eingabefeld |
Akzent (Eisblau)¶
| Token | Wert | Verwendung |
|---|---|---|
--og-accent |
#9DB4FF |
Primärbutton, eigene Bubble, Signalbalken an, Ungelesen-Badge |
--og-accent-hover |
#C2D0FF |
Hover-Zustand des Akzents |
--og-on-accent |
#0B1020 |
Text und Icon auf Akzentfläche |
--og-accent-soft |
#232B45 |
Icon-Kachel hinter einem Akzent-Icon |
--og-accent-pulse |
rgba(157, 180, 255, 0.55) |
Puls-Animation der aktiven Schrittzeile |
Signal und Gefahr¶
| Token | Wert | Verwendung |
|---|---|---|
--og-signal-off |
#2E3440 |
Signalbalken aus, Schalter aus |
--og-ok |
#7FD6A4 |
Statuspunkt „verbunden“, Erfolgs-Badge |
--og-danger |
#FF8F87 |
„Nicht gesendet“, Rahmen einer fehlgeschlagenen Bubble |
--og-danger-title |
#FFB4AD |
Titel im Fehler-Banner |
--og-danger-text |
#E8D6D3 |
Fliesstext im Fehler-Banner |
--og-danger-bg |
#2A2220 |
Banner-Hintergrund |
--og-danger-border |
#4A3330 |
Banner-Rahmen |
Radien, Grössen und Abstände¶
Quelle: src/theme/tokens.scss.
| Token | Wert | Verwendung |
|---|---|---|
--og-r-xs |
9px |
Schritt-Kästchen |
--og-r-sm |
10px |
kleiner Inline-Button („Erneut senden“) |
--og-r-md |
12px |
Segment im Umschalter, Banner-Button |
--og-r-input |
14px |
Eingabefelder, quadratische Icon-Kachel |
--og-r-btn2 |
16px |
Sekundärbutton, Tab-Umschalter |
--og-r-btn |
18px |
Primärbutton, Listenzeile, Banner |
--og-r-card |
20px |
Karten |
--og-r-qr |
28px |
QR-Karte, Kamera-Rahmen, Hero-Icon |
--og-r-pill |
999px |
Badge, Schalter |
--og-gutter |
16px |
Seitenrand für Karten und Umschalter |
--og-gutter-text |
24px |
Seitenrand für Fliesstext |
--og-gutter-title |
20px |
Seitenrand für Seitentitel und Abschnitte |
--og-section-gap |
26px |
Abstand vor einem Abschnittstitel |
--og-touch |
44px |
Mindestgrösse eines Touch-Ziels |
--og-btn-h |
54px |
Höhe des Primärbuttons |
--og-btn2-h |
48px |
Höhe des Sekundärbuttons |
--og-input-h |
46px |
Höhe des Eingabefeldes in der Formularkarte |
--og-composer-h |
48px |
Eingabefeld und Senden-Button im Chat |
--og-row-min |
52px |
Höhe einer Zeile in der Info-Karte |
Schriften¶
Zwei Familien: Schibsted Grotesk für die Oberfläche, Geist Mono für technische Meta-Angaben.
Beide sind lokal gebündelt, die App läuft offline. Quelle der Stile: src/theme/typography.scss.
| Stil | Mixin | Font | Grösse | Gewicht | Verwendung |
|---|---|---|---|---|---|
display |
og-type-display |
--og-font |
32px / 1.1 | 800 | Onboarding-Titel |
app-title |
og-type-app-title |
--og-font |
30px | 800 | „OffGrid“ in den Kontakten |
hero |
og-type-hero |
--og-font |
24px | 750 | „Schlüssel ausgetauscht“ |
empty-title |
og-type-empty-title |
--og-font |
22px | 700 | Leerzustand |
page-title |
og-type-page-title |
--og-font |
20px | 700 | Titel einer Unterseite |
chat-name |
og-type-chat-name |
--og-font |
17px / 1.2 | 650 | Name im Chat-Header |
body-strong |
og-type-body-strong |
--og-font |
16px | 650 | Kontaktname, Zeilentitel |
body |
og-type-body |
--og-font |
16px / 1.4 | 400 | Bubble-Text, Eingabefelder |
body-2 |
og-type-body-2 |
--og-font |
15px / 1.5 | 400 | Erklärtexte |
row |
og-type-row |
--og-font |
15px | 400 | Zeilen in Info-Karten |
preview |
og-type-preview |
--og-font |
14px | 400 | Nachrichtenvorschau (eine Zeile, Ellipsis) |
help |
og-type-help |
--og-font |
13px / 1.4 | 400 | Hilfstexte und Labels |
meta |
og-type-meta |
--og-font-mono |
11px | 400 | Zeiten, Status, technische Werte |
section |
og-type-section |
--og-font-mono |
11px | 500 | Abschnittstitel (Grossbuchstaben, letter-spacing: 0.12em) |
value |
og-type-value |
--og-font-mono |
13px | 400 | Werte rechts in Info-Zeilen |
Icons¶
og-icon rendert ein Inline-SVG mit viewBox="0 0 24 24", fill="none", stroke="currentColor",
runden Enden und aria-hidden="true". Es gibt keine Icon-Fonts und keine Emojis.
Die Pfade sind fest in der Komponente hinterlegt (src/app/ui/icon/icon.component.ts).
name |
Grösse | Strichstärke |
|---|---|---|
back |
22 | 2 |
plus |
20 | 2 |
settings |
20 | 2 |
send |
22 | 2.2 |
check |
14 | 3 |
qr |
20 | 2.2 |
antenna |
22 | 2 |
upload |
22 | 2 |
download |
22 | 2 |
file |
20 | 2 |
info |
18 | 2 |
warning |
18 | 2 |
pin |
22 | 2 |
map |
20 | 2.2 |
copy |
20 | 2 |
refresh |
18 | 2 |
users |
20 | 2 |
Das Icon users wird erst mit dem Gruppenchat (T20) gebraucht.
Grundkomponenten (T01)¶
Jede Komponente liegt in einem eigenen Ordner unter src/app/ui/, die Klasse heisst Og<Name>Component,
der Selektor og-<name>. src/app/ui/index.ts exportiert alle Komponenten und ihre Typen.
| Selektor | Datei | Inputs | Outputs |
|---|---|---|---|
og-icon |
ui/icon/icon.component.ts |
name (Pflicht), size, strokeWidth |
– |
og-button |
ui/button/button.component.ts |
variant, type, disabled, icon, fullWidth |
pressed |
og-link-button |
ui/button/link-button.component.ts |
variant, routerLink, disabled, icon, fullWidth |
pressed |
og-icon-button |
ui/icon-button/icon-button.component.ts |
icon (Pflicht), label (Pflicht), shape, routerLink |
– |
og-topbar |
ui/topbar/topbar.component.ts |
title (Pflicht), backLink, backLabel (Pflicht) |
– |
og-section-label |
ui/section-label/section-label.component.ts |
inset |
– |
og-card |
ui/card/card.component.ts |
padded |
– |
og-info-row |
ui/info-row/info-row.component.ts |
label (Pflicht), value, last |
– |
og-status-dot |
ui/status-dot/status-dot.component.ts |
state |
– |
og-badge |
ui/badge/badge.component.ts |
count |
– |
og-text-field |
ui/text-field/text-field.component.ts |
id (Pflicht), label (Pflicht), type, placeholder, hint, optional, maxlength, error, value (Model) |
blurred |
og-switch |
ui/switch/switch.component.ts |
checked (Model), labelledBy (Pflicht) |
– |
og-segmented |
ui/segmented/segmented.component.ts |
options (Pflicht), value (Model), ariaLabel (Pflicht) |
– |
og-icon¶
Ein Icon in der Standardgrösse des Namens. size (Kantenlänge in px) und strokeWidth überschreiben die
Standardwerte aus der Tabelle oben.
og-button¶
Ein <button> für Aktionen. variant wählt das Aussehen, type (button oder submit, Default button)
legt den Typ fest, icon setzt ein Icon vor den Text,
fullWidth (Default true) spannt den Button über die volle Breite. Bei disabled ist die Opazität 0.4 und
der Button löst keinen Klick aus.
variant |
Höhe | Radius | Grund | Text | Typografie |
|---|---|---|---|---|---|
primary |
--og-btn-h (54px) |
--og-r-btn |
--og-accent |
--og-on-accent |
16px / 700 |
secondary |
--og-btn2-h (48px) |
--og-r-btn2 |
transparent, Rahmen 1px --og-border |
--og-text |
15px / 600 |
danger-text |
56px | – | transparent, kein Rahmen | --og-danger |
16px / 600 |
Weitere Masse: Abstand Icon zu Text 10px, Innenabstand links und rechts 16px. Der Primärbutton zeigt sein Icon
mit 20px. Die Variante danger-text ist linksbündig und für eine Zeile in einer Karte gedacht.
og-link-button¶
Gleiches Aussehen wie og-button, rendert aber ein <a> mit routerLink – für die Navigation.
Bei disabled ist die Route null, das Element trägt aria-disabled="true" und reagiert nicht auf Zeiger.
og-icon-button¶
Ein Icon-Button mit 44 × 44 px. label ist Pflicht und wird als aria-label gesetzt. Mit routerLink rendert
die Komponente ein <a>, ohne Route ein <button>.
shape |
Form | Grund | Rahmen |
|---|---|---|---|
round |
Kreis | --og-surface |
1px --og-border |
plain |
Radius --og-r-input (14px) |
transparent | – |
og-topbar¶
Der Kopf einer Unterseite: ein og-icon-button mit dem Icon back in der Form plain, der Titel als <h1>
im Stil page-title (20px / 700) und rechts ein Bereich für projizierten Inhalt.
Masse: Zeile mit Abstand 6px, Innenabstand 14px 12px 8px 6px.
backLabel ist die Beschriftung des Zurück-Buttons für Screenreader, zum Beispiel „Zurück zu den Kontakten“.
og-section-label¶
Ein Abschnittstitel im Stil section (Mono 11px / 500, Grossbuchstaben, --og-text-3).
Der Einzug inset steuert den Innenabstand: card = 26px 28px 8px, list = 26px 20px 8px.
og-card¶
Ein Container für Formulare und Listen. Seitenrand --og-gutter (16px), Radius --og-r-card (20px),
Grund --og-surface, Rahmen 1px --og-border. Mit padded (true) bekommt die Karte 16px Innenabstand und
14px Abstand zwischen den Kindern. Ohne padded sind die Kinder Zeilen.
og-info-row¶
Eine Zeile in der Karte: Beschriftung links, Wert rechts. Mindesthöhe --og-row-min (52px), Innenabstand 0 16px,
Abstand 12px zwischen Label und Wert, Trennlinie 1px --og-line. Mit last entfällt die Trennlinie.
Das Label steht im Stil row (15px) in --og-text-2, der Wert im Stil value (Mono 13px).
Projizierter Inhalt ersetzt den Wert, zum Beispiel durch Signalbalken.
og-status-dot¶
Ein Punkt mit 8 × 8 px: ok in --og-ok, danger in --og-danger.
og-badge¶
Die Anzahl der ungelesenen Nachrichten. Mindestbreite 18px, Höhe 18px, Innenabstand 0 5px, Pill-Form,
Grund --og-accent, Text --og-on-accent im Meta-Stil (11px / 700). Ab 100 steht dort „99+“.
Bei count 0 wird nichts gerendert.
og-text-field¶
Ein Eingabefeld mit Label, optionalem Hinweis und optionaler Fehlermeldung.
Das value ist ein Model (Zwei-Wege-Bindung [(value)]), blurred wird beim Verlassen des Feldes ausgelöst.
| Teil | Grösse und Farbe |
|---|---|
| Label | Stil help (13px) in --og-text-2, Abstand 6px zum Feld |
| Eingabefeld | Höhe --og-input-h (46px), Radius --og-r-input (14px), Grund --og-input, Rahmen 1px --og-border, Text 16px, Innenabstand 0 14px |
| Fokus | Rahmen --og-accent |
| Hinweis | Stil help unter dem Feld |
| Fehler | Stil help in --og-danger unter dem Feld |
Mit optional hängt die Komponente „ (optional)“ in --og-text-3 an das Label.
og-switch¶
Ein Schalter als <button type="button"> mit role="switch" und aria-checked.
labelledBy verweist auf die Id des Textes, der den Schalter beschriftet.
Die Spur ist 52 × 32 px mit 3px Innenabstand und Pill-Form: an --og-accent mit dem Knopf rechts,
aus --og-signal-off mit dem Knopf links. Der Knopf ist ein Kreis mit 26 × 26 px, an --og-on-accent,
aus --og-text-3. Der Übergang dauert 150ms.
og-segmented¶
Ein Umschalter mit Segmenten als role="tablist" mit aria-label.
options liefert die Segmente als { value, label }, value ist das gewählte Segment (Model).
| Teil | Grösse und Farbe |
|---|---|
| Leiste | Aussenabstand 8px 16px 0, Innenabstand 4px, Radius --og-r-btn2 (16px), Grund --og-surface, Rahmen 1px --og-border, Raster mit 2 Spalten und 4px Abstand |
| Segment | <button role="tab" aria-selected>, Höhe 44px, Radius --og-r-md (12px), 15px |
| Segment aktiv | Grund --og-text, Text --og-bg, 700 |
| Segment inaktiv | transparent, Text --og-text-2, 500 |
Zustandskomponenten (T02)¶
Neun Komponenten bilden die Zustände von Profil, Signal, Hinweisen, Schritten, Nachrichten, Eingabe und
QR-Code ab. Jede Komponente liegt in einem eigenen Ordner unter src/app/ui/. Der Barrel
src/app/ui/index.ts exportiert die Komponenten und ihre Typen.
| Selektor | Datei | Inputs | Outputs |
|---|---|---|---|
og-avatar |
ui/avatar/avatar.component.ts |
pattern, size (Pflicht), variant |
– |
og-signal-bars |
ui/signal-bars/signal-bars.component.ts |
level (Pflicht), size, label (Pflicht) |
– |
og-banner |
ui/banner/banner.component.ts |
tone, title, text |
– |
og-banner-action |
ui/banner/banner-action.component.ts |
routerLink |
– |
og-note |
ui/banner/note.component.ts |
text |
– |
og-step-list |
ui/step-list/step-list.component.ts |
steps (Pflicht), compact, activeStyle |
– |
og-message-bubble |
ui/message-bubble/message-bubble.component.ts |
direction (Pflicht), status, text, meta, groupStart |
retry |
og-composer |
ui/composer/composer.component.ts |
placeholder, label (Pflicht), sendLabel (Pflicht), maxBytes (Pflicht), countBytes (Pflicht), showLocation, blockOverLimit, text (Model) |
send, location |
og-qr |
ui/qr/qr.component.ts |
data (Pflicht) |
– |
og-avatar¶
Ein rundes Profilbild mit einem zentrierten 5 × 5-Raster. pattern enthält 25 booleansche Zellwerte in der
Reihenfolge von oben nach unten und links nach rechts. Aktive Zellen nutzen --og-text-2; inaktive Zellen bleiben
transparent. Die Komponente ist rein dekorativ und trägt aria-hidden="true".
size |
Zelle | Abstand | Zellradius |
|---|---|---|---|
| 40 | 4px | 1px | 1px |
| 48 | 5px | 1px | 1px |
| 56 | 5px | 1px | 1px |
| 72 | 7px | 1.5px | 1px |
| 96 | 9px | 2px | 2px |
variant="pending" zeigt statt des Rasters einen transparenten Kreis mit 1.5px gestricheltem Rahmen in
--og-dashed. Die eckige Variante square ist für den späteren Gruppenchat (T20) vorbereitet.
Die Zellwerte erzeugt avatarPattern(seed) aus ui/avatar/avatar-pattern.ts. Die Funktion liefert immer 25 Werte,
hält die Zeilen spiegelsymmetrisch, schaltet mindestens fünf Zellen an und erzeugt für denselben Seed dasselbe
Raster. Die Konstante AVATAR_PATTERN_SIZE ist ebenfalls im Barrel exportiert.
og-signal-bars¶
Vier SVG-Balken zeigen die Signalstufe level von 0 bis 4. Die ersten level Balken sind aktiv und nutzen
--og-accent; die übrigen Balken nutzen --og-signal-off. Das SVG hat die ViewBox 0 0 20 14.
size |
Breite | Höhe |
|---|---|---|
md |
20px | 14px |
sm |
15px | 11px |
label ist als Screenreader-Beschriftung vorgesehen. Ist der Wert leer, rendert das SVG dekorativ mit
aria-hidden="true".
og-banner, og-banner-action und og-note¶
og-banner zeigt Titel und Text in einem Gefahrenhinweis. Der einzige unterstützte Ton ist danger; Grund,
Rahmen, Titel- und Textfarbe kommen aus --og-danger-bg, --og-danger-border, --og-danger-title und
--og-danger-text. Der Container trägt role="status". Rechts kann eine Aktion projiziert werden.
og-banner-action rendert mit routerLink einen Link <a> mit 40px Höhe. Der Linktext wird projiziert.
og-note ist ein kompakter Hinweis ohne Titel. Er zeigt das Icon warning mit 18px, den Text und trägt
role="note".
og-step-list¶
Eine geordnete Liste <ol> mit den Zuständen done, active und todo. Jeder Schritt kann einen optionalen
Meta-Text rechts enthalten.
done: gefülltes Kästchen in--og-accentmit dem Iconcheck.active: hervorgehobener Text und Meta-Text.activeStyle="pulse"zeigt einen pulsierenden Punkt,activeStyle="spinner"einen rotierenden Kreis. Standard istpulse.todo: offenes Kästchen mit Rahmen in--og-border-mutedund Text in--og-text-3.
compact verkürzt den oberen Abstand der Liste. Bei prefers-reduced-motion: reduce sind Puls und Spinner
ausgeschaltet.
og-message-bubble¶
Eine eingehende (direction="in") oder ausgehende (direction="out") Nachricht. Die Blase hat höchstens 78 %
Breite. groupStart setzt zusätzlich 8px Abstand nach oben, wenn die Nachrichtenrichtung wechselt.
Ausgehender status |
Blase | Signalbalken | Zusatz |
|---|---|---|---|
queued |
transparent, 1.5px gestrichelt in --og-accent |
Level 0 | – |
sent |
Grund --og-accent |
Level 2 | – |
delivered |
Grund --og-accent |
Level 4 | – |
failed |
transparent, 1.5px Rahmen in --og-danger |
keine | „Nicht gesendet“ und Button „Erneut senden“ |
Der Button „Erneut senden“ löst retry aus. Eingehende Nachrichten zeigen in der Meta-Zeile nur den
übergebenen Text, etwa die Uhrzeit, und keine Signalbalken.
og-composer¶
Die Eingabeleiste für Nachrichten. maxBytes definiert das erlaubte Limit. Die Komponente zählt den Text nur über
den Pflicht-Input countBytes; sie bringt keine eigene Byte-Zählung mit. Der Zähler zeigt
<Zahl> / <maxBytes> Zeichen pro Funkspruch.
- Leerer oder nur aus Leerzeichen bestehender Text deaktiviert das Senden.
- Überschreitet die Zahl aus
countBytesdas Limit, wird der Zähler in--og-dangerdargestellt. blockOverLimitist standardmässigtrueund sperrt dann das Senden. Mitfalsebleibt der Senden-Button aktiv.- Enter sendet den getrimmten Text über
sendund leert das Feld. showLocationblendet den Button „Standort senden“ ein. Er löstlocationaus.
Das sichtbare Eingabefeld hat ein verstecktes Label. text ist ein Model und kann mit [(text)] gebunden werden.
og-qr¶
og-qr erzeugt aus data ein QR-Raster als SVG. Das Paket qrcode erzeugt die Module mit der Fehlerkorrekturstufe
M; @types/qrcode liefert die Typen im Entwicklungsbetrieb. Jedes dunkle Modul wird als SVG-Rechteck gerendert.
Das QR ist 230 × 230px gross und sitzt auf einer Karte mit 22px Innenabstand und Radius --og-r-qr.
Die Karte trägt role="img" und aria-label="QR-Code mit deinem Schlüssel". Die beiden Farbwerte liegen in
ui/qr/qr-colors.ts: QR_DARK ist #0F1115, QR_LIGHT ist das transparente #00000000.