Zum Inhalt
Hey AlbertMarke und Designsystem
Als MarkdownEnglishEN

Bauteile

Bauteile

Alle Bauteile des Designsystems mit Live-Beispiel, Einsatzregel und Typen – deutsch benannt, aus einer Quelle, für Web, Mac, iPhone und iPad.

Ein Bauteil ist hier ein fertiges Stück Oberfläche mit fester Bedeutung: eine Karte, ein Knopf, die Leiste. Jedes Bauteil gibt es genau einmal, in packages/design-system. Die Beispiele auf dieser Seite sind keine Bilder. Sie sind die echten Bauteile, mit Familie Sonntag am Mittwoch, dem 3. September.

Wie ein Bauteil gebaut ist

Jedes Bauteil besteht aus vier Teilen. Sie liegen im selben Ordner und tragen denselben Namen.

DateiWas drinsteht
Name.jsxdas Bauteil in React, nur mit Rollen-Tokens
Name.d.tsdie Props, mit deutschen Namen und Erklärung
Name.prompt.mdEinsatz, Sprachregister und Beispiel mit Familie Sonntag – für Menschen und für Agenten
gruppe.card.htmleine Karte mit allen Zuständen, für die Durchsicht

Die Namen sind deutsch, mit Absicht. Knopf heißt nicht Button, Leiste nicht Sidebar. Wer im Code liest, liest dieselben Wörter wie in der Oberfläche und in dieser Dokumentation.

Was jedes Bauteil können muss

Ein Bauteil gilt erst als fertig, wenn es alle Zustände trägt. Das prüft der Agent design-pruefer vor jedem „fertig“.

  • leerer Zustand
  • Skelett statt Kreisel
  • Tastatur und :focus-visible
  • Handy und Tisch
  • hell und dunkel als Token-Umschaltung
  • optimistisches Update mit „Rückgängig“
  • richtiges Register
  • kein Hex-Wert
  • Zielflächen 28/44/56

Dazu kommen drei Regeln, die in den Beispielen unten sichtbar sind:

  • Inaktiv ist eine Rolle, keine Deckkraft. --knopf-inaktiv-flaeche und --text-inaktiv sind gerechnet, damit der Kontrast trägt.
  • Zeiger hebt die Kontur, nicht die Fläche. Aus 1 px wird 1,5 px. Schatten gibt es nicht.
  • Fokus nur über :focus-visible. Der Ring ist 2 px Offen-Rosa mit 2 px Abstand.

Bausteine

Die kleinsten Teile: Menschen, Zeichen, Knöpfe, Pillen, Zahlen. Aus ihnen sind alle anderen Bauteile gebaut. Ein Mensch erscheint immer als Klebezettel in seiner Personenfarbe, mit Initiale. Farbe allein trägt nie.

Avatar

components/bausteine/Avatar.jsx
Alle · gewählt · zurückgenommen
AT
Größen 20 · 32 · 44 · 56
FFLL
Mit Namen
SSofiaGOma Grete

Der Klebezettel eines Menschen – Personenfarbe mit Initiale, nie Farbe allein.

<Avatar wer="anna" groesse={32} />
<Avatar alle gewaehlt onClick={alleZeigen} />

Personenfarben sind über die ganze App gleich: Anna Mint, Tobias Türkis, Felix Flieder, Lena Pfirsich, Sofia Himmel. Text darauf ist immer Tinte – auch nachts, denn der Zettel bleibt hell.

Typen (.d.ts)
/** Klebezettel je Mensch: Personenfarbe plus Initiale, optional mit Namen. */
export interface AvatarProps {
  /** Schluessel aus Familie Sonntag ("anna", "tobias", "felix", "lena", "sofia", "grete", "werner", "hilde") oder ein Objekt {name, initiale, platz}. */
  wer?: string | { name: string; initiale: string; platz: number };
  /** Durchmesser in px. 20 im Kartenkopf, 32 im Filter, 44 am iPhone, 56 fuer Kinder. */
  groesse?: number;
  /** Name neben dem Kreis - fuer Leiste und Listen. */
  mitName?: boolean;
  /** Der schwarze Kreis mit zwei Koepfen: "alle". */
  alle?: boolean;
  /** Im Personenfilter: true = schwarzer Ring, false = grau zurueckgenommen. */
  gewaehlt?: boolean | null;
  ring?: boolean;
  onClick?: () => void;
  style?: React.CSSProperties;
}
export function Avatar(props: AvatarProps): JSX.Element;

DekoForm

components/bausteine/DekoForm.jsx
Auf Gelb · dunklere Stufe
Auf Blau

Die spielerische Form auf einer gelben oder rosa Fläche – Freude, kein Schmuck.

<DekoForm form="stern" groesse={110} farbe="var(--gelb-dunkel)" style={{ right: -18, bottom: -22 }} />

Die Elternfläche braucht position: relative und overflow: hidden. Nie auf Karten mit Bedienelementen, nie im Safe, nie bei Finanzen.

Typen (.d.ts)
/** Spielerische Form als Deko auf gelben und rosa Flaechen - Stern, Herz, Kleks, Puzzleteil. */
export interface DekoFormProps {
  form?: "stern" | "herz" | "kleks" | "kreis" | "puzzle";
  groesse?: number;
  /** Immer die dunklere Stufe derselben Flaechenfarbe: var(--gelb-dunkel), var(--blau-dunkel). */
  farbe?: string;
  drehung?: number;
  style?: React.CSSProperties;
}
export function DekoForm(props: DekoFormProps): JSX.Element;

FahrtPille

components/bausteine/FahrtPille.jsx
Tobias fährt 15:40 · KombiSofia fährt 13:15Anna fährt 16:40 · Kleiner

Die blaue Pille, die eine Fahrt an eine andere Sache hängt – Bündel statt Einzelteile.

<FahrtPille wer="Papa" zeit="15:40" auto="Kombi" />

Blau heisst Fahrt, immer. Der Text lautet „Papa fährt 15:40 · Kombi“ — wer und wann ohne Trenner, das Auto mit Punkt. Uhrzeit als 15:40, nie "15.40" und nie "15 Uhr".

Typen (.d.ts)
/** Blaue Pille fuer eine Fahrt, die an einer anderen Sache haengt. */
export interface FahrtPilleProps {
  /** Wer faehrt - "Papa", "Anna", "Jonas' Mama". */
  wer?: string;
  /** Uhrzeit als HH:MM. */
  zeit?: string;
  /** Auto aus dem Buendel - "Kombi". */
  auto?: string;
  children?: React.ReactNode;
  style?: React.CSSProperties;
}
export function FahrtPille(props: FahrtPilleProps): JSX.Element;

Icon

components/bausteine/Icon.jsx
fahrtschulesportessenarztmusikfreudefamilieritualschwimmenvorschlagtaler

Ein Zeichen aus dem Lucide-Satz — für alles, was ein Symbol braucht.

<Icon name="fahrt" groesse={16} titel="Fahrt" />
  • name nimmt eine Sach-Rolle (fahrt, schule, sport, essen, arzt, musik, freude, familie, ritual, schwimmen, vorschlag) oder direkt einen Lucide-Dateinamen (bell, search, plus).
  • Das Zeichen wird inline gerendert und über stroke: currentColor eingefärbt — es trägt also die Textfarbe seines Elternteils und kommt in Screenshots und Exporten mit.
  • Quelle sind die Dateien in assets/icons/; components/bausteine/zeichen.js hält ihren Pfad-Inhalt. Kommt ein Zeichen dazu: Datei nach assets/icons/ legen und zeichen.js neu erzeugen — nie ein SVG von Hand zeichnen.
  • Ohne titel ist das Zeichen dekorativ (aria-hidden) — richtig, weil neben jedem Zeichen Text steht.
Typen (.d.ts)
/** Zeichen aus dem Lucide-Satz, inline gerendert und über `stroke` eingefärbt. */
export interface IconProps {
  /** Sach-Rolle ("fahrt", "schule", "essen", "familie", "ritual", "vorschlag", ...) oder Lucide-Dateiname ("bell"). */
  name: string;
  /** Kantenlänge in px. Standard 16. */
  groesse?: number;
  /** Strichstärke. Standard 2 — wie im Satz. */
  strich?: number;
  /** Strichfarbe. Standard currentColor. */
  farbe?: string;
  /** Wenn gesetzt, wird das Zeichen für VoiceOver benannt; sonst ist es dekorativ. */
  titel?: string;
  style?: React.CSSProperties;
}
export function Icon(props: IconProps): JSX.Element | null;
export const SACHEN: Record<string, string>;

Knopf

components/bausteine/Knopf.jsx
Primär · zweit · klein · inaktiv
Zeiger 32 · Finger 44 · Kind 56

Der Handlungsknopf auf Karten und Flächen — schwarz für den einen Schritt, beige für den zweiten.

<Knopfreihe>
  <Knopf art="primaer" onClick={ichFahre}>Ich fahre</Knopf>
  <Knopf art="zweit">Später</Knopf>
</Knopfreihe>
<Knopf ziel="kind" zeichen="check">Fertig</Knopf>

Register Family Manager: kurz, zupackend, ein Verb — „Ich fahre“, „Tobias fragen“, „Übernehmen“, „Rückgängig“. Nie „OK“, nie „Abbrechen“ ohne Gegenstand.

Ein Knopf ist immer einzeilig. Knöpfe stehen in einer Knopfreihe: nebeneinander, wenn beide Platz haben, sonst untereinander in voller Breite — nie ein zweizeiliger Knopf, nie zwei ungleich breite Knöpfe untereinander. ziel="kind" gibt 56 px mit 16-px-Schrift, wo Kinder bedienen. inaktiv rechnet eigene Rollen statt Deckkraft.

