Mockups de especificación
Este skill indica cómo dibujar. lw-spec-write indica dónde va la especificación y cómo se renderiza la página.
Hechos del producto
La carpeta del adaptador proviene de la configuración. Léela primero:
ah config get harness.adapterDir
Luego lee <adapterDir>/spec-mockup/. El proyecto la posee. Nombra:
| Hecho | Ejemplo |
|---|---|
| Los archivos de tokens de cada aplicación, en orden | apps/console/src/styles/tokens.css |
| Los selectores de los valores claro y oscuro | :root, .dark |
| La carpeta de iconos de Lucide | node_modules/lucide-react/dist/esm/icons |
| La navegación de cada aplicación con barra lateral, y de dónde proviene | la tabla de rutas de la consola |
| La fuente del texto real | el catálogo i18n de la aplicación |
Cuando falte el adaptador, pregúntale al usuario por el archivo de tokens antes de dibujar. Nunca inventes un color.
El formato
SVG generado, dibujado con una sola biblioteca: boards.py junto a este archivo. Cada especificación tiene un script, docs/specs/assets/<fecha>-<tema>-boards.py. Importa la biblioteca y escribe en docs/specs/assets/<fecha>-<tema>/. Nunca edites un SVG generado a mano: modifica el script y ejecútalo de nuevo. Cuando a la biblioteca le falte una pieza, agrégala al módulo auxiliar del proyecto junto al script, para que la próxima especificación también la tenga.
example.py es un tablero funcional: una página en escritorio y en teléfono, con anotaciones y una leyenda. Ejecútalo para ver la forma:
python3 .claude/skills/lw-spec-mockup/example.py <scratchpad>/mock --render
Un script de especificación comienza así:
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: estado vacío")
f = b.sidebar_app(
GUTTER,
160,
t["bg"],
t["sidebar"],
t["ink"],
t["muted"],
t["active"],
["Dashboard"],
"Dashboard",
"Consola",
)
b.pin(f.x + f.left + 24, f.top + 30, 1)
b.legend(
GUTTER, 1100, [(1, "Estado vacío", "Ningún widget hasta que el producto nombre uno.", "R1")], 1440
)
b.save("docs/specs/assets/<fecha>-<tema>/desktop-vacio.svg")
| Llamada | Dibuja |
|---|---|
tokens(files, theme, base=, dark=, overlay=) | Los valores resueltos --* de los archivos de tokens. t["accent"]. Un token faltante genera una excepción. overlay es un cambio de estilo propuesto |
phone(x, y, bg, ink, surface, url), desktop(x, y, bg, url) | Un teléfono (web o app), una ventana del navegador |
sidebar_app(x, y, ...) | Una aplicación con barra lateral izquierda, como una consola. Copia nav de la tabla de rutas de la aplicación |
screenshot(path, x, y), crop(path, ...) | Una captura real de la pantalla enviada, o una región de ella, para la columna "Hoy" |
icon(name, x, y, size, color) | Un glifo de Lucide desde la carpeta icons del tablero |
image(path, ...), embed_font(family, path) | Arte real y una tipografía de marca desde el repositorio |
pin, legend, caption, column_head, row_label, title | Las notas. Una nota de leyenda de más de dos líneas genera una excepción |
render(svg, png, w, h) | La captura de pantalla con Chrome sin interfaz gráfica para la verificación a continuación |
Fidelidad
- Los colores son tokens, a través de
tokens(). Un valor hexadecimal en un script de especificación es incorrecto, excepto para arte que el archivo de tokens no contenga. - El texto es real. Usa la cadena del catálogo de la aplicación cuando exista. Una cadena nueva es corta: una etiqueta, un valor, un estado vacío, un error o una sugerencia de una línea.
- Los datos son realistas. Usa valores plausibles, y uno largo donde pueda ocurrir.
Lienzo
| Superficie | Marco |
|---|---|
| Teléfono | 390 x 844, márgenes de 48 px |
| Escritorio | 1440 x 900 |
| Tablero | tan ancho como sus marcos, tan alto como sus filas necesiten |
Lo que dibuja una especificación
- Hoy, luego objetivo. "Hoy" es una captura de pantalla real de la pantalla enviada (
today-*.png). "Objetivo" es el SVG. Colócalos lado a lado en una tabla Markdown. - Una fila por estado que aplique: por defecto, vacío, carga, error, texto largo, el contenido más pequeño y el más grande.
- Anotaciones y una leyenda. Una anotación numerada en cada elemento que cambie. Debajo del marco, una leyenda numerada: un encabezado en negrita, dos líneas como máximo, y el ID de la especificación a la que sirve.
- Una lista de componentes en la especificación junto a los tableros: cada elemento, el componente que reutiliza, y cada componente nuevo con su motivo.
Nombres
docs/specs/assets/<YYYY-MM-DD>-<tema>-boards.py
docs/specs/assets/<YYYY-MM-DD>-<tema>/<superficie>-<estado>.svg
docs/specs/assets/<YYYY-MM-DD>-<tema>/today-<superficie>-<estado>.png
La especificación nombra el nombre base de cada archivo que contiene en assets/.
Revisa cada tablero antes de hacer commit
Un defecto de un tablero es invisible en su fuente y evidente en la imagen. Renderiza cada tablero y lee el PNG con la herramienta Leer. Escribe el PNG en el bloc de notas, no en docs/.
- Ningún texto se sale de su marco, y ninguna anotación cubre un valor.
- Cada cadena en la interfaz dibujada es una etiqueta, un valor, un estado vacío, un error o una sugerencia.
- Sin arte de relleno, y sin color elegido a ojo.
- Cada estado y superficie que toca la especificación tiene su marco.
- Hoy y objetivo están lado a lado.
render necesita google-chrome o chromium en PATH.
Después de la compilación
La compilación debe coincidir con el tablero aprobado. Captura la pantalla compilada y colócala junto al tablero. Una diferencia es un defecto de la compilación, no un motivo para volver a dibujar el tablero.