Files
posts/komponenten.md
T
2026-10-20 18:26:21 +02:00

17 KiB
Raw Blame History

Produkt-Komponenten für Posts

Stand: 20.10.2026. Quelle: hivegrid-cloud, Stand origin/main vom 19.10.2026. Referenz-Umsetzung: post8.html, Abschnitt „Post 8 · E-Mail-Marketing“.

Diese Datei ist das Nachschlagewerk für jede Zeichnung, die in einem Post die Software zeigt. Die Regel dazu steht in rules.md §0 und gilt ohne Ausnahme.


1. Die Regel

Jede Oberfläche in einem Post wird aus den echten Komponenten von hivegrid-cloud nachgebaut, mit ihren Maßen, Farben, Schriften, Icons und Beschriftungen. Keine eigenen, ausgedachten Grafiken.

  • Jede Zeichnung bildet einen Screen nach, den es im Produkt gibt. Im Kommentar steht die Datei, aus der er stammt.
  • Beschriftungen stehen wörtlich in apps/web/public/locales/de/translations.json.
  • Was im Produkt nicht existiert, wird nicht gezeichnet. Auch nicht, wenn es gut aussähe.
  • Was nicht auf die Slide passt, wird weggelassen, nicht umgebaut. Im Kommentar steht, was fehlt und warum.
  • Erfunden werden darf nur Demo-Inhalt: Namen, Betreffzeilen, Texte, Daten, Zahlen. Die Zahlen müssen aufgehen.

Warum: Die erste Fassung von Post 8 hatte ein eigenes Design (Pillen mit Rand, Häkchen-Chips, Reiter und Kacheln, die es im Produkt nicht gibt). Dejan: „sieht total vibecoded aus“. Nach dem Umbau auf die Komponenten: „sieht jetzt genau so aus wie die Software“ (20.10.2026).


2. So gehst du vor

  1. Screens finden. Für das Thema des Posts das Modul in hivegrid-cloud suchen: apps/web/app/components/screens/<modul>/, apps/web/app/modules/<modul>/, die Routen unter apps/web/app/routes/app.$tenant/, die Texte in translations.json.
  2. Immer origin/main lesen, nie die Arbeitskopie. Die lokale Kopie von hivegrid-cloud ist veraltet und wird nicht angefasst (kein pull, kein checkout ohne Dejan).
    • Einzelne Datei: git -C ../hivegrid-cloud show origin/main:<pfad>
    • Für viele Dateien ein Schnappschuss ins Scratchpad: git -C ../hivegrid-cloud archive origin/main apps/web/app apps/web/public/locales/de | tar -x -C <scratchpad>/hg-main
    • Vorher git -C ../hivegrid-cloud fetch (holt nur, ändert nichts).
  3. Für jede Slide einen echten Screen wählen. Aufbau von oben nach unten aus dem Code lesen: Kopf, Knöpfe, Tabs, Felder, Tabellenspalten, welche Badge für welchen Status.
  4. Mit den Bausteinen zeichnen (Abschnitt 3). Sie stehen in post8.html und werden in den neuen Post kopiert, weil jeder Post eine einzelne Datei ist.
  5. Prüfen:
    • Streifen-Ansicht ?nur=streifen: Die Karte darf nicht in den Text der Slide ragen.
    • PNG-Export: Die Oberfläche muss in Instrument Sans stehen, nicht in einer Ersatzschrift (siehe Fallen).
    • MP4 mit node animation.mjs postN.html <key>.

3. Bausteine in post8.html

Alle Werte sind Produkt-Pixel und werden mit PZ = 1.15 vergrößert, wie ein Browserzoom. Koordinaten sind Pixel der Zeichnung. Nur Schriften, Icons und Komponentenhöhen laufen über pz().