Typen (.d.ts)
/**
 * Handlungsknopf. Primär schwarz mit hellem Text („Ich fahre“), Zweitknopf beige („Später“),
 * Klein ohne Fläche für Nebenwege. Schriftgrad und Polster wachsen mit der Zielfläche.
 */
export interface KnopfProps {
  art?: "primaer" | "zweit" | "klein";
  /** Zielfläche: Zeiger 32 / 12,5 px, Finger 44 / 14 px, Kind 56 / 16 px. */
  ziel?: "zeiger" | "finger" | "kind";
  /** Sach-Rolle oder Lucide-Name eines Zeichens links im Knopf. */
  zeichen?: string;
  /** Inaktiv ist eine eigene Rolle, keine Deckkraft. */
  inaktiv?: boolean;
  /** Füllt die Breite - für iPhone-Karten. */
  breit?: boolean;
  /** Erlaubt Zeilenumbruch in der Beschriftung. Standard aus: ein Knopf ist einzeilig;
   *  wird die Reihe zu eng, stapelt die Knopfreihe die Knöpfe in voller Breite. */
  umbruch?: boolean;
  type?: "button" | "submit" | "reset";
  children?: React.ReactNode;
  onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
  style?: React.CSSProperties;
}
export function Knopf(props: KnopfProps): JSX.Element;

/** Die Knopfreihe: nebeneinander, wenn Platz ist, sonst untereinander in voller Breite. */
export interface KnopfreiheProps {
  children?: React.ReactNode;
  /** Mindestbreite je Knopf in px, ab der zwei nebeneinander passen. Standard 104. */
  mindest?: number;
  ausrichtung?: "mitte";
  style?: React.CSSProperties;
}
export function Knopfreihe(props: KnopfreiheProps): JSX.Element;

Knopfreihe

components/bausteine/Knopf.jsx
Genug Platz · nebeneinander
Eng · gestapelt, volle Breite

Punktgruppe

components/bausteine/Punktgruppe.jsx
EinerL
Zuständige zuerstAL
Alle vierATFL
Groß, 28TF

Wer dabei ist – die überlappenden Punkte rechts im Kartenkopf.

<Punktgruppe leute={["anna", "lena"]} />

Der Zuständige steht immer zuerst. Alles hat genau einen Zuständigen; fehlt er, trägt die Karte den gestrichelten Offen-Rand und nicht einfach keinen Punkt.

Typen (.d.ts)
/** Die Punkte rechts im Kartenkopf: wer dabei ist. Initiale in Personenfarbe, bei mehreren ueberlappend. */
export interface PunktgruppeProps {
  /** Schluessel aus Familie Sonntag, in der Reihenfolge der Zustaendigkeit. */
  leute?: Array<string | { name: string; initiale: string; platz: number }>;
  /** Durchmesser in px. Standard 20. */
  groesse?: number;
  style?: React.CSSProperties;
}
export function Punktgruppe(props: PunktgruppeProps): JSX.Element;

SachSymbol

components/bausteine/SachSymbol.jsx
Mac · 26
Auf farbiger Karte

Der helle Kreis mit dem Zeichen der Sache – immer links im Kartenkopf, nie woanders.

<SachSymbol art="fahrt" titel="Fahrt" />

Was für eine Sache es ist, sagt dieses Symbol; wer dabei ist, sagen die Punkte rechts (Punktgruppe); was für eine Art es ist, sagt die Kartenfarbe. Kein Menü, kein Chevron im Kopf.

Typen (.d.ts)
/** Zeichen der Sache in einem hellen Kreis - steht links im Kartenkopf. */
export interface SachSymbolProps {
  /** Sach-Rolle: "fahrt" | "schule" | "sport" | "essen" | "arzt" | "musik" | "freude" | "familie" | "ritual" | "vorschlag" | "schwimmen". */
  art?: string;
  /** Kreisdurchmesser in px. Standard 26 (Mac), 24 auf dem iPhone. */
  groesse?: number;
  /** Auf farbigen Karten - haelt den Kreis hell. */
  aufFarbe?: boolean;
  titel?: string;
  style?: React.CSSProperties;
}
export function SachSymbol(props: SachSymbolProps): JSX.Element;

Zaehler

components/bausteine/Zaehler.jsx
Akzent
312
Auf Schwarz
2
Punkt an der Klingel

Der rosa Zaehler – am Ort "Eingang", am Chip "Post", an der Klingel als Punkt.

<Zaehler wert={3} titel="3 Sachen im Eingang" />
<Zaehler art="punkt" titel="Neues da" />

Bei Null zeigen wir keinen Zaehler, sondern nichts – und im Ort selbst den leeren Zustand.

Typen (.d.ts)
/** Rosa Zaehler an Ort, Chip oder Klingel. */
export interface ZaehlerProps {
  /** Die Zahl. Bei 0 nicht anzeigen - Leere ist ein leerer Zustand, kein Zaehler. */
  wert?: number | string;
  /** "akzent" (Standard) | "punkt" (rosa Punkt ohne Zahl, fuer die Klingel). */
  art?: "akzent" | "aufSchwarz" | "punkt";
  groesse?: number;
  /** Beschriftung fuer VoiceOver, z. B. "3 Sachen im Eingang". */
  titel?: string;
  style?: React.CSSProperties;
}
export function Zaehler(props: ZaehlerProps): JSX.Element;

ZahlKachel

components/bausteine/ZahlKachel.jsx
8,7h
heute verplant
2
Fahrten heute
1
offen diese Woche

Die farbige Kachel mit der grossen Zahl – drei davon stehen im Wochenblick nebeneinander.

<ZahlKachel zahl="8,7" einheit="h" beschriftung="heute verplant" farbe="gelb" form="stern" />
<ZahlKachel zahl="2" beschriftung="Fahrten heute" farbe="rosa" form="kleks" />
<ZahlKachel zahl="1" beschriftung="offen diese Woche" farbe="blau" form="kreis" />

Beschriftung ohne Verb und ohne Wertung – "offen diese Woche", nie "noch offen!" und nie ein Fortschrittsbalken daneben.

Typen (.d.ts)
/**
 * Farbige Kachel mit grosser Zahl - der Blick auf die Woche.
 */
export interface ZahlKachelProps {
  /** Die Zahl, deutsch geschrieben: "8,7", "2", "1". */
  zahl: string | number;
  /** Einheit direkt an der Zahl: "h". */
  einheit?: string;
  /** Ein Satzteil, klein darunter: "heute verplant", "Fahrten heute", "offen diese Woche". */
  beschriftung: string;
  farbe?: "gelb" | "rosa" | "blau";
  form?: "stern" | "herz" | "kleks" | "kreis" | "puzzle" | "keine";
  style?: React.CSSProperties;
}
export function ZahlKachel(props: ZahlKachelProps): JSX.Element;

ZustandsPille

components/bausteine/ZustandsPille.jsx
Kein FahrerAbholen 19:00 · AnnaOhne Zuständigenseit 12:30 · alle

Die kleine Pille auf der Karte, die den Zustand der Sache nennt.

<ZustandsPille>Kein Fahrer</ZustandsPille>
<ZustandsPille>Abholen 19:00 · Anna</ZustandsPille>

Register Family Manager: Sache, Zeit, Mensch – in dieser Reihenfolge, ohne Verb. Rosa heisst hier Akzent und Jetzt, niemals Fehler.

Typen (.d.ts)
/** Kleine Pille auf der Karte: "Kein Fahrer", "Abholen 19:00 - Anna". */
export interface ZustandsPilleProps {
  /** "zustand" rosa hell (Standard) | "offen" gestrichelt (ohne Zustaendigen) | "ruhig" hell mit Haarlinie. */
  art?: "zustand" | "offen" | "ruhig";
  children?: React.ReactNode;
  style?: React.CSSProperties;
}
export function ZustandsPille(props: ZustandsPilleProps): JSX.Element;

Karten

Die Karte ist das wichtigste Bauteil. Es gibt sie in fünf Arten, und die Art sagt die Bedeutung: beige für einen Termin, blau für eine Fahrt, gelb für Familie und Ritual, gestrichelt grau für einen Vorschlag von Albert, gestrichelt rosa für etwas ohne Zuständigen. Links im Kopf steht die Sache, rechts stehen die Menschen.

BuendelKarte

components/karten/BuendelKarte.jsx
Drei Sachen um 16:0016:00 – 19:00
Kieferorthopädin16:00AL
Training16:00F
Abholen Halle Süd19:00

Die Antwort auf Gleichzeitiges: eine Karte, drei Zeilen – statt drei Karten, die sich stapeln.

<BuendelKarte titel="Drei Sachen um 16:00" zeit="16:00 – 17:30" teile={[
  { titel: "Kieferorthopädin", symbol: "arzt", zeit: "16:00 – 17:00", leute: ["anna", "lena"] },
  { titel: "Training", art: "termin", symbol: "sport", zeit: "16:00 – 17:30", leute: ["felix"] },
  { titel: "Abholen Halle Süd", art: "fahrt", zeit: "19:00", offen: true }
]} />

Zeilen behalten ihre Bedeutungsfarbe. Eine Zeile ohne Zuständigen wird laut gezeigt: gestrichelter Offen-Rand, nie stillschweigend leer.

Typen (.d.ts)
/** Eine Karte fuer Gleichzeitiges: mehrere Sachen zur selben Stunde, jede als Zeile. */
export interface BuendelTeil {
  titel: string;
  art?: "termin" | "fahrt" | "familie";
  /** Sach-Symbol, wenn es nicht aus der Art folgt. */
  symbol?: string;
  zeit?: string;
  leute?: string[];
  /** Ohne Zustaendigen - die Zeile bekommt den gestrichelten Offen-Rand. */
  offen?: boolean;
}
export interface BuendelKarteProps {
  /** Was die Stunde traegt: "Drei Sachen um 16:00". */
  titel: string;
  zeit?: string;
  teile: BuendelTeil[];
  style?: React.CSSProperties;
}
export function BuendelKarte(props: BuendelKarteProps): JSX.Element;

