Spezifikations-Mockups
Dieses Skill beschreibt, wie gezeichnet wird. lw-spec-write sagt, wohin die Spezifikation gehört und wie die Seite rendert.
Produktfakten
Der Adapterordner stammt aus der Konfiguration. Lies ihn zuerst:
ah config get harness.adapterDir
Dann lies <adapterDir>/spec-mockup/. Das Projekt besitzt ihn. Er benennt:
| Fakt | Beispiel |
|---|---|
| Die Token-Dateien jeder App, in Reihenfolge | apps/console/src/styles/tokens.css |
| Die Selektoren der hellen und dunklen Werte | :root, .dark |
| Der Lucide-Icon-Ordner | node_modules/lucide-react/dist/esm/icons |
| Die Navigation jeder App mit einer Seitenleiste und woher sie kommt | die Routentabelle der Konsole |
| Die Quelle des echten Texts | der i18n-Katalog der App |
Wenn der Adapter fehlt, frage den Benutzer vor dem Zeichnen nach der Token-Datei. Erfinde niemals eine Farbe.
Das Format
Generiertes SVG, gezeichnet mit einer Bibliothek: boards.py neben dieser Datei. Jede Spezifikation hat ein Skript, docs/specs/assets/<Datum>-<Thema>-boards.py. Es importiert die Bibliothek und schreibt nach docs/specs/assets/<Datum>-<Thema>/. Bearbeite niemals ein generiertes SVG von Hand: Ändere das Skript und führe es erneut aus. Wenn der Bibliothek ein Teil fehlt, füge es zum projekteigenen Hilfsmodul neben dem Skript hinzu, damit die nächste Spezifikation es auch hat.
example.py ist ein bearbeitetes Board: eine Seite auf dem Desktop und auf dem Telefon, mit Pins und einer Legende. Führe es aus, um die Form zu sehen:
python3 .claude/skills/lw-spec-mockup/example.py <scratchpad>/mock --render
Ein Spezifikationsskript beginnt so:
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[3]
sys.path.insert(0, str(ROOT / ".claude/skills/lw-spec-mockup"))
from boards import GUTTER, Board, render, tokens
t = tokens("apps/console/src/styles/tokens.css", "light")
b = Board(1440 + 2 * GUTTER, 1200, icons="node_modules/lucide-react/dist/esm/icons")
b.title("Dashboard: der leere Zustand")
f = b.sidebar_app(
GUTTER,
160,
t["bg"],
t["sidebar"],
t["ink"],
t["muted"],
t["active"],
["Dashboard"],
"Dashboard",
"Konsole",
)
b.pin(f.x + f.left + 24, f.top + 30, 1)
b.legend(
GUTTER, 1100, [(1, "Der leere Zustand", "Kein Widget, bis das Produkt eines benennt.", "R1")], 1440
)
b.save("docs/specs/assets/<Datum>-<Thema>/desktop-leer.svg")
| Aufruf | Zeichnet |
|---|---|
tokens(files, theme, base=, dark=, overlay=) | Die aufgelösten --*-Werte der Token-Dateien. t["accent"]. Ein fehlender Token löst einen Fehler aus. overlay ist ein vorgeschlagenes Restyling |
phone(x, y, bg, ink, surface, url), desktop(x, y, bg, url) | Ein Telefon (web oder app), ein Browserfenster |
sidebar_app(x, y, ...) | Eine App mit einer linken Seitenleiste, z. B. eine Konsole. Kopiere nav aus der Routentabelle der App |
screenshot(path, x, y), crop(path, ...) | Ein echter Screenshot des ausgelieferten Bildschirms oder ein Ausschnitt davon, für die Spalte „Heute“ |
icon(name, x, y, size, color) | Ein Lucide-Glyph aus dem icons-Ordner des Boards |
image(path, ...), embed_font(family, path) | Echte Grafik und eine Markenschriftart aus dem Repository |
pin, legend, caption, column_head, row_label, title | Die Notizen. Ein Legendeneintrag länger als zwei Zeilen löst einen Fehler aus |
render(svg, png, w, h) | Der headless-Chrome-Screenshot für den untenstehenden Check |
Wiedergabetreue
- Farben sind Tokens, durch
tokens(). Ein Hex-Wert in einem Spezifikationsskript ist falsch, außer für Grafik, die die Token-Datei nicht enthält. - Text ist echt. Verwende den String des App-Katalogs, wenn er existiert. Ein neuer String ist kurz: ein Label, ein Wert, ein leerer Zustand, ein Fehler oder ein einzeiliger Hinweis.
- Daten sind realistisch. Verwende plausible Werte und einen langen, wo einer vorkommen kann.
Zeichenfläche
| Oberfläche | Rahmen |
|---|---|
| Telefon | 390 x 844, 48 px Ränder |
| Desktop | 1440 x 900 |
| Board | so breit wie seine Rahmen, so hoch wie seine Zeilen benötigen |
Was eine Spezifikation zeichnet
- Heute, dann Ziel. „Heute“ ist ein echter Screenshot des ausgelieferten Bildschirms (
heute-*.png). „Ziel“ ist das SVG. Setze sie nebeneinander in eine Markdown-Tabelle. - Eine Zeile pro Zustand, der zutrifft: Standard, leer, laden, Fehler, langer Text, der kleinste und der größte Inhalt.
- Pins und eine Legende. Ein nummerierter Pin auf jedem Element, das sich ändert. Unter dem Rahmen eine nummerierte Legende: eine fette Überschrift, maximal zwei Zeilen und die Spezifikations-ID, die sie bedient.
- Eine Komponentenliste in der Spezifikation neben den Boards: jedes Element, die Komponente, die es wiederverwendet, und jede neue Komponente mit ihrem Grund.
Namen
docs/specs/assets/<YYYY-MM-DD>-<Thema>-boards.py
docs/specs/assets/<YYYY-MM-DD>-<Thema>/<Oberfläche>-<Zustand>.svg
docs/specs/assets/<YYYY-MM-DD>-<Thema>/heute-<Oberfläche>-<Zustand>.png
Die Spezifikation benennt den Basisnamen jeder Datei, die sie in assets/ hält.
Sieh dir jedes Board an, bevor du es commitst
Ein Fehler eines Boards ist in seiner Quelle unsichtbar und im Bild deutlich. Rendere jedes Board und lies das PNG mit dem Read-Tool. Schreibe das PNG in das Scratchpad, nicht nach docs/.
- Kein Text läuft über seinen Rahmen hinaus, und kein Pin bedeckt einen Wert.
- Jeder String in der gezeichneten UI ist ein Label, ein Wert, ein leerer Zustand, ein Fehler oder ein Hinweis.
- Keine Platzhalter-Grafik und keine Farbe per Augenmaß ausgewählt.
- Jeder Zustand und jede Oberfläche, die die Spezifikation berührt, hat seinen Rahmen.
- Heute und Ziel stehen nebeneinander.
render benötigt google-chrome oder chromium im PATH.
Nach dem Build
Der Build muss dem genehmigten Board entsprechen. Erfasse den gebauten Bildschirm und lege ihn neben das Board. Ein Unterschied ist ein Fehler des Builds, kein Grund, das Board neu zu zeichnen.