Baustein Bildet nach Aufruf
P, P_TON, P_KNOPF, P_KARTE Rollen aus ui/theme.css Farbkonstanten
pText(x, y, t, gr, gew, farbe, o) Text in Instrument Sans o.anker für Ausrichtung
pMess(t, gr, gew) Textbreite in Instrument Sans
pMitte(ym, gr) Grundlinie für Text mittig in einem Kasten
pIkon(name, cx, cy, gr, farbe) Feather/Lucide-Icon, Strich 2 auf 24 Namen aus LUCIDE
pKarte(x, y, b, h, art, radius) Card, InlineMessage art: std, weiss, neutral, info, warnung
pBadge(x, ym, t, ton, klein) BadgeFeedback subtle ton: positiv, warnung, info, neutral
pKnopf(x, y, t, art, o) Button art: primaer, sekundaer, tertiaer, geist, aus; o.ikon, o.ikonRechts, o.klein
pFeld(x, y, b, o) TextInput, Select, TextArea o.label, o.wert, o.platzhalter, o.auswahl, o.hoch, o.hilfe
pTabs(x, y, b, reiter, aktiv, innen) Tabs
pUmschalter(xRechts, y, eintraege, aktiv) ToggleGroup klein Einträge [icon, text]
pTabelle(x, y, spalten, zeilen, zh, zelle) Table Spalten [titel, icon, breite]
pRahmen(h, inhalt, kopf) Seite mit PageHeader kopf.ikon, kopf.titel, kopf.badge, kopf.ikonKasten, kopf.rechts
pKopfKnopf(t, art, o) Knopf rechts im Kopf
emKpi(...) WidgetRahmen mit KPICard

Icons: LUCIDE enthält die Formen aus lucide-static@0.460.0. Subframes Feather*-Icons haben dieselben Formen. Fehlt ein Icon, die SVG von https://cdn.jsdelivr.net/npm/lucide-static@0.460.0/icons/<name>.svg holen und das Innere der <svg> in die Tabelle eintragen. Phosphor (ikon()) gehört nur noch in ältere Posts, nicht in Oberflächen.

Schrift: Die Dateien liegen in assets/fonts/instrument-sans-*.woff2, dieselben wie in hivegrid-cloud/apps/web/public/fonts/. Der Post braucht dafür drei Dinge:

  1. den @font-face-Block im <head>
  2. beim Start document.fonts.load(...) für 400, 500 und 600, bevor zum ersten Mal gemessen wird
  3. instrumentSansCss(), angehängt an fontEmbedCss, sonst fehlt die Schrift im PNG

Alles drei steht in post8.html.


4. Tokens, aufgelöst

Schrift

Instrument Sans, variabel 400 bis 700. Laufweite überall 0.

Token px / Zeilenhöhe / Gewicht Wofür
subtitle 32/40/600 Seitentitel im PageHeader, Wert in StatsCard
heading- 24/32/500 Dialogtitel
body-large 20/24 (‑0: 26)/500 Abschnitte wie „E-Mail gestalten“, EmptyState-Titel
body-default-regular 16/24 (‑0: 22)/400 Knopfbeschriftung, Eingaben, Fließtext
body-default-medium 16/24/500 Feldbeschriftungen
body-default-semibold 16/24/600 Kartentitel
body-small-regular 14/21 (‑0: 18)/400 Hilfetexte, Tabs, Seitenleiste
body-small-medium 14/21/500 Badges, Menüeinträge, Checkbox-Beschriftung
body-extra-small 12/18 (‑0: 16)/400 bis 600 Tabellenkopf (500), Tooltip

text-heading-2 und text-heading-3 werden im Code benutzt, sind aber nirgends definiert. Sie rendern als 16/24/400.

Farben

Neutral: 0 #FAFAFA · 50 #F5F4F4 · 100 #F0F0F0 · 200 #E5E5E5 · 300 #CECECF · 400 #B5B5B7 · 500 #9D9DA0 · 600 #858589 · 700 #6D6D71 · 800 #505057 · 850 #37373E · 900 #24242B · 950 #111114