Karte

components/karten/Karte.jsx
AL
Kieferorthopädin
Praxis Dr. Voss
16:00 – 17:00
TF
Handball, Halle Süd
Schule → Halle Süd
Fr 16:40
Tobias fährt 16:40 · Kombi
ATFL
Mittagessen bei Oma Grete
So 12:30 · alle
Bei Tobias steht ab 13:00 nichts. Fragen?
Vorschlag von Albert
Abholen Halle Süd
19:00
Ohne Zuständigen
Mathe mit Felix17:30F

Die Karte – das Ding, aus dem Heute, Woche und Kalender bestehen. Ein Datenmodell, viele Ansichten.

<Karte art="fahrt" titel="Rückfahrt Auswärtsspiel" ort="Sportplatz Nord → Zuhause"
  zeit="14:00 – 14:30" leute={["felix"]} hoehe="hoch"
  pillen={<ZustandsPille>Kein Fahrer</ZustandsPille>}
  knoepfe={<Knopf>Ich fahre</Knopf>} />

<Karte art="vorschlag" titel="Bei Tobias steht ab 13:00 nichts. Fragen?"
  unterzeile="Vorschlag von Albert"
  knoepfe={<KarteVorschlagKnoepfe onUebernehmen={…} onZurueck={…} />} />

Register Family Manager, kurz und ruhig. Die Bedeutungen werden nicht getauscht: blau ist Fahrt, gelb ist Familie, gestrichelt rosa heisst "ohne Zuständigen". Ein Vorschlag hat immer zwei Knöpfe – Albert erledigt nie still.

Typen (.d.ts)
/**
 * Die Karte in fuenf Arten. Die Farbe sagt die Art, das Symbol die Sache, die Punkte die Leute.
 */
export interface KarteProps {
  /** "termin" beige | "fahrt" blau | "familie" gelb | "vorschlag" gestrichelt grau | "offen" gestrichelt rosa. */
  art?: "termin" | "fahrt" | "familie" | "vorschlag" | "offen";
  /** Kartentitel, ein Gegenstand: "Rückfahrt Auswärtsspiel". */
  titel: string;
  /** Zweitzeile: Ort oder Bündel-Teile, mit " · " getrennt. */
  ort?: React.ReactNode;
  /** Zeitspanne als "16:00 – 17:00" (Halbgeviertstrich mit Leerzeichen). */
  zeit?: string;
  /** Wer dabei ist; der Zustaendige zuerst. */
  leute?: string[];
  /** Sach-Symbol, wenn es nicht aus der Art folgt: "schule", "sport", "arzt", "musik". */
  symbol?: string;
  /** "kurz" (78 px) oder "hoch" (132 px) - fuer das Stundenraster. */
  hoehe?: "kurz" | "hoch";
  /** Zustands- und Fahrt-Pillen. */
  pillen?: React.ReactNode;
  /** Handlungsknoepfe; bei Vorschlaegen immer zwei. */
  knoepfe?: React.ReactNode;
  /** Kleine Zeile unter dem Inhalt, z. B. "Vorschlag von Albert". */
  unterzeile?: React.ReactNode;
  /** Einzeiler fuer Listen: Symbol, Titel, Zeit, Punkt. */
  kompakt?: boolean;
  klickbar?: boolean;
  /** Leicht gekippt, wie ein liegender Klebezettel. */
  gekippt?: boolean;
  children?: React.ReactNode;
  style?: React.CSSProperties;
}
export function Karte(props: KarteProps): JSX.Element;
export function KarteVorschlagKnoepfe(props: { uebernehmen?: string; zurueck?: string; onUebernehmen?: () => void; onZurueck?: () => void }): JSX.Element;

Kartenkopf

components/karten/Kartenkopf.jsx
Schwimmen · Lena
L
Fahrt · Tobias und Felix
TF
Ritual · alle
ATFL

Der Kartenkopf – Symbol links, Leute rechts, nichts dazwischen.

<Kartenkopf art="fahrt" leute={["sofia"]} titel="Fahrt" />

Drei Träger, drei Orte: Kartenfarbe sagt die Art, Symbol sagt die Sache, Punkte sagen die Leute. Wird nur von Karte gebraucht – eigenständig nur, wenn du eine eigene Kartenform baust.

Typen (.d.ts)
/** Der Kopf jeder Karte: Sach-Symbol links, Punktgruppe rechts. */
export interface KartenkopfProps {
  /** Sach-Rolle - bestimmt das Symbol, nicht die Kartenfarbe. */
  art?: string;
  leute?: Array<string>;
  /** Durchmesser des Symbolkreises; die Punkte folgen mit 77 %. */
  groesse?: number;
  titel?: string;
  style?: React.CSSProperties;
}
export function Kartenkopf(props: KartenkopfProps): JSX.Element;

KonfliktKarte

components/karten/KonfliktKarte.jsx
LF
Lena schwimmt, Felix spielt – beides um 16:00.
Ein Auto, zwei Orte. Sofia ist bis 17:00 da.

Die Karte für zwei Sachen, die sich treffen – sie zeigt es und hält niemanden auf.

<KonfliktKarte titel="Lena schwimmt, Felix spielt Handball – beides um 16:00."
  erklaerung="Ein Auto, zwei Orte. Sofia ist bis 17:00 da." wer={["lena", "felix"]}
  schritt="Sofia fragen" zweitSchritt="Später" />

Register Family Manager. Kein Rot, kein Ausrufezeichen, kein "Fehler" – die Karte stellt fest und nennt einen Schritt.

Typen (.d.ts)
/** Zwei Sachen treffen sich. Sichtbar, nie blockierend, mit naechstem Schritt. */
export interface KonfliktKarteProps {
  /** Was sich trifft, als Feststellung: "Lena schwimmt und Felix spielt - beides 16:00." */
  titel: string;
  /** Warum es klemmt, ein Satz. */
  erklaerung?: React.ReactNode;
  wer?: string[];
  /** Der naechste Schritt als Verb: "Tobias fragen". */
  schritt?: string;
  onSchritt?: () => void;
  zweitSchritt?: string;
  onZweitSchritt?: () => void;
  style?: React.CSSProperties;
}
export function KonfliktKarte(props: KonfliktKarteProps): JSX.Element;

SpurenJeMensch

components/karten/SpurenJeMensch.jsx
15:0016:0017:0018:0019:00
AAnna
Kieferorthopädin
TTobias
Abholen Halle Süd
SSofia
Abholen Schule

Spuren je Mensch – wenn drei Sachen gleichzeitig laufen und die Frage "wer wo" lautet.

<SpurenJeMensch leute={["anna", "tobias", "sofia"]} von={15} bis={20} spuren={{
  anna:   [{ titel: "Kieferorthopädin", von: 16, bis: 17 }],
  tobias: [{ titel: "Abholen Halle Süd", von: 19, bis: 19.5, art: "fahrt", offen: true }],
  sofia:  [{ titel: "Abholen Schule", von: 15, bis: 15.5, art: "fahrt" }]
}} />

Jede Spur trägt Name und Avatar, nicht nur Farbe. Dieselben Daten wie im Stundenraster – ein Datenmodell, viele Ansichten.

Typen (.d.ts)
/** Je Mensch eine Spur ueber die Stunden - die dritte Antwort auf Gleichzeitiges. */
export interface Spur {
  titel: string;
  /** Stunde als Dezimalzahl: 16.5 heisst 16:30. */
  von: number;
  bis: number;
  art?: "termin" | "fahrt" | "familie";
  offen?: boolean;
}
export interface SpurenJeMenschProps {
  /** Schluessel aus Familie Sonntag, eine Spur je Person. */
  leute: string[];
  /** Spuren je Personenschluessel. */
  spuren: Record<string, Spur[]>;
  /** Erste und letzte Stunde der Achse. */
  von?: number;
  bis?: number;
  style?: React.CSSProperties;
}
export function SpurenJeMensch(props: SpurenJeMenschProps): JSX.Element;

VorschlagKarte

components/karten/VorschlagKarte.jsx
Samstag fehlt ein Fahrer.
Rückfahrt vom Auswärtsspiel, 14:00. Tobias hat ab 13:00 frei.
Vorschlag von Albert

Alberts Vorschlag auf dem iPhone – gelb, mit Formen, immer mit zwei Knöpfen.

<VorschlagKarte titel="Samstag fehlt ein Fahrer."
  text="Rückfahrt vom Auswärtsspiel, 14:00. Bei Tobias steht ab 13:00 nichts."
  schritt="Tobias fragen" zweitSchritt="Später" />

So klingt Albert: eine Beobachtung, eine Frage, zwei Knöpfe. Nie "Ich habe für dich…". Auf dem Mac nimmst du stattdessen <Karte art="vorschlag"> - dort ist der Vorschlag gestrichelt und ohne Füllung.

Typen (.d.ts)
/**
 * Alberts Vorschlag am iPhone: gelbe Karte mit Formen, zwei Knoepfe, Unterzeile.
 */
export interface VorschlagKarteProps {
  /** Eine Beobachtung: "Samstag fehlt ein Fahrer." */
  titel: string;
  /** Zwei kurze Saetze: was er sieht, was daraus folgt. */
  text?: React.ReactNode;
  /** Erster Knopf, ein Verb: "Tobias fragen", "Übernehmen". */
  schritt?: string;
  onSchritt?: () => void;
  /** Zweiter Knopf. Es gibt immer zwei - Albert erledigt nie still. */
  zweitSchritt?: string;
  onZweitSchritt?: () => void;
  unterzeile?: string;
  style?: React.CSSProperties;
}
export function VorschlagKarte(props: VorschlagKarteProps): JSX.Element;

