@higinio/design-mcp 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 +17 -0
- package/README.md +164 -0
- package/assets/celeste/design-doc.md +842 -0
- package/assets/celeste/framework.css +914 -0
- package/assets/celeste/styleguide.html +3060 -0
- package/assets/celeste/tailwind.css +117 -0
- package/assets/celeste/tokens.css +137 -0
- package/assets/celeste/tokens.json +1013 -0
- package/assets/nocturna/design-doc.md +857 -0
- package/assets/nocturna/framework.css +992 -0
- package/assets/nocturna/styleguide.html +2714 -0
- package/assets/nocturna/tailwind.css +115 -0
- package/assets/nocturna/tokens.css +145 -0
- package/assets/nocturna/tokens.json +952 -0
- package/dist/cli.js +269 -0
- package/dist/data.js +33 -0
- package/dist/doc.js +52 -0
- package/dist/http.js +64 -0
- package/dist/index.js +39 -0
- package/dist/motion.js +142 -0
- package/dist/preflight.js +181 -0
- package/dist/server.js +219 -0
- package/dist/slop.js +257 -0
- package/dist/snippet.js +54 -0
- package/dist/systems.js +21 -0
- package/dist/tokens.js +76 -0
- package/dist/types.js +1 -0
- package/dist/validate.js +135 -0
- package/package.json +37 -0
|
@@ -0,0 +1,857 @@
|
|
|
1
|
+
# DESIGN.md — Sistema de Diseño "Nocturna Eléctrica"
|
|
2
|
+
|
|
3
|
+
> Sistema de marca **dark-first**, enérgico y de alto contraste, de **Higinio Sosa** para todos sus productos de software (web, móvil, dashboards, landing pages).
|
|
4
|
+
> Paleta base: `#52baed` (celeste eléctrico) · `#353d7c` (índigo profundo) · `#010203` (negro nocturno)
|
|
5
|
+
>
|
|
6
|
+
> **Versión 2.0** — sincronizado con el proyecto [Nocturna Eléctrica Design System](https://claude.ai/design/p/1a3a2fb6-b02c-481c-96fc-017d50ec9ce7) de claude.ai/design.
|
|
7
|
+
> Entregables: `nocturna-electrica.html` (style guide), `dist/nocturna-electrica/` (tokens CSS · Tailwind v4 · DTCG JSON), skill `~/.claude/skills/nocturna-electrica-design/`.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Identidad y principios
|
|
12
|
+
|
|
13
|
+
**Personalidad de marca:** moderna, técnica, con energía contenida. El fondo casi negro es el escenario; el celeste eléctrico es el reflector.
|
|
14
|
+
|
|
15
|
+
1. **Contraste con propósito** — El acento `#52baed` solo aparece donde hay acción o dato clave.
|
|
16
|
+
2. **Profundidad por luz** — La elevación se comunica aclarando superficies, no oscureciendo bordes.
|
|
17
|
+
3. **Energía contenida** — Paleta vibrante, espaciado disciplinado: nunca ruidosa.
|
|
18
|
+
4. **Datos protagonistas** — Cifras y gráficas brillan; el chrome de la UI se mantiene discreto.
|
|
19
|
+
5. **Movimiento con propósito** — La animación explica cambios de estado, nunca decora sola.
|
|
20
|
+
|
|
21
|
+
**Regla de oro de implementación:** los componentes consumen **solo tokens semánticos** (`--surface-1`, `--text-primary`, `--accent-400`…), nunca hex directos. El theme switch es un cambio de atributo, no un refactor.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 2. Voz y contenido
|
|
26
|
+
|
|
27
|
+
- **Idioma:** español, técnico pero cercano. Tuteo implícito; sin formalismos.
|
|
28
|
+
- **Tono:** directo, preciso, con energía contenida. Frases cortas y declarativas: *"El fondo casi negro es el escenario; el celeste eléctrico es el reflector."*
|
|
29
|
+
- **Casing:** sentence case en headings y botones ("Crear proyecto", no "Crear Proyecto" ni "CREAR PROYECTO"). Overlines/labels en uppercase con tracking `+0.08em`.
|
|
30
|
+
- **Copy de botones:** verbo + objeto, corto: "Guardar cambios", "Ver informe".
|
|
31
|
+
- **Mensajes de estado:** concretos y accionables — el error dice qué pasó **y qué hacer**.
|
|
32
|
+
- **Números y datos:** protagonistas — siempre en JetBrains Mono o `tabular-nums`, grandes en dashboards, con count-up al montar.
|
|
33
|
+
- **Fechas y números formateados:** siempre con `Intl.DateTimeFormat` / `Intl.NumberFormat` (`es-MX`), nunca formatos hardcodeados — un "12/07/2026" a mano se rompe al cambiar de locale.
|
|
34
|
+
- **`translate="no"`** en nombres de marca y tokens de código (IDs, comandos, rutas), para que la traducción automática no los corrompa.
|
|
35
|
+
- **Numerales para conteos:** "8 despliegues", no "ocho despliegues" — en un dashboard el dígito se escanea más rápido.
|
|
36
|
+
- **Emoji:** no se usan en la UI. El estado se comunica con color + icono + texto.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 3. Paleta de colores
|
|
41
|
+
|
|
42
|
+
### 3.1 Acento — Celeste Eléctrico (base `#52baed`)
|
|
43
|
+
|
|
44
|
+
| Token | Hex | Uso |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `accent-100` | `#d9f0fb` | Texto sobre fondos accent oscuros |
|
|
47
|
+
| `accent-200` | `#a8ddf6` | Hover de links |
|
|
48
|
+
| `accent-300` | `#7dccf1` | Iconos activos, gráficas secundarias, código inline |
|
|
49
|
+
| `accent-400` | `#52baed` | **Acento principal** — CTAs, links, focus, datos clave |
|
|
50
|
+
| `accent-500` | `#2fa3dd` | Hover de botones de acento |
|
|
51
|
+
| `accent-600` | `#1f87bb` | Pressed |
|
|
52
|
+
| `accent-700` | `#186a94` | Bordes de elementos activos |
|
|
53
|
+
|
|
54
|
+
### 3.2 Secundario — Índigo Profundo (base `#353d7c`)
|
|
55
|
+
|
|
56
|
+
| Token | Hex | Uso |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `indigo-300` | `#7a83c4` | Texto/iconos secundarios de marca |
|
|
59
|
+
| `indigo-400` | `#565fa3` | Gráficas, tags, iconos de estados vacíos |
|
|
60
|
+
| `indigo-500` | `#353d7c` | **Secundario** — botones secundarios, secciones destacadas |
|
|
61
|
+
| `indigo-600` | `#2b3264` | Hover del secundario, borde de avatar |
|
|
62
|
+
| `indigo-700` | `#22284f` | Fondos de banners/tarjetas destacadas, fondo de avatar |
|
|
63
|
+
| `indigo-800` | `#1a1e3b` | Fondos sutiles de marca, sidebar activo |
|
|
64
|
+
|
|
65
|
+
### 3.3 Neutrales — Escala Nocturna (derivados de `#010203`)
|
|
66
|
+
|
|
67
|
+
| Token | Hex | Uso |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `night-950` | `#010203` | **Fondo base** |
|
|
70
|
+
| `night-900` | `#0b0d12` | Superficie 1 (tarjetas) |
|
|
71
|
+
| `night-800` | `#14171f` | Superficie 2 (modales, dropdowns, inputs) |
|
|
72
|
+
| `night-700` | `#1e222d` | Superficie 3, hover de filas |
|
|
73
|
+
| `night-600` | `#2a2f3d` | Bordes destacados (énfasis) |
|
|
74
|
+
| `night-500` | `#3d4354` | Bordes por defecto |
|
|
75
|
+
| `night-400` | `#6b7183` | Deshabilitado, placeholders |
|
|
76
|
+
| `night-300` | `#9aa0b0` | Texto terciario, captions |
|
|
77
|
+
| `night-200` | `#c5c9d4` | Texto secundario |
|
|
78
|
+
| `night-100` | `#e8eaef` | Texto principal |
|
|
79
|
+
| `night-50` | `#f6f7f9` | Máximo énfasis, headings |
|
|
80
|
+
|
|
81
|
+
### 3.4 Semánticos (calibrados para fondo oscuro)
|
|
82
|
+
|
|
83
|
+
| Token | Hex | Fondo suave | Uso |
|
|
84
|
+
|---|---|---|---|
|
|
85
|
+
| `success-400` | `#4ade80` | `#0a2617` | Confirmaciones, estados positivos |
|
|
86
|
+
| `warning-400` | `#fbbf24` | `#2a2005` | Advertencias |
|
|
87
|
+
| `danger-400` | `#f87171` | `#2c0f0f` | Errores, acciones destructivas |
|
|
88
|
+
| `info-400` | `#52baed` | `#0d1f2c` | Informativos (reutiliza acento) |
|
|
89
|
+
|
|
90
|
+
### 3.5 Capa de aliases semánticos
|
|
91
|
+
|
|
92
|
+
Los componentes **nunca** usan los primitivos de arriba directamente: consumen esta capa. Así el theme switch (§15) y cualquier rebranding son cambios de una sola línea.
|
|
93
|
+
|
|
94
|
+
| Alias | Valor | Rol |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `--bg` | `night-950` | Fondo de la app |
|
|
97
|
+
| `--surface-1` | `night-900` | Tarjetas en reposo |
|
|
98
|
+
| `--surface-2` | `night-800` | Modales, dropdowns, inputs |
|
|
99
|
+
| `--surface-3` | `night-700` | Hover de filas, superficie máxima |
|
|
100
|
+
| `--border` | `night-500` | Borde por defecto |
|
|
101
|
+
| `--border-strong` | `night-600` | Borde de énfasis (elevación 3, accordion) |
|
|
102
|
+
| `--text-primary` | `night-100` | Texto principal |
|
|
103
|
+
| `--text-secondary` | `night-200` | Texto secundario |
|
|
104
|
+
| `--text-muted` | `night-300` | Terciario, captions |
|
|
105
|
+
| `--text-disabled` | `night-400` | Deshabilitado, placeholder |
|
|
106
|
+
| `--text-heading` | `night-50` | Headings, máximo énfasis |
|
|
107
|
+
| `--text-on-accent` | `night-950` | Texto sobre CTAs de acento (**oscuro, nunca blanco**) |
|
|
108
|
+
| `--accent-ghost-hover` | `rgba(82,186,237,.08)` | Hover de botones ghost |
|
|
109
|
+
| `--accent-ghost-active` | `rgba(82,186,237,.14)` | Active de botones ghost |
|
|
110
|
+
| `--accent-selected` | `rgba(82,186,237,.12)` | Filas/opciones seleccionadas |
|
|
111
|
+
| `--overlay` | `rgba(1,2,3,.7)` | Overlay de modal |
|
|
112
|
+
| `--success/--warning/--danger/--info` | sus `-400` | Estados semánticos |
|
|
113
|
+
|
|
114
|
+
### 3.6 Proporción de uso (regla 60-30-10 adaptada a dark)
|
|
115
|
+
|
|
116
|
+
- **60% — Fondos nocturnos** (`night-950/900/800`): el escenario oscuro.
|
|
117
|
+
- **30% — Texto y estructura** (`night-50→300`, bordes, índigo en fondos de sección).
|
|
118
|
+
- **10% — Acento eléctrico y semánticos**: CTAs, links, cifras clave, gráficas. (Excepción: una visualización de datos multiserie usa la paleta categórica validada de la skill `dataviz-marca`, exenta de este techo — son datos, no cromo decorativo.)
|
|
119
|
+
|
|
120
|
+
Reglas prácticas:
|
|
121
|
+
- El celeste `accent-400` es **el único color que grita**: un CTA primario por pantalla.
|
|
122
|
+
- El índigo es el "30% de marca": fondos de hero, sidebars, estados seleccionados — nunca compite con el acento y **nunca es texto sobre negro** (contraste 1.9:1).
|
|
123
|
+
- Evitar blanco puro `#ffffff` en texto masivo; usar `night-100`.
|
|
124
|
+
- **Fondos siempre planos:** sin imágenes de fondo, sin texturas, sin gradientes decorativos. El único "brillo" permitido es el glow de acento (§7).
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 4. Tipografía
|
|
129
|
+
|
|
130
|
+
### 4.1 Familias
|
|
131
|
+
|
|
132
|
+
| Rol | Fuente | Fallback | Uso |
|
|
133
|
+
|---|---|---|---|
|
|
134
|
+
| Display | **Space Grotesk** (500–700) | system-ui, sans-serif | **Solo h1–h3**, cifras hero, navegación, wordmark |
|
|
135
|
+
| Body/UI | **Manrope** (400–700) | system-ui, sans-serif | Párrafos, formularios, tablas, **h4 en adelante** |
|
|
136
|
+
| Mono | **JetBrains Mono** (400–500) | monospace | Datos numéricos, código, IDs |
|
|
137
|
+
|
|
138
|
+
Carga vía Google Fonts CDN (sin binarios adjuntos — si hay licencias locales, sustituir por `@font-face` con los mismos `font-family`):
|
|
139
|
+
|
|
140
|
+
```css
|
|
141
|
+
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Manrope:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 4.2 Escala (ratio 1.25, base 16px, en `rem`)
|
|
145
|
+
|
|
146
|
+
| Token | Tamaño | Line-height (token) | Peso | Uso |
|
|
147
|
+
|---|---|---|---|---|
|
|
148
|
+
| `text-xs` | 12px | 1.5 `--leading-snug` | 500 | Labels, overlines |
|
|
149
|
+
| `text-sm` | 14px | 1.5 `--leading-snug` | 400 | Texto secundario, tablas |
|
|
150
|
+
| `text-base` | 16px | 1.6 `--leading-body` | 400 | Párrafos, inputs |
|
|
151
|
+
| `text-lg` | 18px | 1.55 | 500 | Lead |
|
|
152
|
+
| `text-xl` | 20px | 1.4 `--leading-h4` | 600 | h4 (Manrope) |
|
|
153
|
+
| `text-2xl` | 25px | 1.3 `--leading-h3` | 600 | h3 (Space Grotesk) |
|
|
154
|
+
| `text-3xl` | 31px | 1.2 `--leading-h2` | 700 | h2 (Space Grotesk) |
|
|
155
|
+
| `text-4xl` | 39px | 1.15 `--leading-heading` | 700 | h1 (Space Grotesk) |
|
|
156
|
+
| `text-5xl` | 49px | 1.05 `--leading-tight` | 700 | Cifras hero |
|
|
157
|
+
|
|
158
|
+
### 4.3 Reglas
|
|
159
|
+
|
|
160
|
+
- En dark el texto claro "engorda" ópticamente: preferir peso **400** donde en light usarías 500.
|
|
161
|
+
- Letter-spacing tokenizado: `--tracking-display: -0.02em` en display; `--tracking-overline: +0.08em` en overlines/labels uppercase.
|
|
162
|
+
- Números: `tabular-nums` o JetBrains Mono.
|
|
163
|
+
- Longitud de línea: `--measure: 65ch` (60–75 caracteres). Mínimo absoluto 12px.
|
|
164
|
+
- `::selection`: `rgba(82,186,237,.3)`. Placeholder: `night-400`.
|
|
165
|
+
|
|
166
|
+
### 4.4 Jerarquía y craft (Butterick)
|
|
167
|
+
|
|
168
|
+
- **Máx. 3 niveles de heading visualmente distintos por vista.** La escala define tamaños para h1–h4, pero en una misma pantalla no uses más de tres niveles claramente diferenciados; el exceso aplana la jerarquía.
|
|
169
|
+
- **Jerarquía por espacio y peso, no solo por tamaño.** Deja más espacio arriba de un heading que abajo (lo ata a su contenido). Distingue niveles con espacio y peso, no solo con puntos de tamaño.
|
|
170
|
+
- **Comillas y apóstrofos curvos** (« », " ", ' '), nunca rectos (`"`, `'`). Un solo espacio tras el punto seguido (nunca doble, herencia de máquina de escribir).
|
|
171
|
+
- **Interlineado generoso en cuerpo de texto: 150–160%** (ya son los tokens `--leading-body`/`--leading-snug`, §4.2); no bajes de 130% ni en texto denso (tablas, captions).
|
|
172
|
+
- **En dash (–) para rangos y series** ("10–20 páginas", "lun–vie"); **em dash (—) solo para incisos**, con moderación (la skill `anti-slop-diseno` marca >2 por bloque como cliché de IA).
|
|
173
|
+
- **Kerning activado** (`font-kerning: normal`); ligaduras por defecto.
|
|
174
|
+
- En JSX/TSX y en strings usa el carácter UTF-8 real (`'`, `"`), no escapes ni entidades (`’`, `’`), que no se decodifican igual en todos los contextos.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 5. Espaciado
|
|
179
|
+
|
|
180
|
+
Escala base 4px — tokens `--space-1…24`: `4 · 8 · 12 · 16 · 20 · 24 · 32 · 40 · 48 · 64 · 80 · 96`
|
|
181
|
+
|
|
182
|
+
Aliases de uso (consumir estos, no números sueltos):
|
|
183
|
+
|
|
184
|
+
| Alias | Valor | Uso |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| `--card-padding` | 24px | Padding de tarjetas (`--card-padding-mobile: 16px`) |
|
|
187
|
+
| `--section-gap` | 64px | Separación entre secciones (el dark agradece más aire) |
|
|
188
|
+
| `--form-gap` | 20px | Gap entre campos de formulario |
|
|
189
|
+
| `--button-padding` | 12px 20px | Padding de botones md |
|
|
190
|
+
| `--touch-target` | 44px | Altura mínima interactiva |
|
|
191
|
+
| `--grid-gutter` | 24px | Gutter del grid (`--grid-gutter-mobile: 16px`) |
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 6. Bordes, radios y focus
|
|
196
|
+
|
|
197
|
+
| Token | Valor | Uso |
|
|
198
|
+
|---|---|---|
|
|
199
|
+
| `radius-sm` | 8px | Inputs, chips |
|
|
200
|
+
| `radius-md` | 12px | Botones, dropdowns, IconButton |
|
|
201
|
+
| `radius-lg` | 16px | Tarjetas, modales, accordion |
|
|
202
|
+
| `radius-xl` | 24px | Hero, contenedores destacados |
|
|
203
|
+
| `radius-full` | 9999px | Avatares, pills, progress |
|
|
204
|
+
|
|
205
|
+
Tokens compuestos:
|
|
206
|
+
|
|
207
|
+
```css
|
|
208
|
+
--border-default: 1px solid var(--night-500);
|
|
209
|
+
--border-emphasis: 1px solid var(--night-600);
|
|
210
|
+
--focus-ring: 2px solid var(--accent-400);
|
|
211
|
+
--focus-ring-offset: 2px;
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Focus global: `:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); }` — se aplica a todo interactivo, solo con teclado (el click de ratón no lo dispara).
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 7. Elevación (superficie + sombra + borde)
|
|
219
|
+
|
|
220
|
+
En fondo casi negro las sombras solas no bastan: la elevación combina **superficie más clara + sombra + borde sutil**.
|
|
221
|
+
|
|
222
|
+
| Nivel | Superficie | Sombra | Uso |
|
|
223
|
+
|---|---|---|---|
|
|
224
|
+
| 0 | `night-950` | — | Fondo de la app |
|
|
225
|
+
| 1 | `night-900` | `--shadow-1: 0 1px 2px rgba(0,0,0,.4)` | Tarjetas en reposo |
|
|
226
|
+
| 2 | `night-800` | `--shadow-2: 0 4px 12px rgba(0,0,0,.5)` | Dropdowns, hover |
|
|
227
|
+
| 3 | `night-800` + borde `night-600` | `--shadow-3: 0 12px 32px rgba(0,0,0,.6)` | Modales, popovers |
|
|
228
|
+
|
|
229
|
+
Tokens adicionales:
|
|
230
|
+
|
|
231
|
+
```css
|
|
232
|
+
--glow-accent: 0 0 0 1px rgba(82,186,237,.35), 0 4px 20px rgba(82,186,237,.18);
|
|
233
|
+
--ring: 0 0 0 3px rgba(82,186,237,.2); /* focus de inputs */
|
|
234
|
+
--ring-danger: 0 0 0 3px rgba(248,113,113,.2); /* focus en estado error */
|
|
235
|
+
--overlay: rgba(1,2,3,.7); /* overlay de modal */
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Glow de acento:** solo interactivos clave, **máximo uno por pantalla**.
|
|
239
|
+
**Transparencia y blur:** únicamente en navbar (`night-950` al 80% + `backdrop-filter: blur(12px)`) y overlay de modal (+ blur 4px). En ningún otro sitio.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 8. Iconografía
|
|
244
|
+
|
|
245
|
+
- **Set:** [Lucide](https://lucide.dev) como sustituto *(flagged: no hay set propio de marca — si existe, reemplazar)*. Trazo 2px, esquinas redondeadas, estilo técnico-limpio.
|
|
246
|
+
- **Componente `Icon`:** wrapper SVG viewBox 24, `stroke-width: 2`, caps/joins redondeados, `fill: none`, tamaño default 20px, color `currentColor`. Decorativo → `aria-hidden="true"`; con `aria-label` → `role="img"`. Set curado: home, chart, bell, gear, search, plus, check, x, copy, folder, chevronDown, arrowRight, user, bolt.
|
|
247
|
+
- **Tamaños:** 20px en botones (gap 8px) · 18px leading en inputs · 16px en chevrons/metadatos.
|
|
248
|
+
- **Color:** los iconos heredan el color del texto; activos en `accent-300/400`; estados vacíos en `indigo-400`.
|
|
249
|
+
- **Alineación óptica:** los iconos asimétricos (play, flechas, chevrons) se centran a ojo, no geométricamente — el centro matemático engaña con formas que pesan hacia un lado. Ajustar 1–2px a mano hasta que se vea centrado.
|
|
250
|
+
- Sin emoji, sin unicode-como-icono.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 9. Marca / Wordmark
|
|
255
|
+
|
|
256
|
+
No hay logo dibujado — el wordmark es puramente tipográfico:
|
|
257
|
+
|
|
258
|
+
- **"higinio.sosa"** en Space Grotesk 700, tracking `-0.02em`, color `--text-heading` (`night-50`), con el **punto separador en `accent-400`**. Tamaño de referencia: 31px (`text-3xl`).
|
|
259
|
+
- **Variante badge:** mismo texto a 22px dentro de caja con fondo `indigo-800`, borde `1px indigo-600`, radius `md` (12px), padding `14px 22px`.
|
|
260
|
+
- Donde un producto necesite logotipo, se renderiza el nombre del producto en `--font-display`; nunca se dibuja un logo aproximado.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## 10. Biblioteca de componentes
|
|
265
|
+
|
|
266
|
+
### 10.1 Botones
|
|
267
|
+
|
|
268
|
+
Altura: 44px (md), 36px (sm), 52px (lg). Radius `md`. Peso 600.
|
|
269
|
+
|
|
270
|
+
| Variante | Reposo | Hover | Active | Disabled |
|
|
271
|
+
|---|---|---|---|---|
|
|
272
|
+
| Primario | fondo `accent-400`, **texto `night-950`** (no blanco) | `accent-500` + glow suave | `accent-600`, sin glow | fondo `night-700`, texto `night-400` |
|
|
273
|
+
| Secundario | fondo `indigo-500`, texto `night-50` | `indigo-600` | `indigo-700` | opacidad 50% |
|
|
274
|
+
| Outline | borde `night-500`, texto `night-100` | fondo `night-800`, borde `night-400` | fondo `night-700` | opacidad 50% |
|
|
275
|
+
| Ghost | texto `accent-400` | fondo `--accent-ghost-hover` | fondo `--accent-ghost-active` | texto `night-400` |
|
|
276
|
+
| Destructivo | fondo `danger-400`, texto `night-950` | oscurecer 8% | oscurecer 14% | opacidad 50% |
|
|
277
|
+
|
|
278
|
+
- Con icono: gap 8px, icono 20px.
|
|
279
|
+
- Loading: spinner 16px, botón conserva ancho (`min-width`).
|
|
280
|
+
|
|
281
|
+
### 10.2 IconButton
|
|
282
|
+
|
|
283
|
+
Botón cuadrado solo-icono. **Requiere `label` accesible** (`aria-label` + `title`). Tamaños sm/md/lg = 36/44/52px, radius `md`.
|
|
284
|
+
|
|
285
|
+
| Variante | Reposo | Hover | Active |
|
|
286
|
+
|---|---|---|---|
|
|
287
|
+
| Outline | transparente, texto `night-200`, borde `night-500` | fondo `night-800`, borde `night-400` | fondo `night-700` |
|
|
288
|
+
| Ghost | transparente, texto `night-300` | fondo `night-800`, texto `night-100` | fondo `night-700` |
|
|
289
|
+
| Accent | texto `accent-400`, sin borde | fondo `--accent-ghost-hover` | fondo `--accent-ghost-active` |
|
|
290
|
+
|
|
291
|
+
Disabled: opacidad 50% + `cursor: not-allowed`. Press: `scale(0.98)` 100ms. Transición de fondo 150ms.
|
|
292
|
+
|
|
293
|
+
### 10.3 Spinner
|
|
294
|
+
|
|
295
|
+
Indicador de carga circular; también sustituye el label del botón en estado loading (ver §10.1).
|
|
296
|
+
|
|
297
|
+
- 16px por defecto (demo también a 24px y 32px para contextos con más peso visual).
|
|
298
|
+
- Trazo `2px solid` con 25% de opacidad (`color-mix(in srgb, currentColor 25%, transparent)`) + cuarto superior en `currentColor` sólido, radius `full`.
|
|
299
|
+
- Color default `accent-400`; hereda `currentColor` — `night-950` dentro de botones primario/destructivo, `night-300` en contextos neutros.
|
|
300
|
+
- Rotación `0.8s linear infinite`.
|
|
301
|
+
- Cargas <300ms: no mostrar spinner (evita parpadeo).
|
|
302
|
+
|
|
303
|
+
### 10.4 Input
|
|
304
|
+
|
|
305
|
+
- Fondo `night-800`, borde `1px night-500`, radius `sm`, altura 44px, padding `0 14px`, texto `night-100`.
|
|
306
|
+
- **Label:** `text-sm` peso 500 `night-200`, margen inferior 6px. Siempre visible (nunca placeholder-como-label).
|
|
307
|
+
- **Placeholder:** `night-400`.
|
|
308
|
+
- **Hover:** borde `night-400`.
|
|
309
|
+
- **Focus:** borde `accent-400` + `--ring`. Transición 150ms.
|
|
310
|
+
- **Error:** borde `danger-400` + `--ring-danger`; mensaje `text-sm` `danger-400` con icono.
|
|
311
|
+
- **Éxito:** borde `success-400` + icono check.
|
|
312
|
+
- **Disabled:** fondo `night-900`, texto `night-400`, cursor `not-allowed`.
|
|
313
|
+
- **Ayuda:** `text-sm` `night-300` debajo.
|
|
314
|
+
- Icono leading: padding-left 40px, icono 18px `night-400`.
|
|
315
|
+
- **`inputmode` según el dato** (`numeric`, `email`, `tel`, `decimal`) para el teclado correcto en móvil; `spellcheck="false"` en emails, códigos y usernames.
|
|
316
|
+
- **Nunca bloquear el pegado** (`onpaste`): rompe gestores de contraseñas y códigos de verificación.
|
|
317
|
+
- **Al enviar con errores,** mover el foco al primer campo inválido (no solo pintarlo de rojo).
|
|
318
|
+
|
|
319
|
+
### 10.5 Textarea
|
|
320
|
+
|
|
321
|
+
Mismos estados que input. Min-height 96px, `resize: vertical`. Contador `text-xs` `night-400` a la derecha.
|
|
322
|
+
|
|
323
|
+
### 10.6 Select
|
|
324
|
+
|
|
325
|
+
- Trigger: como input + chevron 16px `night-300` (rota 180° al abrir, 200ms).
|
|
326
|
+
- Menú: fondo `night-800`, borde `night-600`, radius `md`, sombra nivel 3, padding 6px, max-height 320px.
|
|
327
|
+
- Opción: padding `10px 12px`, radius `sm`, hover fondo `night-700`, seleccionada fondo `--accent-selected` + texto `accent-300` + check.
|
|
328
|
+
- Entrada: fade + translateY(-4px→0), 180ms.
|
|
329
|
+
|
|
330
|
+
### 10.7 SegmentedControl
|
|
331
|
+
|
|
332
|
+
Selector tipo pill de **2–4 opciones mutuamente excluyentes** (cambio de vista, filtros rápidos). Para más de 4 opciones o labels largos, usar `Select`.
|
|
333
|
+
|
|
334
|
+
- Contenedor: fondo `night-800`, borde `1px night-500`, radius `md`, padding 3px. Altura 44px (md) / 36px (sm).
|
|
335
|
+
- Opción activa: fondo `night-700`, texto `accent-300` peso 600, sombra nivel 1; transición de fondo/color/sombra 220ms ease-out.
|
|
336
|
+
- Opción inactiva: texto `night-200` peso 500, `text-sm` (`text-xs` en sm).
|
|
337
|
+
- Focus: ring en la opción enfocada, igual que inputs.
|
|
338
|
+
|
|
339
|
+
### 10.8 Slider
|
|
340
|
+
|
|
341
|
+
Selector de valor único en un rango (`min`/`max`/`step`/`value`).
|
|
342
|
+
|
|
343
|
+
- Track: 6px de alto, radius full; porción rellena `accent-400`, resto `night-700` (gradiente lineal, sin elementos extra).
|
|
344
|
+
- Thumb: círculo `night-50` 18px, borde `2px accent-400`, sombra nivel 1; **al presionar escala 1.15** (100ms).
|
|
345
|
+
- Label opcional `text-sm night-200` arriba-izquierda; valor en JetBrains Mono `night-300` arriba-derecha (`showValue`).
|
|
346
|
+
- Disabled: opacidad 50%. Focus: ring como inputs. Respeta `prefers-reduced-motion` (el escalado del thumb al presionar se desactiva).
|
|
347
|
+
|
|
348
|
+
### 10.9 Checkbox y Radio
|
|
349
|
+
|
|
350
|
+
- 20px, borde `2px night-500`, radius 4px / full.
|
|
351
|
+
- Checked: fondo `accent-400`, check `night-950` animado (stroke-dashoffset, 200ms).
|
|
352
|
+
- Hover: borde `accent-300`. Focus: ring como inputs.
|
|
353
|
+
- Label clicable, gap 10px.
|
|
354
|
+
|
|
355
|
+
### 10.10 Switch
|
|
356
|
+
|
|
357
|
+
- Track 44×24px, radius full. Off: fondo `night-600`. On: fondo `accent-400`.
|
|
358
|
+
- Thumb 20px `night-50`; desliza 20px en 200ms `ease-spring`.
|
|
359
|
+
|
|
360
|
+
### 10.11 Badge y Tag
|
|
361
|
+
|
|
362
|
+
- Badge: `text-xs` peso 500, padding `2px 8px`, radius full. Fondo suave semántico + texto 400 del mismo tono (ej. `#0a2617` + `#4ade80`) + borde `1px` del tono al 30%.
|
|
363
|
+
- Tag removible: botón × 14px, hover fondo más claro.
|
|
364
|
+
|
|
365
|
+
### 10.12 Avatar
|
|
366
|
+
|
|
367
|
+
- Circular (radius full). Tamaños xs/sm/md/lg/xl = 24/32/40/48/64px (iniciales 10/12/15/18/24px).
|
|
368
|
+
- Con imagen: `object-fit: cover`, borde `1px night-600`.
|
|
369
|
+
- Sin imagen: iniciales (máx. 2, uppercase) en Space Grotesk 600 tracking `-0.02em`, color `night-50` sobre fondo `indigo-700` con borde `indigo-600`.
|
|
370
|
+
- Punto de estado opcional (esquina inferior derecha, ~28% del diámetro, mín. 8px, borde 2px `night-950`): online `success-400` · busy `danger-400` · away `warning-400` · offline `night-500`.
|
|
371
|
+
|
|
372
|
+
### 10.13 Divider
|
|
373
|
+
|
|
374
|
+
- Regla hairline `1px night-500`. `role="separator"`.
|
|
375
|
+
- Horizontal (default) · horizontal con label centrado (`text-xs` uppercase, tracking `+0.08em`, `night-300`, peso 500; líneas flexibles a los lados, gap 12px — patrón "o continúa con") · vertical (ancho 1px, se estira al alto del padre flex, margen horizontal 12px).
|
|
376
|
+
|
|
377
|
+
### 10.14 Accordion
|
|
378
|
+
|
|
379
|
+
- Contenedor: borde `1px night-600`, radius `lg`, `overflow: hidden`; filas fondo `night-900` separadas por borde `1px night-800`.
|
|
380
|
+
- Header: botón full-width, padding `16px 20px`, texto 16px peso 500 `night-100`, hover fondo `night-800` (150ms); chevron 18px `night-300` rota 180° al abrir (250ms). `aria-expanded`.
|
|
381
|
+
- Panel: `grid-template-rows` 0fr→1fr 250ms ease-out + fade; contenido padding `0 20px 18px`, 15px/1.6 `night-300`.
|
|
382
|
+
- Soporta apertura múltiple (`multiple`) y abiertos por defecto (`defaultOpen`).
|
|
383
|
+
|
|
384
|
+
### 10.15 Tooltip
|
|
385
|
+
|
|
386
|
+
Fondo `night-700`, borde `night-600`, texto `night-100` `text-xs`, padding `6px 10px`, radius `sm`. Delay 400ms, fade + translateY(4px→0) 150ms.
|
|
387
|
+
|
|
388
|
+
**Skip-delay:** solo el primer tooltip del grupo espera los 400ms; mientras haya un tooltip visible, los hermanos aparecen al instante y sin animación de entrada. El usuario ya está explorando — repetirle el delay y el fade en cada icono estorba.
|
|
389
|
+
|
|
390
|
+
### 10.16 Alert
|
|
391
|
+
|
|
392
|
+
Banner de estado **inline y persistente** (a diferencia del Toast, transitorio): errores de formulario, avisos de plan.
|
|
393
|
+
|
|
394
|
+
- 4 tonos: fondo suave semántico (`*-bg`, ej. `info-bg` `#0d1f2c`), borde `1px` del tono al 30%, radius `md`, padding 14px 16px.
|
|
395
|
+
- Icono semántico 20px en el color del tono: info→`info` · success→`circle-check` · warning→`triangle-alert` · danger→`circle-alert`.
|
|
396
|
+
- Título `text-sm` semibold y cuerpo `text-sm`, ambos en el color del tono (cuerpo con opacidad 88%).
|
|
397
|
+
- Botón cerrar opcional (× 18px, hit-área 36px) vía `onDismiss`; hover fondo `currentColor` al 14%. `role="status"`.
|
|
398
|
+
|
|
399
|
+
### 10.17 Toast
|
|
400
|
+
|
|
401
|
+
- Top-right (desktop), top-center (móvil). Ancho 360px.
|
|
402
|
+
- 4 tonos: `success` · `info` · `warning` · `danger` — cada uno colorea el borde izquierdo y el icono.
|
|
403
|
+
- Fondo `night-800`, borde `night-600`, borde izquierdo 3px semántico, sombra nivel 3, radius `md`, padding 14px 16px.
|
|
404
|
+
- Entrada: slide-in + fade 250ms. Salida: fade + translateX 200ms.
|
|
405
|
+
- Auto-dismiss 5s (pausable con hover). Máximo 3 apilados, gap 12px.
|
|
406
|
+
- **Accesibilidad:** el contenedor lleva `role="status"` + `aria-live="polite"`; los toasts de error usan `role="alert"` para anunciarse de inmediato.
|
|
407
|
+
|
|
408
|
+
### 10.18 Progress y ProgressRing
|
|
409
|
+
|
|
410
|
+
Progreso **determinado** (para carga indeterminada usar `Skeleton`, no Progress).
|
|
411
|
+
|
|
412
|
+
- Track: altura 6px, fondo `night-700`, radius full, `overflow: hidden`.
|
|
413
|
+
- Relleno: `accent-400` con glow `0 0 12px rgba(82,186,237,.5)` (desactivable), transición width 300ms ease-out.
|
|
414
|
+
- Indeterminado: segmento del 40% que barre en loop 1.2s ease-out.
|
|
415
|
+
- Cabecera opcional: label 14px `night-200` + valor `%` en mono 14px `accent-400` `tabular-nums`, margen inferior 8px.
|
|
416
|
+
- Accesibilidad: `role="progressbar"` + `aria-valuenow/min/max` (sin `valuenow` en indeterminado).
|
|
417
|
+
- **ProgressRing:** SVG rotado -90°, track `night-700`, arco `accent-400` con `stroke-linecap: round`, `stroke-dashoffset` anima 250ms ease-out (respeta `prefers-reduced-motion`); tamaño 56px, stroke 5px; porcentaje centrado en mono `accent-400` `text-sm` semibold. Mismo `role="progressbar"` + `aria-valuenow`.
|
|
418
|
+
|
|
419
|
+
### 10.19 Modal
|
|
420
|
+
|
|
421
|
+
- Overlay: `--overlay` + `backdrop-filter: blur(4px)`, fade 200ms.
|
|
422
|
+
- Panel: fondo `night-800`, borde `night-600`, radius `lg`, sombra nivel 3, max-width 480/640px, padding 24px.
|
|
423
|
+
- Entrada: fade + scale(0.96→1) 250ms. Salida 180ms.
|
|
424
|
+
- Header `text-xl` + IconButton cerrar; footer con primaria al extremo derecho.
|
|
425
|
+
- Esc y click en overlay cierran (excepto flujos destructivos).
|
|
426
|
+
- `overscroll-behavior: contain` en el panel scrolleable — al llegar al fondo, el scroll no se encadena a la página de atrás.
|
|
427
|
+
|
|
428
|
+
### 10.20 Menu
|
|
429
|
+
|
|
430
|
+
Lista de acciones desplegable anclada a un trigger (acciones de fila, botón "más", menú de cuenta) — mismo patrón visual que el menú de `Select`, pero se abre desde cualquier trigger.
|
|
431
|
+
|
|
432
|
+
- Panel: fondo `night-800`, borde `1px night-600`, radius `md`, sombra nivel 3, padding 6px, min-width 190px, max-height 320px (`overflow: auto`).
|
|
433
|
+
- Ítem: `role="menuitem"`, padding `10px 12px`, radius `sm`, `text-sm` `night-100`, icono opcional 16px `night-300`, gap 10px; hover fondo `night-700`.
|
|
434
|
+
- Ítem seleccionado: fondo `--accent-selected` + texto `accent-300` (`--link` en modo claro).
|
|
435
|
+
- Entrada: fade + translateY(-4px→0), 180ms. Cierra con click fuera, Esc o al seleccionar.
|
|
436
|
+
|
|
437
|
+
### 10.21 Tabla
|
|
438
|
+
|
|
439
|
+
- Header: fondo `night-900`, `text-xs` uppercase `+0.08em` `night-300`, borde inferior `night-600`.
|
|
440
|
+
- Filas: altura 52px, borde inferior `night-800`, hover fondo `night-700`.
|
|
441
|
+
- Numéricos a la derecha con `tabular-nums`.
|
|
442
|
+
- Fila seleccionada: fondo `--accent-selected` + borde izquierdo 2px `accent-400`.
|
|
443
|
+
|
|
444
|
+
### 10.22 Stat
|
|
445
|
+
|
|
446
|
+
- **Label** overline arriba: 12px, peso 500, uppercase, tracking `+0.08em`, `night-300`.
|
|
447
|
+
- **Cifra:** JetBrains Mono 49px peso 500, line-height 1.05, color `accent-400`, `tabular-nums`, `white-space: nowrap`.
|
|
448
|
+
- **Delta** opcional debajo: mono 14px — `success-400` / `danger-400` / `night-300` (neutral).
|
|
449
|
+
- Gap vertical 6px. Formato numérico `es-ES`.
|
|
450
|
+
- **Count-up** 600ms ease-out al montar; con `prefers-reduced-motion` muestra el valor directo.
|
|
451
|
+
|
|
452
|
+
### 10.23 Pagination
|
|
453
|
+
|
|
454
|
+
Navegador de páginas 1-indexado, bajo tablas y listas largas.
|
|
455
|
+
|
|
456
|
+
- Flechas prev/next (chevron 16px) + lista de páginas que **trunca con elipsis a partir de 7 páginas**.
|
|
457
|
+
- Celdas: min-width 36px, altura 36px, radius `sm`, `text-sm` peso 500 `night-200`; hover fondo `night-800`.
|
|
458
|
+
- Página activa: fondo `accent-400`, texto `night-950` (`text-on-accent`) peso 700 — no depende solo del color —, `aria-current="page"`.
|
|
459
|
+
- Flechas/celdas disabled: texto `night-400`, `cursor: not-allowed`; elipsis no clicable.
|
|
460
|
+
|
|
461
|
+
### 10.24 Navegación
|
|
462
|
+
|
|
463
|
+
- **Navbar:** altura 64px, fondo `night-950/80%` + `backdrop-filter: blur(12px)`, borde inferior `night-600`. Link activo: texto `accent-400` + indicador inferior 2px.
|
|
464
|
+
- **Sidebar:** ancho 260px, fondo `night-900`. Ítem: radius `md`, padding `10px 12px`; activo: fondo `indigo-800`, texto `night-50`, icono `accent-400`.
|
|
465
|
+
- **Tabs:** borde inferior `night-600`; indicador 2px `accent-400` que se desliza (250ms).
|
|
466
|
+
- **Breadcrumbs:** `text-sm` `night-300`, separador `/` (tipográfico), último ítem `night-100`.
|
|
467
|
+
|
|
468
|
+
### 10.25 Skeleton y EmptyState
|
|
469
|
+
|
|
470
|
+
- Skeleton: fondo `night-800` con shimmer (gradiente hacia `night-700`, barre en 1.4s loop). Radius igual al contenido.
|
|
471
|
+
- Estado vacío: icono en `indigo-400`, título `text-lg` `night-100`, descripción `text-sm` `night-300`, CTA claro. Un estado vacío siempre invita a actuar.
|
|
472
|
+
|
|
473
|
+
### 10.26 Card, Link y Code
|
|
474
|
+
|
|
475
|
+
- **Card:** fondo `night-900`, borde `1px night-600`, radius `lg`, padding 24px (16px en móvil), sombra nivel 1.
|
|
476
|
+
- **Card interactiva:** cursor pointer; hover eleva — `translateY(-2px)` + fondo `night-800` + sombra nivel 2; transición de fondo/transform/sombra 220ms ease-out.
|
|
477
|
+
- **Card con glow:** firma de marca (`--glow-accent`) — **máximo una por pantalla**.
|
|
478
|
+
- **Link:** color `accent-400`; hover `accent-200` (150ms). Subrayado animado: línea 1px `currentColor` a -2px, `scaleX(0)→scaleX(1)` desde la izquierda, 200ms ease-out. Externo: `target="_blank" rel="noopener noreferrer"` + icono flecha 12px, gap 4px.
|
|
479
|
+
- **Code (inline):** JetBrains Mono `0.9em`, texto `accent-300`, fondo `night-800`, borde `1px night-700`, radius 6px, padding `1px 6px`.
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## 11. Animaciones y motion
|
|
484
|
+
|
|
485
|
+
### 11.0 Cuándo animar (frequency gate)
|
|
486
|
+
|
|
487
|
+
La frecuencia de una acción decide su intensidad de movimiento:
|
|
488
|
+
|
|
489
|
+
| Frecuencia de la acción | Motion |
|
|
490
|
+
|---|---|
|
|
491
|
+
| Rara / memorable (primer uso, éxito) | Puede deleitar, con presencia contenida |
|
|
492
|
+
| Ocasional (abrir un panel, un modal) | Transición clara y sobria |
|
|
493
|
+
| Diaria / repetida (navegar, alternar) | Sutil y rápida, casi imperceptible |
|
|
494
|
+
| Muy frecuente (100+/día) o de teclado | Sin animación; el movimiento estorbaría |
|
|
495
|
+
|
|
496
|
+
Antes de animar algo, pregúntate: ¿de dónde vino, qué cambió, a dónde se fue? Si no hay respuesta, no lo animes.
|
|
497
|
+
|
|
498
|
+
### 11.1 Tokens
|
|
499
|
+
|
|
500
|
+
```css
|
|
501
|
+
:root {
|
|
502
|
+
--duration-instant: 100ms; /* press, toggle */
|
|
503
|
+
--duration-fast: 150ms; /* hover, focus, color */
|
|
504
|
+
--duration-base: 220ms; /* dropdowns, tooltips, switches */
|
|
505
|
+
--duration-slow: 250ms; /* modales, toasts */
|
|
506
|
+
--duration-page: 350ms; /* transiciones de página/vista */
|
|
507
|
+
|
|
508
|
+
--ease-out: cubic-bezier(0.2, 0, 0, 1);
|
|
509
|
+
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
510
|
+
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* moverse de A a B en pantalla */
|
|
511
|
+
--ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### 11.2 Principios de movimiento
|
|
516
|
+
|
|
517
|
+
1. **Entra rápido, sale más rápido** — Salida ~20% más corta que la entrada.
|
|
518
|
+
2. **Solo `transform` y `opacity`** — Nunca animar propiedades de layout.
|
|
519
|
+
3. **Distancias cortas** — Entradas de 4–8px.
|
|
520
|
+
4. **Stagger en listas** — 30ms por ítem, máximo 6 ítems.
|
|
521
|
+
5. **Firma de marca:** los elementos con acento entran con fade + glow (opacidad del glow 0→1 en 220ms). Usar solo en el elemento hero/CTA.
|
|
522
|
+
6. **Origen consciente** — Popovers, menús y dropdowns aparecen desde el `transform-origin` de su disparador; los modales, desde el centro. Nunca un origen arbitrario.
|
|
523
|
+
7. **Interrumpible** — Toda animación puede invertirse desde su valor visible actual, sin saltar al destino y reanimar. Clave en hover y gestos. En la práctica: para UI que se dispara con frecuencia (toasts, dropdowns, hover) usa `transition` — con `@starting-style` para la entrada al montar —, no `@keyframes`: un keyframe no puede invertirse a mitad de camino. Los `@keyframes` quedan para loops de carga (spinner, shimmer).
|
|
524
|
+
8. **La salida es más discreta que la entrada** — Además de ~20% más corta, atenúa: menor desplazamiento y menor opacidad. Entrar se nota; salir se difumina.
|
|
525
|
+
9. **El easing según el trabajo** — Entrar → `--ease-out`; salir → `--ease-in`; un elemento que se mueve de un punto a otro de la pantalla (indicador de tabs o segmented, reorder) → `--ease-in-out`, que acelera y frena en espejo; hover y cambios de color → `--ease-out` corto; loops de carga → `linear`.
|
|
526
|
+
10. **`will-change` solo mientras anima** — Se pone justo antes de animar y se quita al terminar; nunca permanente en el CSS. Presupuesto de capas GPU: menos de 10 elementos promovidos a la vez.
|
|
527
|
+
|
|
528
|
+
### 11.3 Micro-interacciones por componente
|
|
529
|
+
|
|
530
|
+
| Elemento | Trigger | Animación |
|
|
531
|
+
|---|---|---|
|
|
532
|
+
| Botón primario | hover | color 150ms + glow suave fade-in |
|
|
533
|
+
| Botón | press | `scale(0.98)` 100ms |
|
|
534
|
+
| Link | hover | color a `accent-200` 150ms + subrayado 0→100% (200ms) |
|
|
535
|
+
| Tarjeta interactiva | hover | `translateY(-2px)` + superficie sube a `night-800`, 200ms |
|
|
536
|
+
| Input | focus | ring fade 150ms |
|
|
537
|
+
| Checkbox | check | trazo dibujándose 200ms `ease-spring` |
|
|
538
|
+
| Toggle | cambio | thumb desliza 200ms `ease-spring` + track cross-fade |
|
|
539
|
+
| Acordeón | abrir | `grid-template-rows` 0fr→1fr 250ms + fade |
|
|
540
|
+
| Tabs (indicador) | cambio | indicador desliza (transform/width) 250ms `--ease-in-out` |
|
|
541
|
+
| Dropdown | abrir | fade + translateY(-4px→0) 180ms |
|
|
542
|
+
| Modal | abrir | overlay fade+blur 200ms; panel fade + scale(0.96→1) 250ms |
|
|
543
|
+
| Toast | entrar | slide-in + fade 250ms |
|
|
544
|
+
| Icon swap (p. ej. copiar→check) | cambio | cross-fade: opacity + scale(0.9→1), 150ms; nunca blur |
|
|
545
|
+
| Página/vista | cambio | fade + translateY(8px→0) 350ms |
|
|
546
|
+
| Cifra/dato clave | montar | count-up numérico 350ms `ease-out` (solo dashboards; techo `--duration-page`) |
|
|
547
|
+
|
|
548
|
+
### 11.4 Loading
|
|
549
|
+
|
|
550
|
+
- Spinner: trazo 2px `accent-400`, rotación 0.8s linear.
|
|
551
|
+
- Barra de progreso: ver §10.18.
|
|
552
|
+
- Skeleton shimmer 1.4s loop.
|
|
553
|
+
- Cargas <300ms: no mostrar spinner (evita parpadeo).
|
|
554
|
+
|
|
555
|
+
### 11.5 Accesibilidad del movimiento
|
|
556
|
+
|
|
557
|
+
```css
|
|
558
|
+
@media (prefers-reduced-motion: reduce) {
|
|
559
|
+
*, *::before, *::after {
|
|
560
|
+
animation-duration: 0.01ms !important;
|
|
561
|
+
animation-iteration-count: 1 !important;
|
|
562
|
+
transition-duration: 0.01ms !important;
|
|
563
|
+
scroll-behavior: auto !important;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
Este reset con `*` es el **piso de seguridad**: garantiza que ningún motion sobreviva si el usuario pide reducirlo, incluso en componentes que no se revisaron a mano. Donde el movimiento comunica información real (progreso, un check, un conteo) y eliminarlo del todo perjudicaría la comprensión, un componente puede sobrescribir el reset con un selector más específico y una duración corta (p. ej. `transition-duration: 80ms`) en vez de colapsar a 0.01ms — hoy ningún componente del sistema ejerce esta excepción; todos usan el piso global.
|
|
569
|
+
|
|
570
|
+
**Gating de hover:** los efectos de hover que mueven o elevan (transform, box-shadow, glow) van dentro de `@media (hover: hover) and (pointer: fine)` — en touch no hay hover real y el tap dispara hovers falsos que se quedan pegados hasta el siguiente toque. Los hover de solo color/fondo pueden quedar fuera: un cambio de color pegado no estorba.
|
|
571
|
+
|
|
572
|
+
```css
|
|
573
|
+
@media (hover: hover) and (pointer: fine) {
|
|
574
|
+
.card--interactive:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### 11.6 Anti-patrones de motion
|
|
579
|
+
|
|
580
|
+
Tells de motion genérico (a menudo generado por IA). La tool `validate_motion` del MCP los detecta con estos umbrales:
|
|
581
|
+
|
|
582
|
+
| Anti-patrón | Umbral / detección | En su lugar |
|
|
583
|
+
|---|---|---|
|
|
584
|
+
| `transition: all` | cualquier uso | Enumera: `transition: transform 200ms, opacity 200ms` |
|
|
585
|
+
| `ease-in` en entradas | `ease-in` en una entrada de UI | `--ease-out`; reserva `ease-in` para salidas |
|
|
586
|
+
| Animar propiedades de layout | `width/height/top/left/right/bottom/margin/padding` | `transform`/`opacity`. Excepciones: `width` de Progress, `grid-template-rows` del Accordion |
|
|
587
|
+
| Blur como recurso | `filter`/`backdrop-filter: blur()` | Elimínalo; fondos planos, sombras teñidas de tinta. Excepción: navbar (`blur(12px)`, §10.24) y overlay de modal (`blur(4px)`, §10.19) — únicos dos usos de la marca, ninguno más |
|
|
588
|
+
| Duración excesiva | > el techo del sistema (`--duration-page`) fuera de un loop de carga | Recorta a ≤ `--duration-page` |
|
|
589
|
+
| `scale(0)` | arrancar desde escala 0 | `scale(0.9–0.98)` + `opacity: 0` |
|
|
590
|
+
| Rebote en utilitarios | spring/overshoot en dropdown, menú, modal, tooltip, popover, select | `--ease-out`; el spring sutil se reserva a check y toggle |
|
|
591
|
+
| Overshoot excesivo | curva con y1 > 1.6 | Rebote apenas perceptible (y1 ≤ ~1.4) |
|
|
592
|
+
| blur-everywhere | ≥3 elementos con blur | Rediseña sin blur |
|
|
593
|
+
| hover-scale-en-todo | ≥3 elementos que escalan en hover | Reserva el scale para 1–2 acciones clave |
|
|
594
|
+
| stagger-spam | ≥2 listas con stagger en una vista | Una lista con stagger por vista (máx. 6 ítems) |
|
|
595
|
+
| uniform-fade | ≥4 entradas idénticas | Varía duración/origen o justifícalo |
|
|
596
|
+
| pulsing | indicadores que laten en loop (pulse/ping/glow) | Estado estático claro; spinner solo en carga real |
|
|
597
|
+
| motion-on-mount-for-static | contenido estático que se anima al montar sin razón — no lo detecta la tool; juicio manual | Nada: aplica el frequency gate (§11.0) — si no responde de dónde/qué/adónde, no se anima |
|
|
598
|
+
|
|
599
|
+
### 11.7 Motion gap
|
|
600
|
+
|
|
601
|
+
Un "motion gap" es UI que aparece o desaparece sin transición y debería tenerla. Zonas donde suele faltar:
|
|
602
|
+
|
|
603
|
+
- Render condicional (`{cond && <X/>}`) y ternarios que cambian de vista.
|
|
604
|
+
- Tabs, secciones colapsables y acordeones.
|
|
605
|
+
- Campos condicionales de formularios.
|
|
606
|
+
- Toasts y notificaciones.
|
|
607
|
+
- Estados de carga y de error.
|
|
608
|
+
- Paneles de settings / inspector.
|
|
609
|
+
|
|
610
|
+
Regla: un gap en UI primaria (el flujo principal) es crítico; en UI secundaria, medio. Toda entrada/salida condicional necesita su transición de `opacity`/`transform`, con su contraparte de salida más discreta.
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
## 12. Accesibilidad y contraste
|
|
615
|
+
|
|
616
|
+
- `night-100` sobre `night-950`: **≈ 16:1** ✅ AAA.
|
|
617
|
+
- `accent-400` sobre `night-950`: **≈ 8.5:1** ✅ — sirve como texto/link.
|
|
618
|
+
- `night-950` sobre `accent-400` (CTAs): **≈ 8.5:1** ✅ — por eso el texto del CTA es oscuro.
|
|
619
|
+
- `indigo-500` sobre `night-950`: **≈ 1.9:1** ⚠️ — solo fondo/decorativo, nunca texto.
|
|
620
|
+
- `night-50` sobre `indigo-500`: **≈ 9:1** ✅ — texto sobre fondos índigo funciona.
|
|
621
|
+
- Mínimos WCAG AA: 4.5:1 texto normal, 3:1 texto grande y UI.
|
|
622
|
+
- Focus visible siempre (`:focus-visible` global, §6); estado nunca solo con color (color + icono + texto).
|
|
623
|
+
- Touch targets 44×44px.
|
|
624
|
+
- IconButton siempre con `aria-label`; ProgressBar con `role="progressbar"`; Accordion con `aria-expanded`.
|
|
625
|
+
|
|
626
|
+
---
|
|
627
|
+
|
|
628
|
+
## 13. Tokens CSS consolidados
|
|
629
|
+
|
|
630
|
+
```css
|
|
631
|
+
:root {
|
|
632
|
+
/* == Primitivos: acento == */
|
|
633
|
+
--accent-100: #d9f0fb; --accent-200: #a8ddf6; --accent-300: #7dccf1;
|
|
634
|
+
--accent-400: #52baed; --accent-500: #2fa3dd; --accent-600: #1f87bb; --accent-700: #186a94;
|
|
635
|
+
|
|
636
|
+
/* == Primitivos: índigo == */
|
|
637
|
+
--indigo-300: #7a83c4; --indigo-400: #565fa3; --indigo-500: #353d7c;
|
|
638
|
+
--indigo-600: #2b3264; --indigo-700: #22284f; --indigo-800: #1a1e3b;
|
|
639
|
+
|
|
640
|
+
/* == Primitivos: escala nocturna == */
|
|
641
|
+
--night-950: #010203; --night-900: #0b0d12; --night-800: #14171f;
|
|
642
|
+
--night-700: #1e222d; --night-600: #2a2f3d; --night-500: #3d4354;
|
|
643
|
+
--night-400: #6b7183; --night-300: #9aa0b0; --night-200: #c5c9d4;
|
|
644
|
+
--night-100: #e8eaef; --night-50: #f6f7f9;
|
|
645
|
+
|
|
646
|
+
/* == Primitivos: semánticos == */
|
|
647
|
+
--success-400: #4ade80; --success-bg: #0a2617;
|
|
648
|
+
--warning-400: #fbbf24; --warning-bg: #2a2005;
|
|
649
|
+
--danger-400: #f87171; --danger-bg: #2c0f0f;
|
|
650
|
+
--info-400: #52baed; --info-bg: #0d1f2c;
|
|
651
|
+
|
|
652
|
+
/* == Aliases semánticos (los componentes consumen SOLO esto) == */
|
|
653
|
+
--bg: var(--night-950);
|
|
654
|
+
--surface-1: var(--night-900); --surface-2: var(--night-800); --surface-3: var(--night-700);
|
|
655
|
+
--border: var(--night-500); --border-strong: var(--night-600);
|
|
656
|
+
--text-primary: var(--night-100); --text-secondary: var(--night-200);
|
|
657
|
+
--text-muted: var(--night-300); --text-disabled: var(--night-400);
|
|
658
|
+
--text-heading: var(--night-50); --text-on-accent: var(--night-950);
|
|
659
|
+
--success: var(--success-400); --warning: var(--warning-400);
|
|
660
|
+
--danger: var(--danger-400); --info: var(--info-400);
|
|
661
|
+
--accent-ghost-hover: rgba(82,186,237,.08);
|
|
662
|
+
--accent-ghost-active: rgba(82,186,237,.14);
|
|
663
|
+
--accent-selected: rgba(82,186,237,.12);
|
|
664
|
+
--overlay: rgba(1,2,3,.7);
|
|
665
|
+
|
|
666
|
+
/* == Tipografía == */
|
|
667
|
+
--font-display: "Space Grotesk", system-ui, sans-serif;
|
|
668
|
+
--font-body: "Manrope", system-ui, sans-serif;
|
|
669
|
+
--font-mono: "JetBrains Mono", monospace;
|
|
670
|
+
--text-xs: .75rem; --text-sm: .875rem; --text-base: 1rem; --text-lg: 1.125rem;
|
|
671
|
+
--text-xl: 1.25rem; --text-2xl: 1.5625rem; --text-3xl: 1.9375rem;
|
|
672
|
+
--text-4xl: 2.4375rem; --text-5xl: 3.0625rem;
|
|
673
|
+
--leading-tight: 1.05; --leading-heading: 1.15; --leading-h2: 1.2;
|
|
674
|
+
--leading-h3: 1.3; --leading-h4: 1.4; --leading-snug: 1.5; --leading-body: 1.6;
|
|
675
|
+
--tracking-display: -0.02em; --tracking-overline: 0.08em;
|
|
676
|
+
--measure: 65ch;
|
|
677
|
+
|
|
678
|
+
/* == Espaciado == */
|
|
679
|
+
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
|
|
680
|
+
--space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px;
|
|
681
|
+
--space-12: 48px; --space-16: 64px; --space-20: 80px; --space-24: 96px;
|
|
682
|
+
--card-padding: 24px; --card-padding-mobile: 16px;
|
|
683
|
+
--section-gap: 64px; --form-gap: 20px;
|
|
684
|
+
--button-padding: 12px 20px; --touch-target: 44px;
|
|
685
|
+
--grid-gutter: 24px; --grid-gutter-mobile: 16px;
|
|
686
|
+
|
|
687
|
+
/* == Radios y bordes == */
|
|
688
|
+
--radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px;
|
|
689
|
+
--radius-xl: 24px; --radius-full: 9999px;
|
|
690
|
+
--border-default: 1px solid var(--night-500);
|
|
691
|
+
--border-emphasis: 1px solid var(--night-600);
|
|
692
|
+
--focus-ring: 2px solid var(--accent-400);
|
|
693
|
+
--focus-ring-offset: 2px;
|
|
694
|
+
|
|
695
|
+
/* == Elevación == */
|
|
696
|
+
--shadow-1: 0 1px 2px rgba(0,0,0,.4);
|
|
697
|
+
--shadow-2: 0 4px 12px rgba(0,0,0,.5);
|
|
698
|
+
--shadow-3: 0 12px 32px rgba(0,0,0,.6);
|
|
699
|
+
--glow-accent: 0 0 0 1px rgba(82,186,237,.35), 0 4px 20px rgba(82,186,237,.18);
|
|
700
|
+
--ring: 0 0 0 3px rgba(82,186,237,.2);
|
|
701
|
+
--ring-danger: 0 0 0 3px rgba(248,113,113,.2);
|
|
702
|
+
|
|
703
|
+
/* == Motion == */
|
|
704
|
+
--duration-instant: 100ms; --duration-fast: 150ms; --duration-base: 220ms;
|
|
705
|
+
--duration-slow: 250ms; --duration-page: 350ms;
|
|
706
|
+
--ease-out: cubic-bezier(0.2, 0, 0, 1);
|
|
707
|
+
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
708
|
+
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
|
709
|
+
--ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
|
|
710
|
+
|
|
711
|
+
color-scheme: dark;
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
Estilos base de elementos (extracto de `base.css`): body con `--bg`/`--text-primary`/`--font-body`/`--leading-body` + antialiasing; **h1–h3 en `--font-display`** color `--text-heading`; **h4 en `--font-body`**; `p { max-width: var(--measure) }`; links `accent-400`→hover `accent-200`.
|
|
716
|
+
|
|
717
|
+
---
|
|
718
|
+
|
|
719
|
+
## 14. Breakpoints y grid
|
|
720
|
+
|
|
721
|
+
| Token | Ancho | Contenedor |
|
|
722
|
+
|---|---|---|
|
|
723
|
+
| `sm` | 640px | 100% - 32px |
|
|
724
|
+
| `md` | 768px | 720px |
|
|
725
|
+
| `lg` | 1024px | 960px |
|
|
726
|
+
| `xl` | 1280px | 1200px |
|
|
727
|
+
|
|
728
|
+
Grid de 12 columnas, gutter 24px (16px móvil). Mobile-first.
|
|
729
|
+
|
|
730
|
+
**Touch y viewport:**
|
|
731
|
+
|
|
732
|
+
- `touch-action: manipulation` en botones y links — elimina el delay de double-tap en móvil.
|
|
733
|
+
- Alturas de viewport con `100dvh`, no `100vh` (la barra de iOS cambia el alto visible).
|
|
734
|
+
- **Receta de overflow:** `min-width: 0` en los hijos flex que deben truncar; texto largo con `text-overflow: ellipsis` o `line-clamp`, siempre con `title` o tooltip que exponga el contenido completo.
|
|
735
|
+
|
|
736
|
+
---
|
|
737
|
+
|
|
738
|
+
## 15. Modo claro (variante)
|
|
739
|
+
|
|
740
|
+
El sistema es **dark-first**, pero todo producto debe soportar light mode con los mismos tokens semánticos. La energía de la marca se conserva; solo se invierte el escenario.
|
|
741
|
+
|
|
742
|
+
### 15.1 Filosofía de la inversión
|
|
743
|
+
|
|
744
|
+
- Los fondos claros se tiñen levemente de azul (derivados del acento), nunca gris genérico ni blanco plano en toda la pantalla.
|
|
745
|
+
- El celeste `#52baed` sobre blanco tiene contraste bajo (≈ 2.2:1): **no puede ser texto ni link en light**. Los links y textos de acción bajan a `accent-600 (#1f87bb)` y `accent-700 (#186a94)`.
|
|
746
|
+
- El CTA conserva la firma de marca: **fondo `#52baed` con texto `#010203`** funciona igual sobre claro (contraste ≈ 8.5:1) — el botón se ve idéntico en ambos modos.
|
|
747
|
+
- El índigo `#353d7c` gana protagonismo en light: aquí **sí** funciona como texto, headings de marca y fondos de sección con texto blanco.
|
|
748
|
+
|
|
749
|
+
### 15.2 Mapeo de tokens
|
|
750
|
+
|
|
751
|
+
| Token semántico | Dark | Light |
|
|
752
|
+
|---|---|---|
|
|
753
|
+
| `--bg` | `#010203` | `#f5f8fb` |
|
|
754
|
+
| `--surface-1` | `#0b0d12` | `#ffffff` |
|
|
755
|
+
| `--surface-2` | `#14171f` | `#ffffff` |
|
|
756
|
+
| `--surface-3` | `#1e222d` | `#eaf1f7` |
|
|
757
|
+
| `--border` | `#3d4354` | `#d8dfe8` |
|
|
758
|
+
| `--border-strong` | `#2a2f3d` | `#b9c3d1` |
|
|
759
|
+
| `--text-primary` | `#e8eaef` | `#14171f` |
|
|
760
|
+
| `--text-secondary` | `#c5c9d4` | `#3d4354` |
|
|
761
|
+
| `--text-muted` | `#9aa0b0` | `#6b7183` |
|
|
762
|
+
| `--link` / texto de acción | `#52baed` | `#1f87bb` |
|
|
763
|
+
| `--link-hover` | `#a8ddf6` | `#186a94` |
|
|
764
|
+
| CTA primario (fondo/texto) | `#52baed` / `#010203` | `#52baed` / `#010203` (sin cambio) |
|
|
765
|
+
| Secundario índigo | `#353d7c` fondo | `#353d7c` fondo, texto blanco |
|
|
766
|
+
| `--success` | `#4ade80` | `#15803d` |
|
|
767
|
+
| `--warning` | `#fbbf24` | `#a16207` |
|
|
768
|
+
| `--danger` | `#f87171` | `#c03535` |
|
|
769
|
+
| `--ring` | `rgba(82,186,237,.2)` | `rgba(31,135,187,.25)` |
|
|
770
|
+
|
|
771
|
+
Fondos suaves semánticos en light: `success #e3f5ea` · `warning #fbf3dd` · `danger #fbe7e7` · `info #e3f2fb`.
|
|
772
|
+
|
|
773
|
+
### 15.3 Elevación en light
|
|
774
|
+
|
|
775
|
+
Vuelven las sombras clásicas (teñidas de índigo, no negro puro); las superficies dejan de aclararse por nivel:
|
|
776
|
+
|
|
777
|
+
| Nivel | Superficie | Sombra |
|
|
778
|
+
|---|---|---|
|
|
779
|
+
| 0 | `#f5f8fb` | — |
|
|
780
|
+
| 1 | `#ffffff` | `0 1px 3px rgba(53,61,124,.08)` |
|
|
781
|
+
| 2 | `#ffffff` | `0 4px 10px rgba(53,61,124,.10)` |
|
|
782
|
+
| 3 | `#ffffff` | `0 12px 28px rgba(53,61,124,.14)` |
|
|
783
|
+
|
|
784
|
+
El glow de acento se mantiene como firma, pero más tenue: `0 0 0 1px rgba(82,186,237,.4), 0 4px 16px rgba(82,186,237,.25)` — solo en el CTA hero.
|
|
785
|
+
|
|
786
|
+
### 15.4 Ajustes de componentes en light
|
|
787
|
+
|
|
788
|
+
- **Botón primario:** idéntico al dark (fondo `#52baed`, texto `#010203`) — consistencia de marca entre modos.
|
|
789
|
+
- **Botón secundario:** fondo `#353d7c`, texto blanco (contraste ≈ 9:1 ✅).
|
|
790
|
+
- **Outline/Ghost:** borde `#b9c3d1`, texto `#14171f`; ghost con texto `#1f87bb` y hover `rgba(31,135,187,.08)`.
|
|
791
|
+
- **Inputs:** fondo blanco, borde `#d8dfe8`; focus borde `#1f87bb` + ring.
|
|
792
|
+
- **Tabla:** header `#eaf1f7`, hover de fila `#f5f8fb`, seleccionada `rgba(82,186,237,.12)` + borde izquierdo `#1f87bb`.
|
|
793
|
+
- **Navbar:** blanco 80% + blur, borde inferior `#d8dfe8`; link activo `#186a94`.
|
|
794
|
+
- **Sidebar activo:** fondo `#e3f2fb`, texto `#14171f`, icono `#1f87bb`.
|
|
795
|
+
- **Tooltip:** fondo `#14171f`, texto `#f6f7f9` (se invierte).
|
|
796
|
+
- **Toast:** fondo blanco, borde `#d8dfe8`, borde izquierdo semántico.
|
|
797
|
+
- **Overlay de modal:** `rgba(20,23,31,.45)` + blur 4px.
|
|
798
|
+
- **Skeleton:** base `#eaf1f7`, shimmer hacia `#f5f8fb`.
|
|
799
|
+
- **Count-up y motion:** sin cambios; el glow de entrada baja su opacidad máxima a 0.6.
|
|
800
|
+
|
|
801
|
+
### 15.5 Contraste en light
|
|
802
|
+
|
|
803
|
+
- `#14171f` sobre `#f5f8fb`: ≈ 16:1 ✅ AAA.
|
|
804
|
+
- `#1f87bb` sobre blanco: ≈ 4.5:1 ✅ AA — link/texto de acción válido.
|
|
805
|
+
- `#52baed` sobre blanco: ≈ 2.2:1 ❌ — **solo relleno decorativo o fondo de CTA con texto oscuro**.
|
|
806
|
+
- Blanco sobre `#353d7c`: ≈ 9:1 ✅ AAA.
|
|
807
|
+
|
|
808
|
+
### 15.6 Implementación
|
|
809
|
+
|
|
810
|
+
```css
|
|
811
|
+
[data-theme="light"] {
|
|
812
|
+
--bg: #f5f8fb;
|
|
813
|
+
--surface-1: #ffffff;
|
|
814
|
+
--surface-2: #ffffff;
|
|
815
|
+
--surface-3: #eaf1f7;
|
|
816
|
+
--border: #d8dfe8;
|
|
817
|
+
--border-strong: #b9c3d1;
|
|
818
|
+
--text-primary: #14171f;
|
|
819
|
+
--text-secondary: #3d4354;
|
|
820
|
+
--text-muted: #6b7183;
|
|
821
|
+
--link: #1f87bb;
|
|
822
|
+
--link-hover: #186a94;
|
|
823
|
+
--text-on-accent: #010203; /* CTA no cambia */
|
|
824
|
+
--success: #15803d;
|
|
825
|
+
--warning: #a16207;
|
|
826
|
+
--danger: #c03535;
|
|
827
|
+
--ring: 0 0 0 3px rgba(31,135,187,.25);
|
|
828
|
+
--shadow-1: 0 1px 3px rgba(53,61,124,.08);
|
|
829
|
+
--shadow-2: 0 4px 10px rgba(53,61,124,.10);
|
|
830
|
+
--shadow-3: 0 12px 28px rgba(53,61,124,.14);
|
|
831
|
+
--glow-accent: 0 0 0 1px rgba(82,186,237,.4), 0 4px 16px rgba(82,186,237,.25);
|
|
832
|
+
color-scheme: light;
|
|
833
|
+
}
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
---
|
|
837
|
+
|
|
838
|
+
## 16. Distribución y consumo
|
|
839
|
+
|
|
840
|
+
El sistema se distribuye en varios formatos para poder usarlo desde cualquier stack:
|
|
841
|
+
|
|
842
|
+
| Entregable | Ubicación | Uso |
|
|
843
|
+
|---|---|---|
|
|
844
|
+
| **Este documento** | `DESIGN-nocturna-electrica.md` | Fuente de verdad legible |
|
|
845
|
+
| **Style guide interactivo** | `nocturna-electrica.html` | Referencia visual siempre disponible (abrir en navegador) |
|
|
846
|
+
| **Tokens CSS** | `dist/nocturna-electrica/tokens.css` | Drop-in para cualquier proyecto (vanilla, React, Angular, Vue) |
|
|
847
|
+
| **Tailwind v4** | `dist/nocturna-electrica/tailwind.css` | Bloque `@theme` — genera utilidades `bg-night-900`, `text-accent-400`, etc. |
|
|
848
|
+
| **DTCG JSON** | `dist/nocturna-electrica/tokens.json` | Formato estándar W3C DTCG 2025.10 — interoperable con Figma/Tokens Studio/Style Dictionary |
|
|
849
|
+
| **Skill de Claude** | `~/.claude/skills/nocturna-electrica-design/` | Invocable desde cualquier sesión de Claude Code |
|
|
850
|
+
| **Proyecto claude.ai/design** | [Nocturna Eléctrica Design System](https://claude.ai/design/p/1a3a2fb6-b02c-481c-96fc-017d50ec9ce7) | Componentes React + specimens |
|
|
851
|
+
|
|
852
|
+
**Cómo consumirlo en un proyecto nuevo:**
|
|
853
|
+
|
|
854
|
+
1. Copiar `dist/nocturna-electrica/tokens.css` e importarlo antes que cualquier estilo propio, **o** pegar el bloque `@theme` en el CSS de Tailwind v4.
|
|
855
|
+
2. Los componentes consumen solo aliases semánticos (§3.5) — nunca hex.
|
|
856
|
+
3. Para dark/light: alternar `data-theme` en `<html>`; los tokens hacen el resto.
|
|
857
|
+
4. En el `<head>`: `<meta name="theme-color" content="#010203">` (el `--bg` nocturno) — sin él, la barra del navegador queda clara sobre la app oscura. Con el tema claro activo, actualizarlo a `#f5f8fb`.
|