Zum Inhalt
Hey AlbertMarke und Designsystem
Als MarkdownEnglishEN

Screens

Zustände

Leer, lädt, Fehler, offline, Rückgängig, optimistisches Update, Konflikt, ohne Zuständigen, Vorschlag von Albert, inaktiv – jeweils Aufbau, Register, Beispieltext mit Familie Sonntag und das Bauteil dazu.

Ein Screen hat mehr als einen Zustand. Ohne seine Zustände ist er nicht fertig. Dieses Kapitel beschreibt jeden einzeln: wie er aussieht, in welchem Register er spricht und welches Bauteil ihn trägt.

ZustandBauteilRegister
leerLeererZustandFamily Manager, mit Albert als Figur
lädtSkelett–
FehlerLeererZustand ohne FigurFamily Manager oder Sachbearbeiter
offlineHinweiszeile (Vorschlag)Family Manager
RückgängigToastFamily Manager
optimistisches UpdateKarte sofort, Toast danachFamily Manager
KonfliktKonfliktKarte, BuendelKarteFamily Manager
ohne ZuständigenKarte mit Offen-RandFamily Manager
Vorschlag von AlbertVorschlagKarte, Karte art="vorschlag"Family Manager, Sachbearbeiter im Scan
inaktiveigene Rollen statt Deckkraft–

Leer

Ein leerer Zustand sagt drei Dinge: was hierher gehört, warum es leer ist, was man tun kann. Dazu genau ein Knopf. Albert darf hier als Figur erscheinen, mit einer Randnotiz in Handschrift. In Listen, im Safe und bei Finanzen nie.

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;

Lädt

Nie ein Kreisel, nie ein Fortschrittsbalken als Schmuck. Das Skelett hat die Form dessen, was gleich kommt: sieben Spalten für die Woche, drei Karten für Heute. Es atmet zwischen --karte und --knopf-zweit in --dauer-atem. Bei reduzierter Bewegung steht es still.

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;

Das Skelett erscheint erst, wenn das Laden länger als einen kurzen Moment dauert. Vorschlag: ab 300 ms, damit es bei schnellen Antworten nicht flackert.

Fehler

Ein Fehler ist ein leerer Zustand mit anderem Warum. Kein „Fehler“, kein „Ups“, kein Rot, kein Ausrufezeichen. Er sagt, was nicht ging, und gibt einen Schritt. Albert erscheint bei Fehlern nicht als Figur.

Fehler in Safe, Tresor und Finanzen sprechen im Register Sachbearbeiter: nüchtern, vollständig, mit dem, was gespeichert ist und was nicht.

Offline

Rückgängig

Jede Änderung meldet sich mit einem Toast und „Rückgängig“. Albert stellt fest, er lobt nicht. Ohne Rückweg gibt es keinen Toast. Was sich nicht zurücknehmen lässt, wird vorher gefragt.

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;

Der Toast steht über der Leiste am iPhone und unten in der Mitte am Mac. Er bleibt, bis man weiterarbeitet. Vorschlag: mindestens fünf Sekunden, damit auch Oma Grete ihn lesen kann.

Optimistisches Update

Wer etwas ändert, sieht das Ergebnis sofort. Die App wartet nicht auf den Server. Geht es schief, kommt der alte Stand zurück, und ein Toast sagt, was passiert ist. Ändert ein anderer Mensch etwas, steht dabei, wer es war: „Anna hat die Fahrt übernommen.“

Konflikt

Zwei Sachen treffen sich. Die Karte zeigt es und hält niemanden auf. Kein Rot, kein Fehler. Die Karte stellt fest und nennt einen Schritt.

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;

Laufen drei Sachen zur selben Zeit, bündelt eine Karte sie in drei Zeilen, statt drei Karten zu stapeln.

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;

Ohne Zuständigen

Alles hat genau einen Zuständigen. Etwas ohne Zuständigen ist kein neutraler Zustand, sondern ein Problem und wird laut gezeigt: gestrichelter Rand in Offen-Rosa, --rand-offen, und die Zeile „Noch niemand“. Nie als Fläche, nie still leer. Einzige Ausnahme ist die Aufgaben-Inbox.

Vorschlag von Albert

Albert schlägt vor und handelt nie allein. Ein Vorschlag ist eine Beobachtung, eine Frage, zwei Knöpfe. Am iPhone gelb mit Formen, am Mac gestrichelt grau ohne Füllung. Darunter immer „Vorschlag von Albert“.

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;

Im Eingang spricht der Vorschlag Sachbearbeiter: Absender, Betrag, Frist, Ablage, dazu „Übernehmen“ und „Rückgängig“.

Inaktiv

Inaktiv ist eine eigene, gerechnete Rolle, keine Deckkraft: --text-inaktiv, --linie-inaktiv, --knopf-inaktiv-flaeche, --knopf-inaktiv-text. Ein inaktiver Knopf bleibt lesbar und sagt, warum er nicht geht.

#E9E2D4#2B2620
#A29B8F#7A7469→ text-inaktiv
#A29B8F#7A7469gerechnet, keine Deckkraft
#E4DDCE#2B2620gerechnet, keine Deckkraft