Kopf

Der Kopf jeder Fläche: Anrede, Datum, Suche, Bereiche, Filter nach Menschen, Spanne der Ansicht. Auf dem Mac steht er über dem Wochenboard, am iPhone über dem Tag.

Anredezeile

components/kopf/Anredezeile.jsx
Mac · 40

Guten Morgen, Anna.

Mittwoch, 3. September · KW 36
iPhone · 30

Mittwoch

3. September

Die grosse Zeile, mit der eine Fläche anfängt.

<Anredezeile>Guten Morgen, Anna.</Anredezeile>
<Anredezeile geraet="phone" nebenzeile="3. September · KW 36">Mittwoch</Anredezeile>

Register Erzähler, wenn sie grüßt; Family Manager, wenn sie nur benennt. Punkt am Satzende, nie ein Ausrufezeichen. "du" zum Einzelnen, "ihr" zur Familie.

Typen (.d.ts)
/** Die grosse Zeile oben auf der Flaeche - Bricolage 800, eng gelaufen. */
export interface AnredezeileProps {
  /** "Guten Morgen, Anna." | "Mittwoch" | "Woche 36" */
  children: React.ReactNode;
  /** 40 px auf dem Mac, 30 px auf dem iPhone. */
  geraet?: "mac" | "phone";
  /** Kleine Zeile darunter: "3. September · KW 36". */
  nebenzeile?: React.ReactNode;
  style?: React.CSSProperties;
}
export function Anredezeile(props: AnredezeileProps): JSX.Element;

DatumPille

components/kopf/DatumPille.jsx

Die schwarze Datum-Pille unter der Anredezeile.

<DatumPille onClick={kalenderAuf}>1. – 7. September</DatumPille>

Datum als Mi 3.9. oder 3. September; Spannen mit Halbgeviertstrich und Leerzeichen.

Typen (.d.ts)
/** Die schwarze Pille mit der Spanne: "1. – 7. September". */
export interface DatumPilleProps {
  /** Datum oder Spanne. Halbgeviertstrich mit Leerzeichen: "1. – 7. September". */
  children: React.ReactNode;
  onClick?: () => void;
  aufklappbar?: boolean;
  style?: React.CSSProperties;
}
export function DatumPille(props: DatumPilleProps): JSX.Element;

Kopfzeile

components/kopf/Kopfzeile.jsx
In:

Der Kopf jeder Fläche auf dem Mac und iPad – Suche links, drei Kreise rechts.

<Kopfzeile bereiche={["Termine", "Fahrten", "Aufgaben", "Dokumente", { name: "Post", zaehler: 2 }]}
  onPlus={() => setMenue(true)} />

Die Reihenfolge der Kreise steht fest: Klingel, Nachrichten, Plus – Plus ist rosa und immer der letzte. Chips sind gestrichelt, weil sie Bereiche einschränken und nichts anlegen.

Typen (.d.ts)
/**
 * Der Kopf jeder Flaeche: Suchfeld mit rosa Lupe, Bereichs-Chips, drei Kreise rechts.
 */
export interface KopfzeileProps {
  platzhalter?: string;
  /** Bereichs-Chips, gestrichelt: "Termine", "Fahrten", "Aufgaben", "Dokumente", { name: "Post", zaehler: 2 }. */
  bereiche?: Array<string | { name: string; zaehler?: number }>;
  gewaehlterBereich?: string;
  onBereich?: (name: string) => void;
  onSuche?: (e: React.ChangeEvent<HTMLInputElement>) => void;
  /** Rosa Punkt an der Klingel, wenn etwas Neues da ist. */
  klingelPunkt?: boolean;
  onKlingel?: () => void;
  onNachrichten?: () => void;
  /** Oeffnet das Schnellmenue "Schnell anlegen". */
  onPlus?: () => void;
  style?: React.CSSProperties;
}
export function Kopfzeile(props: KopfzeileProps): JSX.Element;

PersonenFilter

components/kopf/PersonenFilter.jsx
Mac · 32 · zum Antippen
Kinderbereich · 56

Der Personenfilter – Kreise, kein Auswahlmenü.

<PersonenFilter leute={["anna", "tobias", "felix", "lena", "sofia"]}
  gewaehlt={gewaehlt} onWahl={setGewaehlt} />

Der erste Kreis ist schwarz mit zwei Köpfen und heisst "alle". Personenfarben sind überall dieselben; jeder Kreis trägt seine Initiale, damit Farbe nicht allein trägt.

Typen (.d.ts)
/**
 * Die Kreise, mit denen man die Flaeche auf Leute einschraenkt.
 */
export interface PersonenFilterProps {
  /** Schluessel aus Familie Sonntag, in fester Reihenfolge. */
  leute: string[];
  /** Leeres Feld heisst alle. */
  gewaehlt?: string[];
  /** 32 px auf dem Mac, 44 px am iPhone, 56 px im Kinderbereich. */
  groesse?: number;
  onWahl?: (gewaehlt: string[]) => void;
  style?: React.CSSProperties;
}
export function PersonenFilter(props: PersonenFilterProps): JSX.Element;

SchnellMenue

components/kopf/SchnellMenue.jsx
Schnell anlegen

Das Schnellmenü hinter dem rosa Plus.

<SchnellMenue onWahl={art => anlegen(art)} />

Sechs Einträge, Gegenstände statt Verben – "Termin", nicht "Termin anlegen". Die Tastenkürzel stehen nur auf dem Mac.

Typen (.d.ts)
/** Das Menue am Plus-Kreis: Termin, Aufgabe, Fahrt, Dokument scannen, Kontakt, Notiz. */
export interface SchnellMenueEintrag { name: string; zeichen?: string; taste?: string }
export interface SchnellMenueProps {
  titel?: string;
  eintraege?: SchnellMenueEintrag[];
  onWahl?: (name: string) => void;
  style?: React.CSSProperties;
}
export function SchnellMenue(props: SchnellMenueProps): JSX.Element;

SpannenRegler

components/kopf/SpannenRegler.jsx
Zeiger
Finger · 44

Der Regler für die Spanne – dieselben Daten, andere Weite.

<SpannenRegler gewaehlt="Mo – So" onWahl={setSpanne} />

Die Reihenfolge steht fest und wird nicht sortiert: Heute, 3 Tage, Mo – Fr, Mo – So, Monat. Halbgeviertstrich mit Leerzeichen in den Beschriftungen.

Typen (.d.ts)
/** Die Spanne der Ansicht: Heute, 3 Tage, Mo – Fr, Mo – So, Monat. */
export interface SpannenReglerProps {
  spannen?: string[];
  gewaehlt?: string;
  onWahl?: (spanne: string) => void;
  /** "finger" gibt 44 px fuer iPhone und iPad. */
  ziel?: "zeiger" | "finger";
  style?: React.CSSProperties;
}
export function SpannenRegler(props: SpannenReglerProps): JSX.Element;

Leiste

Die schwarze Leiste ist eine Signatur von Hey Albert. Auf dem Mac steht sie links, voll mit 220 px oder eingeklappt mit 72 px. Am iPhone steht sie unten, fixiert, mit dem herausstehenden Plus.

LeisteMac

components/leiste/LeisteMac.jsx

Die schwarze Leiste des Mac – Logo, drei Gruppen, unten der Mensch.

<LeisteMac aktiverOrt="Woche" onWahl={setOrt} style={{ height: "100%" }} />
<LeisteMac eingeklappt fassung="pille" aktiverOrt="Woche" />

Der aktive Ort trägt einen rosa Strich links und ein aufgehelltes Feld. Eingeklappt als fassung="pille" tritt er als rosa Kreis seitlich heraus und die Leiste beult sich um ihn. Albert steht nicht in der Leiste – er ist kein Ort.

Das Logo lädt aus assets/logo-albert-hell.png; setze auf <html> data-albert-logo="<pfad>", wenn es woanders liegt.

Typen (.d.ts)
/**
 * Die schwarze Leiste auf dem Mac - voll (220 px) oder eingeklappt (72 px) als Block oder als Pille.
 */
export interface LeisteOrt { name: string; zeichen: string; zaehler?: number }
export interface LeisteGruppe { gruppe: string; eintraege: LeisteOrt[] }
export interface LeisteMacProps {
  /** Der Ort, auf dem man steht: "Heute" | "Woche" | "Eingang" | "Essen" | ... */
  aktiverOrt?: string;
  /** Gruppen der Navigation. Standard: Flächen, Bereiche, Persönlich. */
  orte?: LeisteGruppe[];
  eingeklappt?: boolean;
  /** Eingeklappt gibt es zwei Fassungen: schmaler "block" oder rundum abgerundete "pille",
   *  bei der der aktive Ort als rosa Kreis seitlich heraustritt. */
  fassung?: "block" | "pille";
  /** Der Mensch unten in der Leiste. */
  ich?: string;
  onWahl?: (ort: string) => void;
  onKlappen?: () => void;
  onProfil?: () => void;
  style?: React.CSSProperties;
}
export function LeisteMac(props: LeisteMacProps): JSX.Element;
export const ORTE: LeisteGruppe[];

LeistePhone

components/leiste/LeistePhone.jsx

Die Leiste am unteren Rand des iPhones – fünf Plätze, nur das Plus tritt heraus.

<LeistePhone aktiverOrt="Woche" onWahl={setOrt} onPlus={schnellAnlegen} />

Der aktive Ort ist rosa unterstrichen, der Eingang trägt seinen rosa Zaehler. Zielfläche 44 px, das Plus 56 px – es wird auch von Kindern gedrückt.

Typen (.d.ts)
/**
 * Die schwarze Leiste unten am iPhone: Heute, Woche, Plus, Eingang, Ich.
 */
