Mockups de especificação
Esta skill diz como desenhar. lw-spec-write diz onde a especificação vai e como a página renderiza.
Fatos do produto
A pasta do adaptador vem da configuração. Leia primeiro:
ah config get harness.adapterDir
Depois leia <adapterDir>/spec-mockup/. O projeto é o dono. Ela nomeia:
| Fato | Exemplo |
|---|---|
| Os arquivos de token de cada aplicativo, em ordem | apps/console/src/styles/tokens.css |
| Os seletores dos valores claro e escuro | :root, .dark |
| A pasta de ícones lucide | node_modules/lucide-react/dist/esm/icons |
| A navegação de cada aplicativo com uma barra lateral, e de onde ela vem | a tabela de rotas do console |
| A fonte de cópia real | o catálogo i18n do aplicativo |
Quando o adaptador estiver faltando, pergunte ao usuário pelo arquivo de token antes de desenhar. Nunca invente uma cor.
O formato
SVG gerado, desenhado com uma biblioteca: boards.py ao lado deste arquivo. Cada especificação tem um script, docs/specs/assets/<data>-<topico>-boards.py. Ele importa a biblioteca e escreve em docs/specs/assets/<data>-<topico>/. Nunca edite um SVG gerado manualmente: altere o script e execute-o novamente. Quando a biblioteca não tiver uma peça, adicione-a ao módulo auxiliar do projeto ao lado do script, para que a próxima especificação também a tenha.
example.py é um quadro trabalhado: uma página no desktop e no celular, com pins e uma legenda. Execute-o para ver a forma:
python3 .claude/skills/lw-spec-mockup/example.py <scratchpad>/mock --render
Um script de especificação começa assim:
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: o estado vazio")
f = b.sidebar_app(
GUTTER,
160,
t["bg"],
t["sidebar"],
t["ink"],
t["muted"],
t["active"],
["Dashboard"],
"Dashboard",
"Console",
)
b.pin(f.x + f.left + 24, f.top + 30, 1)
b.legend(
GUTTER, 1100, [(1, "O estado vazio", "Nenhum widget até que o produto nomeie um.", "R1")], 1440
)
b.save("docs/specs/assets/<data>-<topico>/desktop-vazio.svg")
| Chamada | Desenha |
|---|---|
tokens(files, theme, base=, dark=, overlay=) | Os valores resolvidos --* dos arquivos de token. t["accent"]. Um token ausente gera um erro. overlay é um reestilo proposto |
phone(x, y, bg, ink, surface, url), desktop(x, y, bg, url) | Um celular (web ou app), uma janela do navegador |
sidebar_app(x, y, ...) | Um aplicativo com uma barra lateral esquerda, como um console. Copie nav da tabela de rotas do aplicativo |
screenshot(path, x, y), crop(path, ...) | Uma captura real da tela enviada, ou uma região dela, para a coluna "Hoje" |
icon(name, x, y, size, color) | Um glifo lucide da pasta icons do quadro |
image(path, ...), embed_font(family, path) | Arte real e uma face da marca do repositório |
pin, legend, caption, column_head, row_label, title | As anotações. Uma anotação de legenda com mais de duas linhas gera um erro |
render(svg, png, w, h) | A captura de tela do Chrome headless para a verificação abaixo |
Fidelidade
- Cores são tokens, via
tokens(). Um valor hexadecimal em um script de especificação está errado, exceto para arte que o arquivo de token não contém. - Cópia é real. Use a string do catálogo do aplicativo quando existir. Uma nova string é curta: um rótulo, um valor, um estado vazio, um erro ou uma dica de uma linha.
- Dados são realistas. Use valores plausíveis, e um longo onde um pode ocorrer.
Tela
| Superfície | Moldura |
|---|---|
| Celular | 390 x 844, margens de 48 px |
| Desktop | 1440 x 900 |
| Quadro | tão largo quanto seus quadros, tão alto quanto suas linhas precisam |
O que uma especificação desenha
- Hoje, depois alvo. "Hoje" é uma captura de tela real da tela enviada (
hoje-*.png). "Alvo" é o SVG. Coloque-os lado a lado em uma tabela Markdown. - Uma linha por estado que se aplica: padrão, vazio, carregamento, erro, texto longo, o menor e o maior conteúdo.
- Pins e uma legenda. Um pin numerado em cada elemento que muda. Abaixo da moldura, uma legenda numerada: um cabeçalho em negrito, no máximo duas linhas, e o ID da especificação que atende.
- Uma lista de componentes na especificação ao lado dos quadros: cada elemento, o componente que ele reutiliza e cada novo componente com sua razão.
Nomes
docs/specs/assets/<YYYY-MM-DD>-<topico>-boards.py
docs/specs/assets/<YYYY-MM-DD>-<topico>/<superficie>-<estado>.svg
docs/specs/assets/<YYYY-MM-DD>-<topico>/hoje-<superficie>-<estado>.png
A especificação nomeia o nome base de cada arquivo que contém em assets/.
Olhe para cada quadro antes de commitá-lo
Um defeito de um quadro é invisível em sua fonte e claro na imagem. Renderize cada quadro e leia o PNG com a ferramenta de Leitura. Escreva o PNG no scratchpad, não em docs/.
- Nenhum texto ultrapassa sua moldura, e nenhum pin cobre um valor.
- Cada string na IU desenhada é um rótulo, um valor, um estado vazio, um erro ou uma dica.
- Nenhuma arte de placeholder e nenhuma cor escolhida a olho.
- Cada estado e superfície que a especificação toca tem sua moldura.
- Hoje e alvo ficam lado a lado.
render precisa de google-chrome ou chromium no PATH.
Após a construção
A construção deve corresponder ao quadro aprovado. Capture a tela construída e coloque-a ao lado do quadro. Uma diferença é um defeito da construção, não uma razão para desenhar o quadro novamente.