Rolle Wert
Seite (surface-neutral-primary) #F5F4F4
Karte, Kopfzeile, Tabellenzeile (secondary) #FAFAFA
Tabellenkopf, Hover (tertiary) #F0F0F0
Rand, Rand stark #E5E5E5, #B5B5B7 (Eingabefelder)
Text primär, sekundär, tertiär #24242B, #505057, #6D6D71
Platzhalter, deaktiviert #CECECF
Link #08739C
Icons primär, sekundär, tertiär #24242B, #505057, #858589
Akzent für aktiv und ausgewählt #E1F4FD (information subtle)
Sekundärknopf #E3F1FF
Ton 100 200 500 600 700
Erfolg (grün) #DCFCE4 #BAF8CB #20C54F #15A43E #148134
Warnung (gelb, nicht Marke) #FCF6C5 #FAEB8E #E1AB11 #C2840C #9B5E0D
Fehler (rot) #FEE2E2 #FDCBCB #ED4646 #DA2828 #C52020
Information (ocean) #E1F4FD #BCEAFB #15B1E2 #0890C1 #08739C

Kein Markengelb in der Oberfläche. Die Rolle „brand“ ist im Produkt auf Grau gemappt, Primärknöpfe sind #24242B. Gelb gehört auf der Slide nur zur Überschrift und zu Marken-Elementen außerhalb der Oberfläche.

Radien und Schatten

  • rounded-sm 4 px (Eingabefelder) · rounded-md 6 px (Knöpfe, Karten, Tabs) · rounded-lg 8 px (Widgets) · rounded-xl 12 px. In theme.css steht md = 8, globals.css überschreibt das danach mit 6.
  • Schatten im Produkt: sm 0 1px 2px rgb(0 0 0 / .08), md und lg größer. In den Zeichnungen lassen wir sie weg (rules.md §5). Der Stapel um die Karte hat seinen eigenen Schatten und bleibt.

5. Komponenten

Komponente Maße und Farben
Button klein h32, px12, Abstand 8 · mittel h36, px16, Abstand 12 · groß h48. Radius md. Beschriftung 16/400, Icon 16. primär #24242B/#FAFAFA · sekundär #E3F1FF/#24242B · tertiär #F5F4F4 mit Rand #E5E5E5 · ghost transparent · information #0890C1 · destructive #DA2828 · deaktiviert #F0F0F0/#CECECF
IconButton rund, quadratisch mit square (md). 24/32/36/48 Kasten, Icon 16/20/20/24
BadgeFeedback (alle Status) Pille ohne Rand, h24 (klein 20), px10, Abstand 8, Punkt 4 px, Schrift 14/500. positiv #DCFCE4, Text und Punkt #148134 · Warnung #FCF6C5, Text #24242B, Punkt #9B5E0D · information #E1F4FD, Text #24242B, Punkt #08739C · neutral #F0F0F0, Text und Punkt #24242B · negativ #FEE2E2, #C52020
Badge (Farbtöne) Pille h24 mit 1-px-Rand, sonst wie oben. Standard #F0F0F0, Rand #E5E5E5
Tabs Reiter px16, py10 (rund 40 hoch), oben rund, #FAFAFA, unten 2 px #E5E5E5, Schrift 14/400 #505057. Aktiv: Fläche #E1F4FD, unten 2 px #24242B, Schrift 14/500 #24242B
TextInput / Select h36, px12, Radius 4, Rand #B5B5B7, Fläche #F5F4F4. Beschriftung 16/500 #505057, 6 px darüber. Wert 16 #505057, Platzhalter #CECECF, Hilfe 14 #6D6D71. Pfeil 16 #858589
TextArea wie oben, mindestens 96 hoch, Innenabstand 12×8
Checkbox 16, Radius 4, Rand 2 px #B5B5B7. An: #24242B mit Haken #FAFAFA. Beschriftung 14/500
Switch 36×22, rund. Aus: #F5F4F4 mit dunklem Knopf. An: #24242B mit hellem Knopf
Table Außenrand #E5E5E5. Kopf h32, #F0F0F0, Schrift 12/500 #505057, Icon 12 #858589. Zeilen #FAFAFA mit Trennlinien #E5E5E5, Höhe aus dem Inhalt (rund 31)
Avatar rund, 24/32/40/48/64. Ohne Bild #F0F0F0, Kürzel 16/500 #505057
IconWithBackground Radius 4. Kasten 22/24/28/32/40, Icon 14/16/20/24/32. information #E1F4FD mit #08739C
Card Radius md, Rand #E5E5E5, Fläche #FAFAFA, Innenabstand 16 (lg 24). Varianten information, warning, negative, positive, neutral
InlineMessage Card in Ton-Variante, px16, py12, Text 16 #24242B. Standard information
Alert Radius md, Rand, px16, py20, Icon 24. Titel 16/500 im Ton, Text 16 #505057
Widget (WidgetRahmen) Radius lg, Rand, weiß. Kopf h44 mit Titel 13/600. KPI: Wert halbfett, Fußzeile 11 #6D6D71 über einer Linie
ToggleGroup h36 (klein 32), Innenabstand 2, Rand, #F0F0F0. Aktiv: #24242B mit Schrift #FAFAFA
DropdownMenu #FAFAFA, Rand, Radius md, Innenabstand 4. Einträge 14/500 #6D6D71, Hover #F0F0F0
Tooltip #505057, Schrift 12/500 #FAFAFA, Radius 4
Progress 6 px, rund, Spur #F0F0F0, Füllung #24242B
EmptyState Titel 20/500, Text 16 #505057. Umrandet: Rand und #F5F4F4
Dialog weiß, Rand, Radius md, Overlay rgb(0 0 0 / .4). DecisionDialog 420 oder 560 breit, Titel 24/500