export interface LeistePhoneProps {
  aktiverOrt?: string;
  plaetze?: Array<{ name: string; zeichen: string; zaehler?: number; plus?: boolean }>;
  onWahl?: (ort: string) => void;
  onPlus?: () => void;
  style?: React.CSSProperties;
}
export function LeistePhone(props: LeistePhoneProps): JSX.Element;
export const PLAETZE: Array<{ name: string; zeichen: string; zaehler?: number; plus?: boolean }>;

Zeit

Alles, was die Woche ordnet: die Reiter über dem Raster, das Stundenraster selbst, die Jetzt-Linie, die Woche als Liste und der Wochenüberblick. Alle zeigen dieselben Sachen aus demselben Datenmodell.

JetztLinie

components/zeit/JetztLinie.jsx
16:40

Die Jetzt-Linie im Stundenraster.

<JetztLinie zeit="16:40" oben={210} />

Rosa heisst hier "jetzt" – niemals "Fehler". Die Linie ist gestrichelt, läuft über die ganze Breite und fängt keine Klicks.

Typen (.d.ts)
/** Gestrichelte Achse quer durch das Raster, mit der Uhrzeit als rosa Pille. */
export interface JetztLinieProps {
  /** Uhrzeit als HH:MM. */
  zeit?: string;
  /** px von oben, wenn die Linie im Raster absolut sitzt. */
  oben?: number;
  style?: React.CSSProperties;
}
export function JetztLinie(props: JetztLinieProps): JSX.Element;

Listenzeile

components/zeit/Listenzeile.jsx
Elternabend Klasse 619:00A

Die Woche als Liste – für das iPhone, wo ein Raster zu dicht wird.

<Listenzeile tag="Mi 3." heute text="heute · Anna und Felix" wert="3,0 h" aufgeklappt>
  <Karte kompakt titel="Elternabend Klasse 6" symbol="schule" zeit="19:00" leute={["anna"]} />
</Listenzeile>

Der Text sagt, wer den Tag trägt, nicht wie voll er ist. Zahlen deutsch: "3,0 h".

Typen (.d.ts)
/** Eine Zeile der Woche als Liste: Tag-Pille, Satzteil, verplante Zeit. */
export interface ListenzeileProps {
  /** Tag als Pille: "Mo 1.", "Mi 3.". */
  tag: string;
  heute?: boolean;
  /** Ein Satzteil, klein: "heute · Anna und Felix", "vorbei · zum Aufklappen". */
  text?: React.ReactNode;
  /** Verplante Zeit rechts: "3,0 h". */
  wert?: string;
  /** Setzt das Chevron; undefined heisst kein Chevron. */
  aufgeklappt?: boolean;
  onClick?: () => void;
  /** Die Karten des Tages, wenn die Zeile offen ist. */
  children?: React.ReactNode;
  style?: React.CSSProperties;
}
export function Listenzeile(props: ListenzeileProps): JSX.Element;

Tagesreiter

components/zeit/Tagesreiter.jsx
Mac · mit KW
KW36
iPhone · Samstag hat Offenes

Die Tagesreiter – auf dem Mac Karten, am iPhone Kreise.

<Tagesreiter kw={36} tage={[
  { tag: "MONTAG", zahl: "1.9." }, { tag: "MITTWOCH", zahl: "3.9.", heute: true }, { tag: "SONNTAG", zahl: "7.9." }
]} />
<Tagesreiter variante="phone" gewaehlt="MI" tage={[{ tag: "MO", zahl: "1" }, { tag: "MI", zahl: "3", heute: true }, { tag: "SA", zahl: "6", offen: true }]} />

Heute ist schwarz, der Sonntag steht in gedämpftem Rosa-Grau. Am iPhone heisst ein rosa Kreis: an diesem Tag ist etwas offen.

Typen (.d.ts)
/**
 * Die Reiter ueber dem Raster: Wochentag in Kapitaelchen, Datum darunter.
 */
export interface Tag {
  /** Wochentag: "MONTAG" auf dem Mac, "MO" am iPhone.
   *  Das ist der Schluessel: onWahl meldet diesen Wert zurueck, und "gewaehlt" wird dagegen geprueft. */
  tag: string;
  /** Datum: "1.9." auf dem Mac, "1" am iPhone. */
  zahl: string;
  /** Nur Mac: eigener Schluessel fuer onWahl, wenn "tag" nicht eindeutig ist. */
  name?: string;
  heute?: boolean;
  /** Am iPhone: Tag mit Offenem bekommt einen rosa Kreis. */
  offen?: boolean;
}
export interface TagesreiterProps {
  tage: Tag[];
  /** Am iPhone der gewaehlte Tag - derselbe Wert wie "tag", z. B. "MI". */
  gewaehlt?: string;
  variante?: "mac" | "phone";
  /** Kalenderwoche links vor den Reitern - nur auf dem Mac. */
  kw?: string | number;
  onWahl?: (tag: string) => void;
  style?: React.CSSProperties;
}
export function Tagesreiter(props: TagesreiterProps): JSX.Element;

Wochenueberblick

components/zeit/Wochenueberblick.jsx
Wer trägt die Woche1 ohne Zuständigen
MO13,4 h
SLT
DI22,6 h
SA
MI33,0 h
SAFT
DO44,1 h
SLF
FR55,2 h
TFL
SA62,0 h
F?
SO71,5 h
Oma GreteSonntagsrunde
Ein Zettel je Sache: wer, und was für eine Sache.Familieohne Zuständigen

Die Woche auf einen Blick — die Frage, die sie beantwortet: Wer trägt welchen Tag, und wo fehlt jemand?

<Wochenueberblick tage={[
  { tag: "MO", zahl: "1", stunden: "3,4 h", sachen: [{ wer: "sofia", art: "fahrt" }, { wer: "lena", art: "schwimmen" }] },
  { tag: "MI", zahl: "3", heute: true, stunden: "3,0 h", sachen: [{ wer: "sofia", art: "fahrt" }, { wer: "anna", art: "arzt" }, { wer: "felix", art: "sport" }] },
  { tag: "SA", zahl: "6", stunden: "2,0 h", sachen: [{ wer: "felix", art: "sport" }, { offen: true, art: "fahrt", titel: "Rückfahrt" }] }
]} />

Jede Sache ist ein kleiner Zettel: die Initiale sagt wer, das Zeichen sagt was. Der Tag mit Offenem zeigt es laut — gestrichelter Zettel, und oben zählt „1 ohne Zuständigen“. Dieselben Daten wie Raster und Liste; das ist der Blick, den man am iPhone im Vorbeigehen wirft.

Typen (.d.ts)
/**
 * Die Woche auf einen Blick: wer trägt welchen Tag, wo fehlt jemand.
 * Je Sache ein kleiner Zettel — Initiale in Personenfarbe plus Zeichen der Sache.
 */
export interface UeberblickSache {
  /** Personenschlüssel des Zuständigen. */
  wer?: string;
  /** Sach-Rolle für das Zeichen: "fahrt", "schule", "sport", "essen", "arzt", "musik", "freude", "ritual". */
  art?: string;
  titel?: string;
  /** Ohne Zuständigen: gestrichelter Zettel mit „?“ in Offen-Rosa. Zählt oben mit. */
  offen?: boolean;
  /** Familie und Ritual: gelber Zettel. */
  familie?: boolean;
}
export interface UeberblickTag {
  /** Kurzform: "MO" … "SO". */
  tag: string;
  zahl: string;
  heute?: boolean;
  /** Verplante Zeit des Tages: "3,0 h". */
  stunden?: string;
  sachen?: UeberblickSache[];
}
export interface WochenueberblickProps {
  /** Überschrift der Karte. Standard „Wer trägt die Woche“. */
  titel?: string;
  tage: UeberblickTag[];
  style?: React.CSSProperties;
}
export function Wochenueberblick(props: WochenueberblickProps): JSX.Element;

Zeitachse

components/zeit/Zeitachse.jsx
15:0015:3016:0016:3017:0017:3018:00
S
Abholen
Schule → Zuhause
L
Schwimmen
Hallenbad · Badezeug
16:00 – 17:00
S
Abholen
Schule → Zuhause
AL
Kieferorthopädin
Praxis Dr. Voss
16:00 – 17:00
TF
Handball, Halle Süd
16:40 Anpfiff
16:00 – 17:30
Tobias fährt 15:40
ATFL
Sonntagsrunde
17:00 – 17:45
16:40

Das Stundenraster von Mac und iPhone – es setzt Karten, es baut sie nicht.

<Zeitachse von="14:00" bis="19:00" jetzt="16:40" spalten={[
  { name: "Mi 3.9.", sachen: [
    { von: "16:00", bis: "17:00", element: <Karte titel="Kieferorthopädin" symbol="arzt" leute={["anna", "lena"]} /> }
  ] }
]} />

Eine Spalte je Tag (Woche) oder je Mensch (Spuren). Uhrzeiten immer HH:MM; die Achse läuft in Roboto Mono mit tabellarischen Ziffern.

Die Achse setzt nur den Anfang und eine Mindesthöhe aus der Dauer. Sie deckelt nie – eine Karte wird so hoch, wie ihr Inhalt braucht, und legt sich bei Bedarf über die nächste (spätere Sachen liegen oben). Gib der Karte darum kein height und kein overflow: hidden mit, sonst schneidest du ihren Inhalt ab.

Typen (.d.ts)
/**
 * Das Stundenraster: Achse links, eine Spalte je Tag oder Mensch, Sachen nach Uhrzeit gesetzt.
 */
