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.
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 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;
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.
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.
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.
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 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.
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.“
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.
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;
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.
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“.
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 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