App-Rahmen

  • Seite: Hintergrund #F5F4F4. Kopfleiste h64, #FAFAFA, Linie unten.
  • PageHeader: #FAFAFA, px24, py20, Linie unten. Icon 24 in #858589, Abstand 8, Titel 32/600 #24242B. Knöpfe rechts mit Abstand 8. In den Zeichnungen Titel 24, weil die Karte schmaler ist als ein Bildschirm.
  • Filterleiste: #F5F4F4, px24, py10. Suchfeld #FAFAFA, Facetten-Chips h24 mit Rand #B5B5B7. Aktiv #BCEAFB.
  • Seitenleiste: 240 breit, #FAFAFA. Einträge h40, Icon 20 mit Strich 1.5, Schrift 14. Gewählt: #E1F4FD.

6. Fallen

  1. Schrift im PNG. html-to-image bettet nur die Schrift ein, die im HTML vorkommt, also Manrope. Instrument Sans steckt nur im SVG und fehlte im Export. Lösung: instrumentSansCss() an fontEmbedCss hängen. Prüfen, indem man Export und Vorschau übereinanderlegt.
  2. Messen vor dem Laden. pMess() speichert Breiten zwischen. Wird vor dem Laden der Schrift gemessen, bleibt die Breite der Ersatzschrift hängen. Darum document.fonts.load am Anfang von boot().
  3. Was in den Übersetzungen steht, ist nicht automatisch zu sehen. Beispiel: „Pre-Send-Prüfung“ und die sechs Prüfpunkte stehen in translations.json, angezeigt werden sie nirgends. Immer im Screen-Code nachsehen, ob ein Text wirklich gerendert wird.
  4. Website und Produkt unterscheiden. Die Website zeigt manches, was es im Produkt nicht gibt (in Post 8 die Produktkacheln im Mail-Baukasten). Maßgeblich ist das Produkt.
  5. Höhe der Karte. Zu hohe Karten ragen in den Text der Slide. Lieber Schritte oder Hilfetexte weglassen als die Komponenten zu verkleinern.
  6. Feste Höhe im Bewegtbild. Die Höhe der Zeichnung darf nicht vom Zeitpunkt abhängen, sonst ändert sich der viewBox und alles springt.

7. Beispiel: E-Mail-Marketing (Post 8)

Slide Screen Datei in apps/web/app/components/screens/email-marketing/
Bewegtbild Kampagnenbericht EmailMarketingDetailScreens.tsx
Zielgruppe Dialog „Zielgruppe bearbeiten“ SegmentEditorDialog.tsx, SegmentFilterBuilder.tsx
Vorlagen Vorlagenseite EmailMarketingTemplateScreens.tsx
Rechtssicher „Empfänger & Versand“ mit „Bereit zum Versand?“ EmailMarketingCampaignEditorPage.tsx
Baukasten Gestaltung CampaignContentBuilder.tsx, CampaignBuilderSidebar.tsx, campaign-mail-look.ts