export interface ZeitachseSache {
  /** Beginn als HH:MM. */
  von: string;
  /** Ende als HH:MM; ohne Ende bekommt die Sache ihre Mindesthoehe. */
  bis?: string;
  /** Was gezeichnet wird - in der Regel eine <Karte>. */
  element: React.ReactNode;
  /** Zusaetzliche Mindesthoehe in px, damit eine kurze Sache lesbar bleibt.
   *  Die Dauer setzt ebenfalls eine Mindesthoehe - gedeckelt wird nie, Karten wachsen mit ihrem Inhalt. */
  mindest?: number;
}
export interface ZeitachseSpalte { name?: string; sachen?: ZeitachseSache[] }
export interface ZeitachseProps {
  von?: string;
  bis?: string;
  /** Abstand der Marken in Minuten. Standard 30. */
  schritt?: number;
  /** Hoehe einer Stunde in px. Standard 80. */
  pxProStunde?: number;
  spalten: ZeitachseSpalte[];
  /** Uhrzeit der Jetzt-Linie, z. B. "16:40". Ohne Angabe keine Linie. */
  jetzt?: string;
  style?: React.CSSProperties;
}
export function Zeitachse(props: ZeitachseProps): JSX.Element;

Zustände

Leer, laden, bestätigt. Ein leerer Zustand sagt, was hierher gehört, warum es leer ist und was man tun kann. Laden zeigt ein Skelett in der Form des Inhalts, nie einen Kreisel. Jede Änderung bekommt einen Toast mit „Rückgängig“.

LeererZustand

components/zustaende/LeererZustand.jsx

Der leere Zustand – drei Dinge und ein Knopf.

<LeererZustand mitAlbert
  was="Hier landet die Post der Schule."
  warum="Diese Woche kam nichts an. Sobald etwas kommt, macht Albert daraus einen Vorschlag."
  schritt="Post verbinden"
  randnotiz="Ich schaue täglich rein." />

Kein "Fehler", kein "Ups", kein Ausrufezeichen. Albert erscheint hier als Figur – in Listen, im Safe, bei Finanzen nie.

Typen (.d.ts)
/**
 * Der leere Zustand: was hierher gehoert, warum es leer ist, ein Knopf.
 */
export interface LeererZustandProps {
  /** Was hierher gehoert - ein Satz, kein Titel: "Hier landet die Post der Schule." */
  was: React.ReactNode;
  /** Warum es leer ist: "Diese Woche kam nichts an." */
  warum?: React.ReactNode;
  /** Was man tun kann, als Verb: "Post verbinden". */
  schritt?: string;
  onSchritt?: () => void;
  /** Albert als Figur - nur in Onboarding, leeren Zustaenden, Briefings, Kinderbereich, Jahresbuch. */
  mitAlbert?: boolean;
  /** Alberts Bleistift-Randnotiz in Reenie Beanie. Nur im Register Erzaehler. */
  randnotiz?: string;
  style?: React.CSSProperties;
}
export function LeererZustand(props: LeererZustandProps): JSX.Element;

Skelett

components/zustaende/Skelett.jsx
Form des Boards
Liste und Karte

Das Skelett – die Form dessen, was gleich kommt.

<Skelett form="board" spalten={7} zeilen={3} />

Nie ein Kreisel, nie ein Fortschrittsbalken. Es atmet zwischen Karte und Zweitknopf-Beige und steht still, wenn das System weniger Bewegung will.

Typen (.d.ts)
/** Skelett in der Form des Inhalts. Atmet 300 ms, steht bei reduzierter Bewegung, nie ein Kreisel. */
export interface SkelettProps {
  /** "karte" (eine Karte) | "zeile" (Liste) | "board" (Stundenraster). */
  form?: "karte" | "zeile" | "board";
  /** Zeilen je Spalte. */
  zeilen?: number;
  /** Spalten im Board. */
  spalten?: number;
  style?: React.CSSProperties;
}
export function Skelett(props: SkelettProps): JSX.Element;

Toast

components/zustaende/Toast.jsx
Tobias fährt am Freitag.
Schwimmen mit Lena steht am Montag.

Der Toast – eine Feststellung und ein Rückweg.

<Toast text="Tobias fährt am Samstag." onZurueck={zurueck} />

Albert stellt fest ("Fertig."), er lobt nicht. Ohne onZurueck gibt es keinen Toast – was nicht rücknehmbar ist, wird vorher gefragt.

Typen (.d.ts)
/** Kurze Rueckmeldung mit Rueckweg. Jedes optimistische Update bekommt einen. */
export interface ToastProps {
  /** Feststellung im Praesens, ohne Lob: "Tobias fährt am Samstag." */
  text: React.ReactNode;
  zurueck?: string;
  onZurueck?: () => void;
  style?: React.CSSProperties;
}
export function Toast(props: ToastProps): JSX.Element;

Kinder

Der Taler-Stand eines Kindes. Jedes Kind sieht nur den eigenen Stand. Es gibt keine Rangliste, keinen Vergleich unter Geschwistern und keinen Abzug.

TalerStand

components/kinder/TalerStand.jsx
LDein Stand
14Taler
Sparziel: Skateboard · 30 Taler
Das hast du geschafft
Tisch gedecktZimmer aufgeräumtSchwimmzeug gepackt
Regal der Eltern
Kino20Eis4

Der Taler-Stand für Kinder – Register Vorleser, kurze Sätze, grosse Wörter.

<TalerStand wer="lena" taler={14} ziel={30} zielName="Skateboard"
  geschafft={["Tisch gedeckt", "Zimmer aufgeräumt", "Schwimmzeug gepackt"]}
  regal={[{ name: "Kino", taler: 20 }, { name: "Eis", taler: 4 }]} />

Nicht verhandelbar: keine Rangliste, kein Geschwistervergleich, kein Abzug, kein roter Stand, kein Serien-Druck. Der Streifen zeigt das Sparziel – nie, wie viel jemand "aufholen" muss. Zielflächen 56 px.

Typen (.d.ts)
/**
 * Der Taler-Stand eines Kindes: grosse Zahl, Sparziel als Streifen, Regal der Eltern.
 */
export interface TalerStandProps {
  /** Das Kind - jedes Kind sieht nur den eigenen Stand. */
  wer?: string;
  /** Erreichte Taler. Nie negativ, es gibt keinen Abzug. */
  taler: number;
  /** Taler, die das Sparziel kostet. */
  ziel?: number;
  /** Name des Sparziels: "Skateboard". */
  zielName?: string;
  /** Was das Kind geschafft hat, als Pillen. Nie eine Quote. */
  geschafft?: string[];
  /** Das Regal der Eltern: was man sich holen kann und was es kostet. */
  regal?: Array<{ name: string; taler: number }>;
  style?: React.CSSProperties;
}
export function TalerStand(props: TalerStandProps): JSX.Element;

Scan

Der Weg vom Papier zum Vorschlag: Kamera mit Kantenrahmen, Seitenstapel, dann ein Vorschlag mit Absender, Betrag, Frist und Ablage. Nichts wird abgelegt, bevor ein Mensch „Übernehmen“ wählt.

ScannerFluss

components/scan/ScannerFluss.jsx
Kamera
Halte das Blatt in den Rahmen. Albert liest es gleich.
2 Seiten
Seitenstapel
123
Vorschlag
Rechnung Musikschule
Absender
Musikschule Nord
Betrag
48,00 €
Frist
18. September
Ablage
Wichtiges › Verträge
Vorschlag von Albert

Der Scanner – drei Schritte: Kamera, Stapel, Vorschlag.

<ScannerFluss schritt="vorschlag" seiten={2} vorschlag={{
  titel: "Rechnung Musikschule", absender: "Musikschule Nord",
  betrag: "48,00 €", frist: "18. September", ablage: "Wichtiges › Verträge"
}} />

Der Vorschlag läuft im Register Sachbearbeiter: nüchtern, vollständig, kein Schmuck, keine Formen. „Übernehmen“ und „Rückgängig“ stehen immer beide da — Albert legt nichts still ab.

Typen (.d.ts)
/**
 * Der Scanner-Fluss: Kamera mit Kantenrahmen, Seitenstapel, Vorschlagskarte.
 */
export interface ScannerVorschlag {
  /** Was Albert gelesen hat, als Gegenstand: "Rechnung Musikschule". */
  titel: string;
  absender?: string;
  /** Betrag deutsch: "48,00 €". */
  betrag?: string;
  /** Frist als Datum: "18. September". */
  frist?: string;
  /** Wohin es soll: "Wichtiges › Verträge". */
  ablage?: string;
}
export interface ScannerFlussProps {
  schritt?: "kamera" | "stapel" | "vorschlag";
  /** Zahl der aufgenommenen Seiten. */
  seiten?: number;
  vorschlag?: ScannerVorschlag;
  onAufnehmen?: () => void;
  onUebernehmen?: () => void;
  onZurueck?: () => void;
  style?: React.CSSProperties;
}
export function ScannerFluss(props: ScannerFlussProps): JSX.Element;

Website

Die Bauteile der Homepage, aus dem Homepage-Blueprint. Ein Ziel pro Bildschirm, ein Verb pro Knopf, ein Feld pro Formular. Die Website teilt Palette und Tokens mit der App. Einen eigenen Token-Satz für die Website gibt es nicht.

Akkordeon

components/website/Akkordeon.jsx

Nein. Er liest ihn ein und schreibt zurück.

Das Akkordeon für die Fragen.

<Akkordeon fragen={[{ frage: "Gibt es Albert für Android?", antwort: "Nein. iPhone, iPad, Mac. Gäste erhalten Einladungen per Link." }]} />

Sechs bis acht Fragen, in der Reihenfolge ihrer Häufigkeit. Antworten im Register Sachbearbeiter: erst die Antwort, dann ein Satz.

Typen (.d.ts)
/** Fragen und kurze Antworten, eine offen. */
export interface Frage { frage: string; antwort: React.ReactNode }
export interface AkkordeonProps {
  fragen: Frage[];
  /** Index der anfangs offenen Frage; -1 fuer keine. */
  offen?: number;
  style?: React.CSSProperties;
}
export function Akkordeon(props: AkkordeonProps): JSX.Element;

