Wie Bauteile zusammen Antworten geben – Bündel, Vorschlag, Konflikt, ohne Zuständigen, Spuren, Formulare, Listen, Scan, Kinder, Seitenblatt, Bestätigen und Löschen.
Ein Muster ist eine wiederkehrende Frage mit einer festen Antwort. Wie zeigt Albert einen Vorschlag? Was passiert, wenn zwei Sachen gleichzeitig sind? Die Antwort steht hier einmal und gilt dann auf jedem Gerät. Jedes Muster nennt seine Bauteile, seine Regel und ein Beispiel aus der Woche von Familie Sonntag.
Eine Sache kommt selten allein. Eine Fahrt zieht Auto, Fahrer, Mitfahrer und Checkliste mit. Das ist Leitsatz drei: Bündel statt Einzelteile. Wer die Fahrt sieht, sieht das Bündel. Wer die Fahrt verschiebt, verschiebt das Bündel mit.
Wenn mehrere Sachen zur selben Stunde fallen, fasst die Bündelkarte sie zusammen. Jede Sache bleibt eine eigene Zeile mit eigenem Zuständigen.
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;
Albert schlägt vor und handelt nie allein. Jeder Vorschlag hat vier Teile: eine Vorschau, „Übernehmen“, einen zweiten Weg und einen Eintrag im Protokoll. Er ist als Vorschlag erkennbar, auf dem Mac gestrichelt grau, am iPhone als gelbe Karte. Er nennt seine Herkunft.
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;
Die Regeln:
Immer zwei Knöpfe. Der erste ist ein Verb, das die Folge nennt: „Tobias fragen“, „Übernehmen“. Der zweite ist der andere Weg: „Später“.
Immer mit Herkunft. „Aus der Mail der Klassenlehrerin: Unterschrift und 8 € bis Freitag. Als zwei Aufgaben übernehmen?“
Nach dem Übernehmen ein Toast mit „Rückgängig“. Die Änderung erscheint sofort. Der Rückweg bleibt offen.
Jede Aktion von Albert steht im Protokoll. Wer, was, wann, aus welcher Quelle.
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;
Alles hat genau einen Zuständigen. Fehlt er, ist das kein neutraler Zustand, sondern ein Problem, und es wird laut gezeigt: gestrichelter Rand in Offen-Rosa, eine Pille „Ohne Zuständigen“, ein Knopf, mit dem jemand übernimmt. Albert benennt die Lücke und bietet einen Weg an.
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;
Die einzige Ausnahme ist die Aufgaben-Inbox. Dort darf etwas kurz ohne Zuständigen liegen, bis es verteilt ist. Im Wochenüberblick zählt Offenes oben mit, am iPhone bekommt der Tag mit Offenem einen rosa Kreis im Tagesreiter.
Gleichzeitiges hat drei Antworten. Die Bündelkarte fasst zusammen, die Konfliktkarte benennt den Zusammenstoß. Die Spuren zeigen je Mensch eine Zeile über die Stunden. So sieht man auf einen Blick, wer wann frei ist.
Knöpfe stehen nebeneinander, wenn beide Platz haben. Sonst stehen sie untereinander, in voller Breite. Ein Knopf ist immer einzeilig. Zwei ungleich breite Knöpfe untereinander gibt es nicht.
Ein Formular fragt nur, was es wirklich braucht. Auf der Website gilt: ein Feld pro Formular, ein Verb pro Knopf. Jedes Feld über eins kostet ungefähr die Hälfte der Einträge. Darum hat die Liste genau ein Feld, die E-Mail-Adresse.
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;
In der App füllt Albert vor, was er weiß, und markiert es als Vorschlag. Ein Mensch bestätigt. Pflichtfelder, die leer sind, machen den Knopf nicht durchscheinend, sondern inaktiv als eigene Rolle. Fehler im Feld nennen, was fehlt und was richtig wäre: „Die Uhrzeit fehlt. Zum Beispiel 16:40.“
Die Woche als Liste ist die Antwort am iPhone, wenn das Raster zu eng wird. Jeder Tag ist eine Zeile mit Tag-Pille, einem Satzteil und der verplanten Zeit. Heute ist schwarz. Ein Tag klappt auf und zeigt seine Karten in der kompakten Form.
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;
Regeln für Listen:
Eine Zeile sagt in einem Satzteil, was der Tag trägt: „heute · Anna und Felix“.
Zahlen stehen rechts, in Tabellenziffern: „3,0 h“.
Vergangenes ist zurückgenommen, aber lesbar: „vorbei · zum Aufklappen“.
Ist die Liste leer, zeigt sie einen leeren Zustand, keine leere Fläche.
Papier wird zum Vorschlag in drei Schritten: Kamera, Seitenstapel, Vorschlag. Erkannt wird auf dem Gerät. An ein Modell geht nur der erkannte Text, nie das Bild. Der Vorschlag nennt Absender, Betrag, Frist und Ablage. Erst „Übernehmen“ legt ab.
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;
Kinder sind Beteiligte, nicht Publikum. Die Prüffrage für jede Kinderansicht: Könnte eine Neunjährige sie allein benutzen? Kinderansichten nutzen die Zielfläche 56 px, 16 px Schrift auf Knöpfen und das Register Vorleser.
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;
Die Regeln für Taler:
Jedes Kind sieht nur den eigenen Stand.
Keine Rangliste, kein Vergleich unter Geschwistern, kein Abzug, kein Druck durch Serien.
Sichtbar ist, was geschafft ist, nie eine Quote oder ein Aufholbalken.
Die Werte legen die Eltern fest. Albert stellt fest, loben tun Menschen: „Fertig. Bleibt noch: Zimmer aufräumen.“
Zusatzinformationen öffnen sich im Seitenblatt von rechts, statt auf eine neue Seite zu springen. Die Fläche dahinter bleibt sichtbar und wird vom Schleier gedämpft (--flaeche-schleier). So geht der Blick nicht verloren. Im HQ öffnen Kennzahl-Kacheln, Kontakte und Mails ihr Seitenblatt. Für das Web gibt es dafür das Primitiv Sheet in bauteile/ui/.
Jede Änderung erscheint sofort, also optimistisch. Danach kommt ein Toast. Er stellt fest, im Präsens oder in der Vergangenheitsform desselben Verbs, und bietet „Rückgängig“ an. Er lobt nicht.
Löschen ist das eine, was „Rückgängig“ nicht auffängt. Darum fragt Albert einmal nach und nennt die Folge ohne Drama. Gelöscht wird an einer Stelle, mit Bestätigung im Dialog. Im HQ geht das auf der Eintragsseite und in der Sammelleiste, nie mit einem Klick in der Zeile.