@devground/ui-conventions 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -35,10 +35,16 @@ explicitly.
|
|
|
35
35
|
|
|
36
36
|
1. It always loads the universal base layer (`references/base.md`): rules that hold across
|
|
37
37
|
any stack or design system.
|
|
38
|
-
2. It
|
|
38
|
+
2. It always loads the universal design fundamentals (`references/design-fundamentals.md`):
|
|
39
|
+
principles, not a style — tokens over raw values (and a **SYSTEM GAP** instead of an invented
|
|
40
|
+
value), one primary action and one emphasis region per context, numbers with their
|
|
41
|
+
composition and timestamp, purposeful space, people-first copy, and how a product family
|
|
42
|
+
shares one grammar with different voices.
|
|
43
|
+
3. It looks for a project-specific overlay at `docs/ui-conventions.md` in the repo you're
|
|
39
44
|
working in. If one exists, it loads it, and on conflict the overlay wins over the base
|
|
40
|
-
— concrete tokens, own components, and helpers replace the generic rule.
|
|
41
|
-
|
|
45
|
+
— concrete tokens, own components, and helpers replace the generic rule. The overlay is the
|
|
46
|
+
only door for a brand or design system, so one project's look never leaks into another.
|
|
47
|
+
4. If no overlay exists yet, it offers to mine one from your actual code (never without
|
|
42
48
|
asking) using `references/mining-prompt.md`, producing a `docs/ui-conventions.md` in the
|
|
43
49
|
format of `references/overlay-template.md`.
|
|
44
50
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Copies a directory tree, never overwriting a file that already exists at the
|
|
8
|
+
* destination.
|
|
9
|
+
*
|
|
10
|
+
* This guard is the package's core promise: re-running the installer after an
|
|
11
|
+
* upgrade pulls in new files without clobbering local edits to a skill. It lives
|
|
12
|
+
* in its own module (rather than inline in `setup.js`) so it can be tested —
|
|
13
|
+
* `setup.js` runs its work at import time, so anything defined there is
|
|
14
|
+
* untestable without executing the installer.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} src Source directory.
|
|
17
|
+
* @param {string} dst Destination directory (created if missing).
|
|
18
|
+
* @returns {{ written: number, skipped: number }} Files copied vs. left alone.
|
|
19
|
+
*/
|
|
20
|
+
function copyDirGuarded(src, dst) {
|
|
21
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
22
|
+
let written = 0;
|
|
23
|
+
let skipped = 0;
|
|
24
|
+
|
|
25
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
26
|
+
const srcPath = path.join(src, entry.name);
|
|
27
|
+
const dstPath = path.join(dst, entry.name);
|
|
28
|
+
|
|
29
|
+
if (entry.isDirectory()) {
|
|
30
|
+
const sub = copyDirGuarded(srcPath, dstPath);
|
|
31
|
+
written += sub.written;
|
|
32
|
+
skipped += sub.skipped;
|
|
33
|
+
} else if (entry.isFile()) {
|
|
34
|
+
if (fs.existsSync(dstPath)) {
|
|
35
|
+
skipped++;
|
|
36
|
+
} else {
|
|
37
|
+
fs.copyFileSync(srcPath, dstPath);
|
|
38
|
+
written++;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return { written, skipped };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { copyDirGuarded };
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devground/ui-conventions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "UI convention skill for Claude Code — loads a project's frontend conventions (own components vs browser primitives, es-CL input formatting for RUT/phone/currency, accessibility and focus, error/loading states, microinteractions) as context BEFORE generating or editing UI, with an optional per-project overlay.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"devground-ui-conventions": "./setup.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"setup.js",
|
|
10
|
-
"skills/"
|
|
10
|
+
"skills/",
|
|
11
|
+
"lib/",
|
|
12
|
+
"!lib/*.test.js"
|
|
11
13
|
],
|
|
12
14
|
"keywords": [
|
|
13
15
|
"ui",
|
|
@@ -37,5 +39,19 @@
|
|
|
37
39
|
"bugs": {
|
|
38
40
|
"url": "https://github.com/elkisdm/devground/issues"
|
|
39
41
|
},
|
|
40
|
-
"
|
|
42
|
+
"devground": {
|
|
43
|
+
"status": "supported",
|
|
44
|
+
"graduatedBy": "ADR-0032",
|
|
45
|
+
"graduatedAt": "2026-09-07"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"vitest": "^2.1.0",
|
|
49
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
50
|
+
"@devground/vitest-config": "0.3.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest",
|
|
55
|
+
"test:coverage": "vitest run --coverage"
|
|
56
|
+
}
|
|
41
57
|
}
|
package/setup.js
CHANGED
|
@@ -26,29 +26,7 @@ function info(msg) {
|
|
|
26
26
|
console.log(` \x1b[36m→\x1b[0m ${msg}`);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
function copyDirGuarded(src, dst) {
|
|
31
|
-
fs.mkdirSync(dst, { recursive: true });
|
|
32
|
-
let written = 0;
|
|
33
|
-
let skipped = 0;
|
|
34
|
-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
35
|
-
const srcPath = path.join(src, entry.name);
|
|
36
|
-
const dstPath = path.join(dst, entry.name);
|
|
37
|
-
if (entry.isDirectory()) {
|
|
38
|
-
const sub = copyDirGuarded(srcPath, dstPath);
|
|
39
|
-
written += sub.written;
|
|
40
|
-
skipped += sub.skipped;
|
|
41
|
-
} else if (entry.isFile()) {
|
|
42
|
-
if (fs.existsSync(dstPath)) {
|
|
43
|
-
skipped++;
|
|
44
|
-
} else {
|
|
45
|
-
fs.copyFileSync(srcPath, dstPath);
|
|
46
|
-
written++;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return { written, skipped };
|
|
51
|
-
}
|
|
29
|
+
const { copyDirGuarded } = require('./lib/copy-guarded.js');
|
|
52
30
|
|
|
53
31
|
const global = process.argv.includes('--global');
|
|
54
32
|
const baseDir = global ? os.homedir() : process.cwd();
|
|
@@ -20,9 +20,15 @@ skill by name.
|
|
|
20
20
|
|
|
21
21
|
1. **Always** load `references/base.md` first. It is the universal layer: rules that apply
|
|
22
22
|
to any project regardless of stack or design system.
|
|
23
|
-
2. **
|
|
23
|
+
2. **Always** load `references/design-fundamentals.md` too. It is the universal design layer:
|
|
24
|
+
principles (tokens over raw values, one decision per context, data with its context,
|
|
25
|
+
purposeful space, people-first copy) — never a concrete style. When a token or rule you
|
|
26
|
+
need is missing, declare a **SYSTEM GAP** instead of inventing a value.
|
|
27
|
+
3. **Look for a project overlay** at `docs/ui-conventions.md` in the repo you are working in.
|
|
24
28
|
- If it exists, load it. On conflict, **the overlay wins over base.md** — its concrete
|
|
25
|
-
tokens, own components, and helpers replace the generic rule.
|
|
29
|
+
tokens, own components, and helpers replace the generic rule. The overlay is also the
|
|
30
|
+
only place a brand or design system enters: never carry one project's look (colors,
|
|
31
|
+
fonts, radii) into another project that does not declare it.
|
|
26
32
|
- If it does **not** exist, continue with the base layer alone and **offer** to mine one,
|
|
27
33
|
pointing at `references/mining-prompt.md`. Do not mine it unless the user asks for it.
|
|
28
34
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Fundamentos de diseño de producto — capa universal
|
|
2
|
+
|
|
3
|
+
## 0. Cómo usar este documento
|
|
4
|
+
|
|
5
|
+
`base.md` cuida que la UI sea **correcta** (semántica, foco, errores). Este documento cuida que
|
|
6
|
+
sea **intencional**: que cada decisión visual tenga un trabajo y que el producto se lea como un
|
|
7
|
+
sistema y no como una suma de pantallas.
|
|
8
|
+
|
|
9
|
+
Son principios y reglas, **no una estética**. No fijan colores, tipografías ni radios concretos:
|
|
10
|
+
esos valores los pone el sistema de diseño del proyecto, declarado en su overlay
|
|
11
|
+
(`docs/ui-conventions.md`). Un proyecto sin overlay aplica estos fundamentos con los tokens que ya
|
|
12
|
+
tenga, sin importar un estilo de otro proyecto.
|
|
13
|
+
|
|
14
|
+
Precedencia: los **valores** del overlay siempre ganan. Un **principio** de este documento solo se
|
|
15
|
+
deja de aplicar si el overlay lo declara como excepción explícita, con su motivo.
|
|
16
|
+
|
|
17
|
+
## 1. El sistema antes que la pantalla
|
|
18
|
+
|
|
19
|
+
La IA aprende de lo que encuentra: si encuentra cinco formas de resolver lo mismo, replica la
|
|
20
|
+
inconsistencia; si encuentra un sistema, lo escala. Por eso:
|
|
21
|
+
|
|
22
|
+
- **Consume nombres de token y componentes aprobados; no decidas valores.** Nunca escribas un hex,
|
|
23
|
+
un tamaño, un radio o un espaciado suelto si existe un token. Si no existe, no lo inventes:
|
|
24
|
+
declara un **SYSTEM GAP** en tu respuesta (qué faltó y qué valor provisorio usaste) para que un
|
|
25
|
+
humano lo resuelva en el sistema.
|
|
26
|
+
- **Un valor existe una sola vez y se nombra por su uso**, no por su apariencia: `decide`,
|
|
27
|
+
`señala`, `superficie`, en vez de `blue-600` o `gray-light`. Dos valores que se ven iguales y
|
|
28
|
+
son distintos en el código son dos valores para el sistema: unifícalos.
|
|
29
|
+
- **Ninguna distancia a ojo.** El espaciado sale de una escala (típicamente múltiplos de 4) y los
|
|
30
|
+
radios, de un conjunto corto. Un `13px` o un `mt-[7px]` es una decisión no tomada.
|
|
31
|
+
- **Diagnostica antes de rediseñar.** Para ordenar UI existente, primero cuenta lo que hay
|
|
32
|
+
(colores, espaciados, radios y tamaños de texto distintos; patrones repetidos; fricciones) y
|
|
33
|
+
clasifica cada patrón en **conservar / adaptar / dejar**, con evidencia y no por gusto. Un patrón
|
|
34
|
+
pasa a ser regla común cuando lo confirman dos contextos de tipo distinto.
|
|
35
|
+
|
|
36
|
+
## 2. Cada recurso tiene un trabajo
|
|
37
|
+
|
|
38
|
+
- **Nada es decorativo ni está por defecto.** Si un color, un gradiente, una sombra, un borde o una
|
|
39
|
+
métrica no ayuda a comprender, decidir o actuar, no va.
|
|
40
|
+
- **El color se asigna por rol**, y cada rol tiene un solo significado en todo el producto. El
|
|
41
|
+
color de la acción que decide no se reutiliza para decorar.
|
|
42
|
+
- **La marca vive en su zona**: entrada, portadas y zona de marca. Los gradientes y luces de marca
|
|
43
|
+
**nunca van detrás de un dato ni comunican un estado.**
|
|
44
|
+
- **Estado = indicador + palabra, nunca solo color** («● Sin respuesta», no un punto naranja
|
|
45
|
+
solo).
|
|
46
|
+
|
|
47
|
+
## 3. Jerarquía: una decisión por contexto
|
|
48
|
+
|
|
49
|
+
- **Una sola acción primaria por contexto**: la que cambia algo. Crear, exportar, cancelar y
|
|
50
|
+
navegar no deciden; van en el estilo secundario o neutro.
|
|
51
|
+
- **Una sola región de énfasis máximo por vista**: el bloque que concentra lo que requiere
|
|
52
|
+
decisión ahora (p. ej. «42 requieren tu decisión» con su acción). Si hay dos, ninguna lo es.
|
|
53
|
+
- **La profundidad la dan el tono y el material, no la acumulación de efectos.** Lo que requiere
|
|
54
|
+
atención se acerca (más contraste, superficie de trabajo); lo que acompaña se aleja (capa más
|
|
55
|
+
tenue). Evita apilar sombras, bordes y cápsulas para separar cosas.
|
|
56
|
+
- **La selección es un tono, no un objeto.** Reposo: nada compite. Hover: sube el tono. Selección:
|
|
57
|
+
tono, peso y trazo. Foco: un anillo, solo con teclado (`focus-visible`). No agregues iconos,
|
|
58
|
+
barras ni badges extra para marcar lo activo; un filtro activo se marca con tono.
|
|
59
|
+
|
|
60
|
+
## 4. Precisión en los datos
|
|
61
|
+
|
|
62
|
+
- **Ninguna cifra suelta.** Toda métrica dice de qué está hecha y cuándo se midió: «477 asignados ·
|
|
63
|
+
59 % de respuesta · meta 70 % · medido 09:14».
|
|
64
|
+
- **Declara lo que muestras.** Entorno, fuente, período y filtros activos se ven siempre (un
|
|
65
|
+
selector que dice «producción» sobre datos de sandbox es un defecto). Un estado que no dice
|
|
66
|
+
cuándo se midió no es un estado.
|
|
67
|
+
- **Las cifras se alinean.** Usa numerales tabulares (`font-variant-numeric: tabular-nums`) y
|
|
68
|
+
alinea por columna horas, IDs, montos y estados.
|
|
69
|
+
- **El valor que decide va junto a la lista**, no escondido en un detalle. Usa maestro-detalle
|
|
70
|
+
(lista + panel) para no perder contexto al abrir un registro.
|
|
71
|
+
- **No repitas enlaces genéricos.** Un «Ver detalle» en cada fila es ruido: la fila entera es el
|
|
72
|
+
destino, o el enlace dice a dónde lleva.
|
|
73
|
+
|
|
74
|
+
## 5. Espacio con propósito
|
|
75
|
+
|
|
76
|
+
- **El espacio agrupa.** Separa lo que decide de lo que acompaña y de lo que pasa en segundo plano;
|
|
77
|
+
la cercanía comunica relación.
|
|
78
|
+
- **Lo que nombra es grande y liviano; lo operativo es denso.** Títulos y cifras protagonistas con
|
|
79
|
+
tamaño grande y peso bajo; tablas y listas compactas.
|
|
80
|
+
- **Aire, no vacío.** Simplificar no es dejar huecos porque sí. Tampoco cargar la vista: con más de
|
|
81
|
+
tres filtros, el panel nace plegado y deja a la vista los frecuentes.
|
|
82
|
+
|
|
83
|
+
## 6. La interfaz habla con personas
|
|
84
|
+
|
|
85
|
+
- **Voz directa, precisa y cercana, sin ser blanda.** Habla a la persona, no a la base de datos.
|
|
86
|
+
Mal: «revisar last_error en inbound_events». Bien: «Este lead no llegó al CRM; reintentamos a las
|
|
87
|
+
10:30».
|
|
88
|
+
- **Las personas aparecen con nombre y cara** (avatar o iniciales), no con un ID.
|
|
89
|
+
- **Estado vacío = causa + paso siguiente** («Aún no hay reservas este mes · Crear reserva»).
|
|
90
|
+
- **El aviso confirma y deja deshacer** en las acciones reversibles («Proyecto reasignado a A. Soto
|
|
91
|
+
· Deshacer»), en vez de pedir confirmación previa.
|
|
92
|
+
|
|
93
|
+
## 7. Familias de productos: misma gramática, distintas voces
|
|
94
|
+
|
|
95
|
+
Cuando varios productos conviven en una suite, no deben verse iguales, pero quien cambia de
|
|
96
|
+
producto no debe aprender otro idioma:
|
|
97
|
+
|
|
98
|
+
- **Comparten la gramática**: la misma estructura de navegación, el mismo selector de producto en
|
|
99
|
+
el mismo lugar, la misma búsqueda global, los mismos estados de interacción y las mismas piezas
|
|
100
|
+
(pastillas, filtros, avisos).
|
|
101
|
+
- **Varía la voz**, y solo a través de tokens: densidad, tonalidad y forma propias de cada
|
|
102
|
+
producto, nunca reglas distintas para lo mismo.
|
|
103
|
+
|
|
104
|
+
## 8. Antipatrones (el look genérico de la IA)
|
|
105
|
+
|
|
106
|
+
- Bordes, cápsulas, sombras y radios grandes en todo, para «separar» lo que el espacio y el tono ya
|
|
107
|
+
separan.
|
|
108
|
+
- Botones en píldora y varias acciones de color primario compitiendo en la misma vista.
|
|
109
|
+
- La «tecnología que grita»: fondos negros con neones y azul eléctrico; o el extremo opuesto,
|
|
110
|
+
minimalismo vacío que esconde la información.
|
|
111
|
+
- Tarjetas de KPI con cifras sin contexto ni fecha, puestas porque «se ven bien».
|
|
112
|
+
- Gradientes sin función, sobre todo detrás de datos.
|
|
113
|
+
- Mensajes de sistema en lenguaje técnico y personas identificadas por ID.
|
|
@@ -45,8 +45,20 @@ librería o CSS puro, ninguna]
|
|
|
45
45
|
|
|
46
46
|
> Helper preferente si el repo no tiene uno propio: @devground/chile-formats.
|
|
47
47
|
|
|
48
|
+
## 7. Sistema de diseño (valores)
|
|
49
|
+
|
|
50
|
+
> Solo si el proyecto adopta un sistema de diseño. Aquí entran los valores que
|
|
51
|
+
> `design-fundamentals.md` deja abiertos; un proyecto sin esta sección no hereda el estilo de
|
|
52
|
+
> otro.
|
|
53
|
+
|
|
54
|
+
- **Fuente:** [ruta o URL del sistema de diseño]
|
|
55
|
+
- **Tokens por rol:** [nombre de uso → token del código, p. ej. `decide` → `--color-decide`]
|
|
56
|
+
- **Tipografías, escala de espaciado, radios:** [valores]
|
|
57
|
+
- **Excepciones a los fundamentos:** [principio → motivo, o «ninguna»]
|
|
58
|
+
- **SYSTEM GAP abiertos:** [lo que el sistema todavía no define]
|
|
59
|
+
|
|
48
60
|
## Deuda conocida
|
|
49
61
|
|
|
50
|
-
| Deuda
|
|
51
|
-
|
|
|
62
|
+
| Deuda | Ubicación | Patrón correcto |
|
|
63
|
+
| ------------- | --------------------------- | -------------------------------- |
|
|
52
64
|
| [descripción] | `ruta/al/archivo.ext:línea` | [qué debería usarse en su lugar] |
|