Anmerkung

components/website/Anmerkung.jsx
Gestrichelt in Rosa: noch niemand zuständig
Gestrichelt grau kommt von Albert

Die Anmerkung in Tinte – drei bis vier auf der Heldenansicht, absolut über dem Bild gesetzt.

<Anmerkung text="Gestrichelt in Rosa: alles, wofür noch niemand zuständig ist" richtung="links" style={{ position: "absolute", left: 620, top: 300 }} />

Handschrift (Reenie Beanie) ist hier erlaubt: Register Erzähler, keine Bedienung.

Typen (.d.ts)
/** Anmerkungslinie in Tinte, die auf ein Detail im Bild zeigt. */
export interface AnmerkungProps {
  text: string;
  /** Wohin der Zeiger geht. */
  richtung?: "links" | "rechts";
  /** Laenge der Linie in px. */
  laenge?: number;
  style?: React.CSSProperties;
}
export function Anmerkung(props: AnmerkungProps): JSX.Element;

Bildplatz

components/website/Bildplatz.jsx
Tuschezeichnung
TuschezeichnungTurnbeutel am Haken, ein gelber Marker
Foto · ohne Akzent
FotoDer Gründer am Küchentisch

Der Platzhalter für Zeichnung, Foto oder Screenshot auf der Website.

<Bildplatz was="Tuschezeichnung" motiv="Turnbeutel am Haken, ein gelber Marker" />
<Bildplatz was="Foto" motiv="Das gedruckte Jahresbuch, aufgeschlagen, Küchentisch von oben" verhaeltnis="16 / 10" akzent={false} />

Regeln fürs Bild: Tinte auf Papier, genau ein gelber Akzent, kein Stockfoto, kein 3D, kein KI-Bild, keine Kindergesichter. Genau ein echtes Foto auf der ganzen Seite – der Gründer.

Typen (.d.ts)
/** Platzhalter fuer eine Tuschezeichnung, ein Foto oder einen Screenshot - sagt, was hierher gehoert. */
export interface BildplatzProps {
  /** "Tuschezeichnung" | "Foto" | "Screenshot" */
  was?: string;
  /** Das Motiv in einem Satzteil: "Turnbeutel am Haken, gelber Marker". */
  motiv?: string;
  /** CSS aspect-ratio, z. B. "4 / 3", "1 / 1", "16 / 10". */
  verhaeltnis?: string;
  /** Der eine gelbe Markerakzent. Aus, wenn das Bild selbst einen traegt. */
  akzent?: boolean;
  children?: React.ReactNode;
  style?: React.CSSProperties;
}
export function Bildplatz(props: BildplatzProps): JSX.Element;

EmailZeile

components/website/EmailZeile.jsx
Tisch
Handy · gestapelt

Das eine Feld der Website.

<EmailZeile onSenden={email => merken(email)} />

Ein Feld: 12,4 % tragen sich ein; vier Felder: die Hälfte. Kein Name, kein Häkchen, kein zweites Feld.

Typen (.d.ts)
/** Ein E-Mail-Feld und ein Knopf - nie mehr Felder. */
export interface EmailZeileProps {
  platzhalter?: string;
  knopf?: string;
  onSenden?: (email: string) => void;
  /** Am Handy untereinander. */
  gestapelt?: boolean;
  style?: React.CSSProperties;
}
export function EmailZeile(props: EmailZeileProps): JSX.Element;

Fusszeile

components/website/Fusszeile.jsx

Die Fusszeile.

<Fusszeile />

Impressum, Datenschutz, Kontakt, Changelog, Alle Funktionen, DE/EN. Kein Newsletter-Formular, keine Social-Icons ohne Kanal.

Typen (.d.ts)
/** Die Fusszeile: nur Kanaele, die tatsaechlich bespielt werden. */
export interface FusszeileProps {
  links?: string[];
  sprache?: string;
  style?: React.CSSProperties;
}
export function Fusszeile(props: FusszeileProps): JSX.Element;

GrenzenListe

components/website/GrenzenListe.jsx
  • Albert liest keine Mail ohne Regel.Ihr legt fest, welche Absender er anfassen darf. Der Rest bleibt unangetastet.
  • Albert verschickt nichts allein.Jede Antwort ist ein Entwurf, den ein Erwachsener liest und sendet.
  • Die Putzhilfe sieht die Grundsteuer nicht.Fünf Rollen mit harten Grenzen – serverseitig, nicht nur ausgeblendet.
  • Der Safe wird auf deinem Gerät verschlüsselt.Ausweise und Zugangsdaten können wir nicht lesen. Wir wollen es auch nicht.
  • Eure Daten liegen in Frankfurt.Ihr könnt sie jederzeit mitnehmen oder löschen – in der App, ohne Anruf.

Die wichtigste Sektion der Website: Grenzen statt Versprechen.

<GrenzenListe />

Register Sachbearbeiter. Jeder Satz ist überprüfbar. Nirgends das Wort "KI", kein Siegel, kein "bankenähnlich".

Typen (.d.ts)
/** "Was Albert nicht tut" - fuenf ueberpruefbare Saetze mit Haarlinien-Zeichen. */
export interface Grenze { zeichen: string; satz: string; erklaerung: string }
export interface GrenzenListeProps {
  eintraege?: Grenze[];
  style?: React.CSSProperties;
}
export function GrenzenListe(props: GrenzenListeProps): JSX.Element;

ModulReihe

components/website/ModulReihe.jsx
ScreenshotEssensplan der Woche am iPhone
Der Plan, der die Einkaufsliste schreibtIhr legt die Essen der Woche fest. Albert setzt die Zutaten auf die Liste, Sofia kauft am Donnerstag ein.

Die Modul-Reihe – sechs auf der Homepage, nicht einundzwanzig.

<ModulReihe rollenname="Wer fährt, wen, womit, wann los" bildRechts
  text="Fahrzeit und Puffer rechnet Albert. Fahrten ohne Fahrer sind gestrichelt – Oma darf übernehmen."
  bild={<Bildplatz was="Screenshot" motiv="Fahrtenkarte mit Fahrer-Punkten" />} />

Rollenname statt Feature-Name. Höchstens 40 Wörter je Modul.

Typen (.d.ts)
/** Eine Modul-Reihe: Rollenname, zwei Saetze, Bild - abwechselnd links und rechts. */
export interface ModulReiheProps {
  /** Der Rollenname, kein Feature-Name: "Der Plan, der die Einkaufsliste schreibt". */
  rollenname: string;
  /** Zwei Saetze. */
  text: React.ReactNode;
  bild: React.ReactNode;
  bildRechts?: boolean;
  /** Am Handy untereinander, Bild oben. */
  gestapelt?: boolean;
  style?: React.CSSProperties;
}
export function ModulReihe(props: ModulReiheProps): JSX.Element;

PreisKarte

components/website/PreisKarte.jsx
Ein Preis für den ganzen Haushalt.
X,XX €im Monat · oder XX € im Jahr
  • Alle Geräte.
  • Alle Familienmitglieder, Gäste und Helfer inklusive.
  • Monatlich kündbar, in der App.
Wir sagen euch den Preis, bevor ihr etwas eingebt.

Die Preiskarte – eine, nicht drei.

<PreisKarte monat="X,XX €" jahr="XX €" hinweis="Wir sagen euch den Preis, bevor ihr etwas eingebt." />

Register Sachbearbeiter. Kein "ab", kein Sternchen, keine versteckte Stufe. Steht der Preis nicht fest, sagt die Karte genau das.

Typen (.d.ts)
/** Die eine Preiskarte. Ein Preis, alles drin. */
export interface PreisKarteProps {
  titel?: string;
  /** Monatspreis in Tabellenziffern: "6,99 €". Platzhalter "X,XX €", solange der Preis nicht feststeht. */
  monat?: string;
  jahr?: string;
  zeilen?: string[];
  knopf?: string;
  onKnopf?: () => void;
  /** "30 Tage kostenlos, ohne Kartendaten" - nur wenn es stimmt. */
  hinweis?: string;
  style?: React.CSSProperties;
}
export function PreisKarte(props: PreisKarteProps): JSX.Element;

Schritt

components/website/Schritt.jsx
Rückfahrt Turnier
Fr 18:40
2
SehenJede Person hat ihre Farbe, jeder Tag seine Spalte. Was ohne Zuständigen ist, trägt einen gestrichelten Rand.

Der Schritt – Nummer, Ausschnitt, ein Wort, zwei Sätze.

<Schritt nummer={2} titel="Sehen" text="Jede Person hat ihre Farbe, jeder Tag seine Spalte. Was ohne Zuständigen ist, trägt einen gestrichelten Rand – bis jemand es nimmt.">
  <Karte art="offen" titel="Rückfahrt Turnier" zeit="18:40" symbol="fahrt" />
</Schritt>

Drei Schritte sind die Grenze – vier werden nicht mehr gelesen.

Typen (.d.ts)
/** Ein Schritt in "So funktioniert es". Drei Schritte, nie vier. */
export interface SchrittProps {
  nummer: number | string;
  /** Ein Wort: "Sagen", "Sehen", "Sonntag". */
  titel: string;
  /** Zwei Saetze. */
  text: React.ReactNode;
  /** Der kleine Ausschnitt aus dem Produkt. */
  children?: React.ReactNode;
  style?: React.CSSProperties;
}
export function Schritt(props: SchrittProps): JSX.Element;

StickyKnopf

components/website/StickyKnopf.jsx
Am Handy · unten fixiert

Der fixierte Knopf am Handy.

<StickyKnopf sichtbar={heroWeg} onKnopf={zurListe} />

Erst einblenden, wenn der Hero aus dem Bild ist – sonst stehen zwei gleiche Knöpfe untereinander.

