@devground/ui-conventions 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 devground contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # @devground/ui-conventions
2
+
3
+ **UI convention skill for Claude Code.** Loads a project's frontend conventions — own
4
+ components vs. browser primitives, es-CL input formatting for RUT/phone/currency,
5
+ accessibility and focus, error/loading states, microinteractions — as context **before**
6
+ generating or editing UI, instead of relying on a design audit to catch inconsistencies
7
+ after the fact.
8
+
9
+ ## Install
10
+
11
+ Into the current project:
12
+
13
+ ```bash
14
+ npx @devground/ui-conventions
15
+ # -> .claude/skills/ui-conventions
16
+ ```
17
+
18
+ For all your projects (user-level):
19
+
20
+ ```bash
21
+ npx @devground/ui-conventions --global
22
+ # -> ~/.claude/skills/ui-conventions
23
+ ```
24
+
25
+ Existing files are never overwritten — re-run after an upgrade to pull in new reference
26
+ files while keeping your local edits.
27
+
28
+ ## How it triggers
29
+
30
+ The skill fires before you generate or modify any frontend UI — forms, inputs, modals,
31
+ buttons, pages, React/Next.js/Tailwind components — even if you don't name the skill
32
+ explicitly.
33
+
34
+ ## Base → overlay → mining
35
+
36
+ 1. It always loads the universal base layer (`references/base.md`): rules that hold across
37
+ any stack or design system.
38
+ 2. It looks for a project-specific overlay at `docs/ui-conventions.md` in the repo you're
39
+ 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
+ 3. If no overlay exists yet, it offers to mine one from your actual code (never without
42
+ asking) using `references/mining-prompt.md`, producing a `docs/ui-conventions.md` in the
43
+ format of `references/overlay-template.md`.
44
+
45
+ ## Relationship with design-taste / design-audit
46
+
47
+ `@devground/design-taste` owns aesthetics — layout, typography, motion, spacing taste.
48
+ `ui-conventions` is a different layer: correctness and consistency (input semantics,
49
+ focus management, error states) applied while the code is being written. A later
50
+ design-audit remains a safety net, but with this skill loaded up front there should be
51
+ little left for it to catch.
52
+
53
+ ## License
54
+
55
+ MIT
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@devground/ui-conventions",
3
+ "version": "0.1.0",
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
+ "bin": {
6
+ "devground-ui-conventions": "./setup.js"
7
+ },
8
+ "files": [
9
+ "setup.js",
10
+ "skills/"
11
+ ],
12
+ "keywords": [
13
+ "ui",
14
+ "conventions",
15
+ "frontend",
16
+ "accessibility",
17
+ "chile",
18
+ "es-cl",
19
+ "rut",
20
+ "claude-code",
21
+ "skill",
22
+ "devground"
23
+ ],
24
+ "license": "MIT",
25
+ "engines": {
26
+ "node": ">=20"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/elkisdm/devground.git",
34
+ "directory": "packages/ui-conventions"
35
+ },
36
+ "homepage": "https://github.com/elkisdm/devground/tree/main/packages/ui-conventions#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/elkisdm/devground/issues"
39
+ },
40
+ "scripts": {}
41
+ }
package/setup.js ADDED
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Installs the ui-conventions skill into a Claude Code skills directory.
5
+ *
6
+ * npx @devground/ui-conventions -> <cwd>/.claude/skills/ (this project)
7
+ * npx @devground/ui-conventions --global -> ~/.claude/skills/ (all projects)
8
+ *
9
+ * Each skill lands in its own directory (matching its frontmatter `name`).
10
+ * Existing files are never overwritten — your local edits to a skill are
11
+ * preserved, only missing files are added (re-run after upgrading to pull in
12
+ * new skills or reference files).
13
+ */
14
+
15
+ const fs = require('node:fs');
16
+ const path = require('node:path');
17
+ const os = require('node:os');
18
+
19
+ function log(msg) {
20
+ console.log(` \x1b[32m✓\x1b[0m ${msg}`);
21
+ }
22
+ function warn(msg) {
23
+ console.log(` \x1b[33m!\x1b[0m ${msg}`);
24
+ }
25
+ function info(msg) {
26
+ console.log(` \x1b[36m→\x1b[0m ${msg}`);
27
+ }
28
+
29
+ /** Copies a tree, skipping files that already exist at the destination. */
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
+ }
52
+
53
+ const global = process.argv.includes('--global');
54
+ const baseDir = global ? os.homedir() : process.cwd();
55
+ const src = path.join(__dirname, 'skills');
56
+ const dstRoot = path.join(baseDir, '.claude', 'skills');
57
+
58
+ if (!fs.existsSync(src)) {
59
+ console.error('\x1b[31m✗\x1b[0m skills/ not found in package. Aborting.');
60
+ process.exit(1);
61
+ }
62
+
63
+ // Only directories under skills/ are actual skills; loose files
64
+ // would be packaging metadata, not skills.
65
+ const skillDirs = fs
66
+ .readdirSync(src, { withFileTypes: true })
67
+ .filter((e) => e.isDirectory())
68
+ .map((e) => e.name);
69
+
70
+ let written = 0;
71
+ let skipped = 0;
72
+ const installed = [];
73
+ for (const name of skillDirs) {
74
+ const res = copyDirGuarded(path.join(src, name), path.join(dstRoot, name));
75
+ written += res.written;
76
+ skipped += res.skipped;
77
+ if (res.written > 0) installed.push(name);
78
+ }
79
+
80
+ const scope = global ? 'global (~/.claude/skills)' : 'project (.claude/skills)';
81
+ if (written > 0) {
82
+ log(`ui-conventions installed — ${scope} — ${written} file(s)`);
83
+ info(`skills: ${installed.join(', ')}`);
84
+ }
85
+ if (skipped > 0) warn(`${skipped} file(s) already existed (kept your version)`);
86
+ if (written === 0 && skipped > 0) info('Already up to date.');
87
+
88
+ info('Loads before you generate UI: universal conventions + your project overlay if');
89
+ info('docs/ui-conventions.md exists.');
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: ui-conventions
3
+ description: Fires BEFORE generating or modifying any frontend UI — forms, inputs, modals, buttons, pages, React/Next.js/Tailwind components — even if the user does not name the skill; loads the project's UI conventions so code is correct and consistent on the first pass instead of fixed by a later audit; especially for Chilean (es-CL) apps: RUT, phone +56, currency/UF formatting.
4
+ ---
5
+
6
+ # UI Conventions
7
+
8
+ ## 1. Purpose / when this fires
9
+
10
+ This skill acts **before** UI code is written, not after. It is a correctness/consistency
11
+ layer: own components vs. browser primitives, input formatting, accessibility and focus,
12
+ error/loading states, and microinteractions — plus region-specific rules for Chile (es-CL)
13
+ such as RUT, phone, and currency formatting.
14
+
15
+ Trigger it whenever you are about to generate or edit a form, input, modal, button, page,
16
+ or any React/Next.js/Tailwind component — even if the user's request does not mention this
17
+ skill by name.
18
+
19
+ ## 2. Load order (mandatory)
20
+
21
+ 1. **Always** load `references/base.md` first. It is the universal layer: rules that apply
22
+ to any project regardless of stack or design system.
23
+ 2. **Look for a project overlay** at `docs/ui-conventions.md` in the repo you are working in.
24
+ - 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.
26
+ - If it does **not** exist, continue with the base layer alone and **offer** to mine one,
27
+ pointing at `references/mining-prompt.md`. Do not mine it unless the user asks for it.
28
+
29
+ ## 3. Apply during generation, not after
30
+
31
+ Apply these conventions while writing the code — the first draft should already follow
32
+ them. This skill is not a linter that runs afterward; it is context loaded up front so the
33
+ correction pass a design-audit would otherwise require becomes unnecessary.
34
+
35
+ ## 4. Relationship with other skills
36
+
37
+ `design-taste` / `frontend-design` own aesthetics — layout, typography, motion, spacing
38
+ taste. This skill is a different layer: correctness and consistency (does the input have
39
+ the right `type`/`inputMode`, does the modal trap focus, is the RUT validated). A later
40
+ design-audit still acts as a safety net, but with this skill loaded up front there should
41
+ be little left for it to catch.
42
+
43
+ ## 5. Maintain the overlay
44
+
45
+ When you generate UI and establish a new pattern the overlay does not yet capture, pay down a
46
+ row from its "Deuda conocida" table, or discover that `docs/ui-conventions.md` contradicts the
47
+ real code, update the overlay in the **same** change — do not leave it stale. Same principle as
48
+ spec-flow's codemap: whoever uses the overlay maintains it. If the repo has no overlay yet and
49
+ you just wrote UI worth codifying, offer to mine one (`references/mining-prompt.md`); never
50
+ rewrite the overlay silently — surface the diff so the human can confirm it.
@@ -0,0 +1,118 @@
1
+ # Convenciones de UI — capa base
2
+
3
+ ## 0. Cómo usar este documento
4
+
5
+ Esta es la capa universal: reglas válidas para cualquier proyecto, independientemente del
6
+ stack o sistema de diseño. Si el proyecto tiene un overlay (`docs/ui-conventions.md`), ese
7
+ overlay tiene precedencia sobre lo que dice aquí.
8
+
9
+ ## 1. Componentes propios vs. primitivas del navegador
10
+
11
+ - Usa siempre el componente propio del proyecto (botón, input, modal) en vez de HTML nativo
12
+ estilizado a mano. Un `<button>` o `<input>` nativo con clases sueltas reimplementa algo
13
+ que el sistema de componentes ya resuelve, y diverge con el tiempo.
14
+ - Combina clases con el helper de composición de clases del proyecto (equivalente a
15
+ `cn`/`twMerge`), nunca concatenando template strings. Antipatrón: `` `btn ${variant} ${size}` ``
16
+ en vez de `cn('btn', variant, size)`.
17
+ - No introduzcas una librería nueva (toaster, motion, date-picker) sin una decisión
18
+ explícita (ADR o equivalente). Si el proyecto no tiene toaster, usa feedback inline; si
19
+ no tiene motion, usa transiciones CSS.
20
+ - Reutiliza el modal/dialog del proyecto para overlays — ya resuelve el trap de foco, el
21
+ cierre con Escape y el bloqueo de scroll. No reimplementes esa lógica con un portal
22
+ ad-hoc por componente.
23
+
24
+ ## 2. Formateo de inputs (semántica universal)
25
+
26
+ - Usa `type`, `inputMode` y `autoComplete` semánticos según el campo:
27
+ - Email → `type="email"` `inputMode="email"` `autoComplete="email"` `spellCheck={false}`
28
+ `autoCapitalize="none"`.
29
+ - Teléfono → `type="tel"` `inputMode="tel"` `autoComplete="tel"`.
30
+ - Montos o identificadores numéricos de texto libre → `inputMode="numeric"`, sin
31
+ `type="number"` (que introduce spinners y bloquea el formateo visual).
32
+ - Para campos con formato visual (RUT, teléfono, montos), aplica la máscara en el
33
+ `onChange` pero persiste siempre el valor limpio o normalizado, no el string
34
+ formateado. Ejemplo genérico:
35
+
36
+ ```tsx
37
+ function handleChange(raw: string) {
38
+ const formatted = formatForDisplay(raw);
39
+ setDisplayValue(formatted);
40
+ setCleanValue(clean(raw));
41
+ }
42
+ ```
43
+
44
+ - Valida en el boundary con un schema (p. ej. zod), no con lógica inline dentro del
45
+ componente. El componente muestra el error; no decide si el valor es válido.
46
+
47
+ ## 3. Accesibilidad y estados de foco
48
+
49
+ - Todo botón icon-only lleva `aria-label` (dinámico si el estado que representa cambia,
50
+ p. ej. "Expandir menú" / "Minimizar menú").
51
+ - Todo campo tiene `label` asociado con `htmlFor` + `id` (usa un generador de id estable,
52
+ como `useId()`, en componentes reutilizables para no colisionar entre instancias).
53
+ - Los errores de un campo se asocian con `aria-invalid` + `aria-describedby` apuntando al
54
+ id del mensaje de error, y el mensaje lleva `role="alert"` o `role="status"` para que un
55
+ lector de pantalla lo anuncie.
56
+ - El anillo de foco usa el token de foco del proyecto y se dispara con `focus-visible`, no
57
+ con `focus:` a secas (que también dispara con click de mouse) ni con un color
58
+ hardcodeado.
59
+ - Los overlays (modal, dropdown, dialog) cierran con Escape, atrapan el foco dentro
60
+ mientras están abiertos, y restauran el foco al elemento que los abrió al cerrarse.
61
+ - Los SVG puramente decorativos llevan `aria-hidden="true"`.
62
+ - Los colores se aplican por token semántico del proyecto, nunca como clase de color cruda
63
+ (`gray-*`, `red-*`) ni hex suelto en el JSX.
64
+
65
+ ## 4. Manejo de errores y carga
66
+
67
+ - Las rutas con carga asíncrona muestran un skeleton fiel al layout real (mismo header,
68
+ mismas filas o secciones), nunca un spinner de página completa.
69
+ - Durante el envío de un formulario: el botón queda `disabled`, muestra un spinner, y su
70
+ label cambia a gerundio ("Enviando…", "Guardando…").
71
+ - Los errores de formulario se muestran inline junto al campo que falló, no solo en un
72
+ banner genérico, y se anuncian por ARIA. En formularios largos, mueve el foco al primer
73
+ mensaje de error tras un submit fallido.
74
+ - Nunca silencies un error de red o de submit: siempre mapéalo a un mensaje legible para
75
+ el usuario y regístralo (`console.error` o equivalente). Un `catch` vacío o que solo
76
+ cambia estado sin loguear es un antipatrón.
77
+ - Los estados vacíos (empty states) son un componente dedicado con copy accionable, no un
78
+ `null` o una tabla sin filas sin explicación.
79
+ - Los estados de éxito son una vista o tarjeta dedicada, no solo la desaparición del
80
+ formulario.
81
+ - Toda ruta que hace fetching que puede fallar tiene un error boundary (`error.tsx` o
82
+ equivalente del framework) con una acción de reintento.
83
+
84
+ ## 5. Microinteracciones
85
+
86
+ - El hover y el active de un control viven en la primitiva (el componente base), no se
87
+ duplican en cada lugar que lo usa.
88
+ - `prefers-reduced-motion` se respeta siempre: si la animación es CSS, usa el prefijo o
89
+ gate correspondiente (`motion-safe:` o un override global en `prefers-reduced-motion:
90
+ reduce`); si la animación es JS/imperativa, verifica la preferencia del sistema antes de
91
+ animar (equivalente a `useReducedMotion()`).
92
+ - El cierre animado de un overlay dispara el `onClose` real cuando termina la animación de
93
+ salida (`onAnimationEnd` o equivalente), nunca antes — si no, el contenido desaparece
94
+ antes de que termine la transición.
95
+ - Para acciones tipo "copiar al portapapeles": intercambia el label a un estado de
96
+ confirmación ("¡Copiado!") por ~2 segundos y envuelve la llamada al portapapeles en
97
+ `try/catch` (el acceso al portapapeles puede estar bloqueado por el navegador).
98
+ - Las animaciones en cascada (listas, grids) usan un delay proporcional al índice del
99
+ elemento, no todos animan al mismo tiempo.
100
+ - Los keyframes nuevos se declaran en el theme o en el CSS global del proyecto, no inline
101
+ por componente.
102
+
103
+ ## 6. Región: Chile (es-CL)
104
+
105
+ > Si el proyecto no tiene helpers propios de RUT/teléfono/moneda, usa o instala
106
+ > `@devground/chile-formats` en vez de reimplementarlos; si ya tiene un helper, respétalo.
107
+
108
+ - **RUT**: usa un único helper de formateo (puntos y guion) y de validación (módulo 11);
109
+ aplica la máscara en el `onChange` pero persiste siempre el valor limpio, sin puntos ni
110
+ guion. El mensaje de error debe mencionar el dígito verificador ("RUT inválido, verifica
111
+ el dígito verificador"). Nunca reimplementes el algoritmo de módulo 11 dentro de un
112
+ componente.
113
+ - **Teléfono**: muestra el formato visual `+56 9 XXXX XXXX`, con `inputMode="tel"` y
114
+ `autoComplete="tel"`; normaliza el valor antes de persistirlo (no guardes el string tal
115
+ como el usuario lo tipeó).
116
+ - **Moneda, UF y fechas**: usa `Intl.NumberFormat("es-CL")` e `Intl.DateTimeFormat("es-CL")`
117
+ centralizados en un único helper compartido del proyecto. Nunca uses `toLocaleString` ad
118
+ hoc dentro de un componente, ni reimplementes el formateador en más de un lugar.
@@ -0,0 +1,38 @@
1
+ # Prompt de minado — convenciones de UI de un repo
2
+
3
+ Este prompt es reutilizable: pégalo en cualquier repo cuando quieras generar su overlay de
4
+ convenciones de UI (`docs/ui-conventions.md`), el archivo que la skill `ui-conventions`
5
+ carga después con precedencia sobre la capa base.
6
+
7
+ ---
8
+
9
+ Actúa como un auditor de convenciones de interfaz. Recorre el frontend del repo actual (no
10
+ solo el README) y extrae los patrones recurrentes reales del código, organizados en estas
11
+ categorías:
12
+
13
+ 1. Componentes propios vs. primitivas del navegador.
14
+ 2. Formateo de inputs.
15
+ 3. Accesibilidad y estados de foco.
16
+ 4. Manejo de errores y carga.
17
+ 5. Microinteracciones.
18
+ 6. Regional: Chile (es-CL) si aplica — RUT, teléfono, moneda/UF, fechas.
19
+
20
+ Para cada patrón que documentes:
21
+
22
+ - Escribe la regla como un enunciado imperativo, no como una descripción.
23
+ - Cita un ejemplo correcto real del repo con `ruta/al/archivo.ext:línea`.
24
+ - Cita un antipatrón real si existe, con su propia ruta:línea. Los antipatrones citados son
25
+ deuda conocida documentada, no licencia para repetirlos en código nuevo.
26
+
27
+ Verifica el stack real contra el código (versión de framework, versión de Tailwind,
28
+ sistema de componentes, si hay toaster o librería de motion) — no confíes en el README ni
29
+ en archivos de configuración desactualizados si el código dice otra cosa.
30
+
31
+ Genera `docs/ui-conventions.md` en la raíz del repo auditado, usando el formato de
32
+ `references/overlay-template.md` de esta skill: mismo orden de secciones que la base
33
+ (0 a 6), mismo formato de patrón (Regla · Ejemplo correcto · Antipatrón), y cerrando con
34
+ una tabla de deuda conocida.
35
+
36
+ Ese archivo (`docs/ui-conventions.md`) es el overlay que la skill `ui-conventions` cargará
37
+ automáticamente la próxima vez que se genere o edite UI en este repo, y sus reglas
38
+ concretas prevalecerán sobre la capa base genérica en caso de conflicto.
@@ -0,0 +1,52 @@
1
+ # Convenciones de interfaz — [nombre del proyecto]
2
+
3
+ > Lo definido en este archivo sustituye a la regla genérica de `base.md` en caso de
4
+ > conflicto.
5
+
6
+ **Stack:** [framework + versión] · [versión de Tailwind u otro CSS] · [sistema de
7
+ componentes: shadcn, design system propio, ninguno] · [toast: sí/no, cuál] · [motion:
8
+ librería o CSS puro, ninguna]
9
+
10
+ ## 1. Componentes propios vs. primitivas del navegador
11
+
12
+ - **Regla:** [enunciado imperativo]
13
+ **Ejemplo correcto:** `ruta/al/archivo.ext:línea`
14
+ **Antipatrón:** [qué evita]
15
+
16
+ ## 2. Formateo de inputs
17
+
18
+ - **Regla:** [enunciado imperativo]
19
+ **Ejemplo correcto:** `ruta/al/archivo.ext:línea`
20
+ **Antipatrón:** [qué evita]
21
+
22
+ ## 3. Accesibilidad y estados de foco
23
+
24
+ - **Regla:** [enunciado imperativo]
25
+ **Ejemplo correcto:** `ruta/al/archivo.ext:línea`
26
+ **Antipatrón:** [qué evita]
27
+
28
+ ## 4. Manejo de errores y carga
29
+
30
+ - **Regla:** [enunciado imperativo]
31
+ **Ejemplo correcto:** `ruta/al/archivo.ext:línea`
32
+ **Antipatrón:** [qué evita]
33
+
34
+ ## 5. Microinteracciones
35
+
36
+ - **Regla:** [enunciado imperativo]
37
+ **Ejemplo correcto:** `ruta/al/archivo.ext:línea`
38
+ **Antipatrón:** [qué evita]
39
+
40
+ ## 6. Regional: Chile (es-CL)
41
+
42
+ - **Regla:** [enunciado imperativo]
43
+ **Ejemplo correcto:** `ruta/al/archivo.ext:línea`
44
+ **Antipatrón:** [qué evita]
45
+
46
+ > Helper preferente si el repo no tiene uno propio: @devground/chile-formats.
47
+
48
+ ## Deuda conocida
49
+
50
+ | Deuda | Ubicación | Patrón correcto |
51
+ | --- | --- | --- |
52
+ | [descripción] | `ruta/al/archivo.ext:línea` | [qué debería usarse en su lugar] |