Status-Badges dort: „Gesendet“ positiv · „Geplant“, „Pausiert“ Warnung · „Wird gesendet“ information · „Entwurf“, „Archiviert“ neutral. Empfänger: „Zugestellt“, „Geöffnet“, „Geklickt“ positiv · „Ausstehend“ Warnung · „Gebounced“ negativ. Vorlagen: „Standard“ information · „Eigen“ neutral. Versandbereitschaft: „Erledigt“ positiv · „Offen“ neutral.


8. Beispiel: Form Builder (Post 7)

Seit 20.10.2026 nach dem echten Editor gebaut. Vorher kam Post 7 aus dem Klickdummy des Pitch-Pakets (S02EinfacherFragenEditor.tsx) und zeigte Dinge, die es nicht gibt.

Slide Screen Datei in apps/web/app/components/screens/forms/
Bewegtbild Reiter „Fragen“, neue Frage klappt auf editor/questions-tab/QuestionsTabView.tsx, SortableQuestionCard.tsx
Die Fragen zugeklappte Fragekarten, Werkzeugleiste SortableQuestionCard.tsx:121, QuestionsTabView.tsx
Bedingt sichtbar offene Frage mit „Nur anzeigen, wenn“ editor/questions-tab/QuestionEditors.tsx:123
Lead Ansicht „Felder zuweisen“ editor/LeadBanner.tsx, editor/LeadMappingView.tsx
  • Kopf (FormEditorPage.tsx:411): kein PageHeader, eine eigene Zeile mit clipboard-list, Name 20/500, Badge „Entwurf“ (neutral), „Veröffentlicht“ (positiv) oder „Änderungen nicht live“ (Warnung). Rechts klein: „Einbetten & QR“ (sekundär, nur extern), „Vorschau“ (tertiär), „Veröffentlichen“ (primär), ⋯. Autosave „Automatisch gespeichert“ steht dort und blendet nach 3 s aus.
  • Reiter: „Fragen“ (list-checks), „Design“ (palette), „Antworten“ (inbox), gleich breit.
  • Fragekarte zu: Radius 8, px20 py16, Griff, Typ-Icon, Titel 16/500 mit rotem * bei Pflicht, darunter der Typ und bei optionalen „ · optional“. Kopieren grau, Löschen rot #C52020. Bedingte Fragen: Badge „Bedingt sichtbar“ klein neutral.
  • Fragekarte offen: kein farbiger Rand, sondern 6-px-Streifen links in #0791F0. Optionen nur hier.
  • Feldtypen: Text, Mehrzeiliger Text, Auswahl, Mehrfachauswahl, Checkbox, E-Mail, Telefon, Zahl, Datum, Uhrzeit, Datum und Uhrzeit, Datei (paperclip), Foto (Kamera, nur intern), Unterschrift. Nicht: „Kurzantwort“, „Absatz“, „Multiple-Choice-Frage“.
  • „Frage hinzufügen“: gestrichelte Fläche, 2 px #B5B5B7, Icon 24, Schrift 20/500. Neue Frage: Typ „Text“, Name „Unbenannte Frage“, neue Optionen „Auswahl A“, „Auswahl B“.
  • Lead: Banner „Lead-Verknüpfung“ mit „Aktiv“, Text „Neue Antworten werden als Lead gespeichert.“, Knöpfe „Felder zuweisen“ (primär) und „Deaktivieren“ (tertiär). Eine Pipeline gibt es im Form Builder nicht.
  • Gibt es nicht: eine Fußzeile, „Zuletzt gespeichert vor wenigen Sekunden“, „Bereit zum Veröffentlichen“.

Bausteine dazu in post7.html: fbRahmen, fbFrage, fbLeiste, fbNeu, fbFeld, fbOffenKopf, fbOffenKarte, fbOption, fbOptionNeu, fbSchalter, pKurz (Text mit „…“ kürzen).

Alle Grafiken eines Posts bekommen dieselbe SVG-Höhe, damit die Karten auf allen Slides oben auf einer Linie stehen. Eine kürzere Karte lässt deshalb unten Platz frei. Das ist gewollt. Zu hoch darf keine werden: Sie ragt sonst in den Text. Das Bewegtbild ohne Fließtext hat ein eigenes visBottom.