Typen (.d.ts)
/** Fixierter Knopf am unteren Rand - nur am Handy, ab Sektion 2. */
export interface StickyKnopfProps {
  knopf?: string;
  onKnopf?: () => void;
  sichtbar?: boolean;
  style?: React.CSSProperties;
}
export function StickyKnopf(props: StickyKnopfProps): JSX.Element | null;

SzeneKarte

components/website/SzeneKarte.jsx
TuschezeichnungTurnbeutel am Haken, ein gelber Marker
Der Turnbeutel, der zu Hause liegt – obwohl zwei Leute dran gedacht hatten.
TuschezeichnungUhr 18:40, leerer Fahrersitz
Freitag, 18:40: Rückfahrt vom Turnier. Fahrer: niemand.

Die Szenenkarte – "das sind wir", ohne Schuldgefühl.

<SzeneKarte motiv="Turnbeutel am Haken" satz="Der Turnbeutel, der zu Hause liegt – obwohl zwei Leute dran gedacht hatten." />

Register Erzähler. Kein "Bist du auch überfordert?" – konkrete Szenen lösen dieselbe Wiedererkennung aus, ohne zu kränken.

Typen (.d.ts)
/** Eine Alltagsszene: Zeichnung und ein konkreter Satz. */
export interface SzeneKarteProps {
  /** Motiv der Zeichnung, fuer den Platzhalter. */
  motiv?: string;
  /** Der Satz - mit Uhrzeit, Betrag oder Gegenstand: "Freitag, 18:40: Rückfahrt vom Turnier. Fahrer: niemand." */
  satz: React.ReactNode;
  /** Eigenes Bild statt Platzhalter. */
  bild?: React.ReactNode;
  style?: React.CSSProperties;
}
export function SzeneKarte(props: SzeneKarteProps): JSX.Element;

Vertrauenszeile

components/website/Vertrauenszeile.jsx

Für iPhone, iPad und MacDaten in FrankfurtAlbert liest keine Mail ohne Regel

Die kleine Zeile unter dem Knopf.

<Vertrauenszeile />

Drei Fakten, überprüfbar, ohne Siegel. Nie "100 % sicher", nie ein Schloss.

Typen (.d.ts)
/** Kleine Zeile unter dem Knopf: drei ueberpruefbare Fakten, mit Punkt getrennt. */
export interface VertrauenszeileProps {
  fakten?: string[];
  style?: React.CSSProperties;
}
export function Vertrauenszeile(props: VertrauenszeileProps): JSX.Element;

WebNavigation

components/website/WebNavigation.jsx

Die schlanke Navigation oben auf der Website.

<WebNavigation onKnopf={zurListe} />

Ein Knopf, nie zwei – drei oder mehr Knöpfe im Hero kosten. Keine Mega-Menüs. Der Knopf nennt die Folge, kein "Los geht's". Links: Funktionen · Vertrauen · Preis · Fragen.

Die Marke steht als schwarzer Kreis mit dem hellen Kritzelball und der Wortmarke in Bricolage – so bleibt sie auf Creme ruhig. Der Pfad zur Datei kommt aus data-albert-logo-auf-schwarz auf <html> (Vorgabe: assets/logo-albert-auf-schwarz.png).

Typen (.d.ts)
/** Die Navigation der Website: Logo, drei bis vier Links, ein Knopf. */
export interface WebNavigationProps {
  links?: string[];
  /** Der eine Knopf. Nennt die Folge: "Auf die Liste setzen", nach dem Start "Albert laden". */
  knopf?: string;
  onKnopf?: () => void;
  onLink?: (link: string) => void;
  /** Am Handy: nur Logo und Knopf. */
  geraet?: "desktop" | "phone";
  style?: React.CSSProperties;
}
export function WebNavigation(props: WebNavigationProps): JSX.Element;

HQ

Zwei Bauteile für Hey Albert HQ, das interne Werkzeug des Teams. Sie zeigen Familien und den Weg von der Liste bis zur Zahlung. In der App für Familien kommen sie nicht vor.

Satellit

components/hq/Satellit.jsx
Freunde und Helfer · 1Familie · 1Kern · 4FSASAnnaTSTobiasFSFelixLSLenaGSGreteSSofia

Die Kreise einer Familie – wer zum Kern gehört, wer zur Familie, wer Freund oder Helfer ist.

<Satellit name="Familie Sonntag" mitglieder={[
  { name: "Anna Sonntag", rolle: "owner", beziehung: "mother", farbe: 1 },
  { name: "Tobias Sonntag", rolle: "adult", beziehung: "father", farbe: 2 },
  { name: "Felix Sonntag", rolle: "child", beziehung: "son", farbe: 3 },
  { name: "Lena Sonntag", rolle: "child", beziehung: "daughter", farbe: 4 },
  { name: "Grete Sonntag", rolle: "guest", beziehung: "grandmother", farbe: 7 },
  { name: "Sofia", rolle: "service", beziehung: "au_pair", farbe: 5 }
]} />

Drei Ringe, gestrichelt nach aussen hin, der Kern mit fester Linie. Jede Person als Klebezettel in ihrer Personenfarbe mit Initialen, der Vorname darunter. In der Mitte die Initialen des Haushalts auf Tinte. Register Family Manager: Beschriftungen sind Wörter, keine Sätze – "Kern", "Familie", "Freunde und Helfer".

Typen (.d.ts)
/** Das Satellitenbild einer Familie: Kern, Familie, Freunde und Helfer als drei Ringe um den Haushalt. */
export interface SatellitMitglied {
  id?: string; name: string;
  /** Rolle im Haushalt: owner | adult | child | guest | service */
  rolle: "owner" | "adult" | "child" | "guest" | "service";
  /** Verwandtschaft (albert.kinship): mother, father, partner, daughter, son, grandmother, grandfather, aunt, uncle, au_pair, household_help, friend, other */
  beziehung?: string | null;
  /** Personenfarbe 1–8 (6 und 8 sind nicht vergeben). */
  farbe?: number | null;
}
export interface SatellitProps {
  mitglieder: SatellitMitglied[];
  /** Name des Haushalts, Initialen in der Mitte. */
  name?: string;
  /** Beschriftung der Ringe. */
  worte?: { kern: string; familie: string; freunde: string };
  style?: React.CSSProperties;
}
export function Satellit(props: SatellitProps): JSX.Element;
/** Welcher Ring: kern (owner, adult, child), familie (Gast mit Verwandtschaft), freunde (alles andere). */
export function kreisVon(m: SatellitMitglied): "kern" | "familie" | "freunde";

Trichter

components/hq/Trichter.jsx
eingetragen
120
bestätigt
9680 %
Umfrage beantwortet
4143 %
Trial
1844 %
zahlend
950 %

Der Trichter im HQ – wie viele Familien von Stufe zu Stufe bleiben.

<Trichter stufen={[
  { name: "eingetragen", n: 120 }, { name: "bestätigt", n: 96 }, { name: "Umfrage beantwortet", n: 41 },
  { name: "Trial", n: 18 }, { name: "zahlend", n: 9 }
]} />

Balken in Gelb (Fläche Familie), Zahl in Tabellenziffern, die Quote zur Stufe davor in Zweittext. Stufennamen klein geschrieben, wie Zustände – keine Sätze, kein Ausrufezeichen.

Typen (.d.ts)
/** Stufen von der Liste bis zur Zahlung als Balken mit Quote zur Stufe davor. */
export interface TrichterStufe { name: string; n: number }
export interface TrichterProps { stufen: TrichterStufe[]; style?: React.CSSProperties }
export function Trichter(props: TrichterProps): JSX.Element;

Web-Primitive

Für Formulare, Dialoge und Tabellen im Web gibt es zehn Primitive nach dem Vorbild von shadcn/ui. Sie liegen in bauteile/ui/ und laufen auf unseren Rollen: tailwind/shadcn.css bildet die Variablen von shadcn auf --grund, --karte, --tinte und die anderen Rollen ab. Größen sind Zielflächen: Zeiger 28, Finger 44, Kind 56.

  • badge
  • button
  • checkbox
  • dialog
  • input
  • select
  • sheet
  • table
  • tabs
  • textarea

Website-Blöcke

Die Website wird aus Blöcken gebaut, die Payload verwaltet. Zu jedem Block-Slug in packages/content-model/src/bloecke.ts gibt es genau ein Bauteil in bloecke/. RenderBloecke wählt nach blockType, filtert nach Markt und gibt Sprache und Markt weiter. pnpm test im Designsystem prüft, dass Slug und Bauteil zusammenpassen.

  • Aufruf
  • Bausteine
  • Bereiche
  • Bild
  • Eintragen
  • Faq
  • Hero
  • Hinweisleiste
  • Preise
  • Satz
  • Schritte
  • Text
  • Themen
  • Umfrage
  • Vergleich
  • Zitat

Ein neues Bauteil beitragen

Kein Bauteil entsteht nur in einer App. Was in apps/web, in AlbertUI oder auf der Website neu gebaut wird, kommt zuerst hierher oder spätestens im selben Commit.

  1. Prüfen, ob es das Bauteil schon gibt. Oft reicht eine neue Art eines bestehenden Bauteils.
  2. Im passenden Ordner unter components/ vier Dateien anlegen: .jsx, .d.ts, .prompt.md und eine @dsCard-Karte mit allen Zuständen.
  3. Nur Rollen-Tokens verwenden. Fehlt eine Rolle, kommt sie in tokens/quelle/ dazu, und pnpm tokens erzeugt CSS, Tailwind und Swift neu.
  4. Den Eintrag in _ds_manifest.json und die Liste im readme.md ergänzen.
  5. pnpm qa laufen lassen: kein Hex-Wert, kein Schatten, kein verbotenes Wort, kein Ausrufezeichen, kein Bild ohne alt, kein freier Radius.
  6. Den Agenten design-pruefer die Definition of Done prüfen lassen.