@netzi/tokens 0.1.0-beta.10 → 0.1.0-beta.9

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/styles/tokens.css CHANGED
@@ -1,1348 +1,657 @@
1
1
  /* ===========================================================
2
2
  @netzi/tokens — Design tokens (CSS custom properties)
3
3
 
4
- ARCHIVO GENERADO desde la fuente DTCG (formato del W3C Design Tokens
5
- Community Group, 2025.10): no se edita a mano. Un test comprueba que no se
6
- desincronizan.
7
-
8
4
  Ejes de tema (los aplica NetziThemeService sobre <html>):
9
5
  [data-theme=dark] → tema OSCURO (default: claro)
10
- [data-theme=light] → claro también en una sección dentro de una oscura
11
6
  [data-preset=...] → color primario (blue|green|purple|orange|red|cyan)
12
- [data-contrast=high] → más contraste (bordes/texto); sin el atributo
13
- sigue a prefers-contrast: more, y normal lo fija
7
+ [data-contrast=high] → más contraste (bordes/texto)
14
8
  [data-glass=on] → capas flotantes de cristal (translúcidas)
15
- [data-ambient=on] → fondo ambiental bajo el contenido
16
9
  [data-density=compact] → controles más compactos
17
10
  dir=rtl → de derecha a izquierda (CSS usa props lógicas)
18
- prefers-reduced-motion → las duraciones de desplazamiento (move) se anulan
19
- print → papel: los ejes oscuro, cristal y ambiental no aplican
20
- (van en @media not print); sin sombras ni movimiento
21
11
  --netzi-font-sans inline → familia tipográfica
22
12
 
13
+ El PRIMARIO (`--netzi-primary*`) es themeable por preset. Los colores de
14
+ marca/semánticos (blue/green/purple/red/yellow) son constantes y se usan para
15
+ estados fijos, que además tienen su alias por lo que significan
16
+ (`--netzi-info`, `--netzi-success`, `--netzi-warning`, `--netzi-danger`).
17
+
23
18
  OVERRIDE (white-label por app): redefine cualquier token en tu propio scope.
24
19
  Las actualizaciones de este paquete cambian SOLO los defaults.
25
20
  =========================================================== */
26
21
 
27
22
  :root {
28
- /* Capa 1 — PRIMITIVOS. Valores de marca y escalas, iguales en todos los temas. Los semánticos
29
- y los de componente se apoyan en ellos. */
30
- /* Colores de marca para RELLENO: saturados, para que un fondo, un punto de estado o una barra
31
- de gráfica se vean. Como texto sobre una superficie clara no llegan al mínimo; para lo que
32
- se lee está su variante text (capa semántica). */
23
+ /* --- Acentos de marca / semánticos (constantes) --- */
33
24
  --netzi-blue: #0ba5ff;
34
25
  --netzi-blue-dark: #0476c2;
35
26
  --netzi-green: #0ae972;
36
27
  --netzi-purple: #8c4cff;
37
28
  --netzi-red: #ff6b6b;
38
29
  --netzi-yellow: #f0c040;
39
- /* Forma: radios de esquina, la materia prima de los radios por papel (capa semántica:
40
- control, surface, pill). La densidad compacta los reduce. */
41
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa --netzi-radius-surface-sm
42
- (paneles de un control, avisos, tarjetas compactas). Deprecado en @netzi/tokens
43
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
44
- --netzi-radius: 8px;
45
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa --netzi-radius-control
46
- (controles y piezas pequeñas). Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como
47
- pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
48
- --netzi-radius-sm: 6px;
49
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa --netzi-radius-surface
50
- (tarjetas, diálogos, paneles). Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como
51
- pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
52
- --netzi-radius-lg: 10px;
53
- /* Del todo redondeado (#220): la materia prima de --netzi-radius-pill. La densidad compacta
54
- no lo toca. Interna: no llegó a publicarse como pública (#228). */
55
- --netzi-radius-full: 9999px;
56
- /* Espaciado de layout (base 4px): la materia prima de los espaciados por papel (capa
57
- semántica: related, group, stack, inset, section, page). Pública solo mientras convive su
58
- desuso (#228). La densidad compacta reduce los pasos medios y grandes. */
59
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa 0, que no necesita token.
60
- Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como pronto en @netzi/tokens
61
- 0.1.0-beta.11, cuando pasa a interna. */
62
- --netzi-space-0: 0;
63
- /* deprecated: Escala primitiva, en desuso para las apps (#228): sin papel propio (2px no es
64
- un paso del ritmo de layout); entre piezas de un mismo elemento usa --netzi-space-related.
65
- Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como pronto en @netzi/tokens
66
- 0.1.0-beta.11, cuando pasa a interna. */
67
- --netzi-space-1: 2px;
68
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa --netzi-space-related
69
- (piezas de un mismo elemento). Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como
70
- pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
71
- --netzi-space-2: 4px;
72
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa --netzi-space-group
73
- (elementos hermanos de un grupo). Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como
74
- pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
75
- --netzi-space-3: 8px;
76
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa --netzi-space-stack
77
- (grupos apilados) o --netzi-space-inset-sm (relleno compacto). Deprecado en @netzi/tokens
78
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
79
- --netzi-space-4: 12px;
80
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa --netzi-space-inset
81
- (relleno de una tarjeta o un panel). Deprecado en @netzi/tokens 0.1.0-beta.10; se retira
82
- como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
83
- --netzi-space-5: 16px;
84
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa --netzi-space-inset-lg
85
- (relleno de un diálogo o un panel grande). Deprecado en @netzi/tokens 0.1.0-beta.10; se
86
- retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
87
- --netzi-space-6: 24px;
88
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa --netzi-space-section
89
- (entre secciones de una página). Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como
90
- pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
91
- --netzi-space-7: 32px;
92
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa --netzi-space-page (entre
93
- regiones de una página y su margen). Deprecado en @netzi/tokens 0.1.0-beta.10; se retira
94
- como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
95
- --netzi-space-8: 48px;
96
- /* Escala tipográfica: la materia prima de los estilos de texto (typography, capa semántica).
97
- Pública solo mientras convive su desuso (#228). */
98
- /* Escala tipográfica (#51) para el texto que NO es un control: líneas secundarias, pies,
99
- metadatos, títulos. Ratio cercano a 1.15 y md = 14px, que es el 100 % de la escala (el
100
- panel de ajustes divide entre 14). Todos escalan con el ajuste de accesibilidad; la
101
- densidad compacta no los toca: el texto no se encoge, se encoge el aire. */
102
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el tamaño de su papel,
103
- --netzi-typography-caption-font-size (#155), o su estilo de texto entero,
104
- --netzi-typography-caption. Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como pronto
105
- en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
106
- --netzi-font-size-xs: calc(11px * var(--netzi-font-scale, 1));
107
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el tamaño de su papel,
108
- --netzi-typography-body-sm-font-size (#155), o su estilo de texto entero,
109
- --netzi-typography-body-sm. Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como pronto
110
- en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
111
- --netzi-font-size-sm: calc(12.5px * var(--netzi-font-scale, 1));
112
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el tamaño de su papel,
113
- --netzi-typography-body-ui-font-size (#155), o su estilo de texto entero,
114
- --netzi-typography-body-ui. Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como pronto
115
- en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
116
- --netzi-font-size-md: calc(14px * var(--netzi-font-scale, 1));
117
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el tamaño de su papel,
118
- --netzi-typography-body-font-size (#155), o su estilo de texto entero,
119
- --netzi-typography-body. Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como pronto en
120
- @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
121
- --netzi-font-size-lg: calc(16px * var(--netzi-font-scale, 1));
122
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el tamaño de su papel,
123
- --netzi-typography-title-font-size (#155), o su estilo de texto entero,
124
- --netzi-typography-title. Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como pronto
125
- en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
126
- --netzi-font-size-xl: calc(18px * var(--netzi-font-scale, 1));
127
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el tamaño de su papel,
128
- --netzi-typography-headline-font-size (#155), o su estilo de texto entero,
129
- --netzi-typography-headline. Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como
130
- pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
131
- --netzi-font-size-2xl: calc(22px * var(--netzi-font-scale, 1));
132
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el tamaño de su papel,
133
- --netzi-typography-display-font-size (#155), o su estilo de texto entero,
134
- --netzi-typography-display. Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como pronto
135
- en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
136
- --netzi-font-size-3xl: calc(28px * var(--netzi-font-scale, 1));
137
- /* Peso. Los cinco cortes que trae la fuente cargada (fonts.css); otro valor se sintetiza y se
138
- ve peor. */
139
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el estilo de texto de su
140
- papel (--netzi-typography-body, --netzi-typography-body-sm). Deprecado en @netzi/tokens
141
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
142
- --netzi-font-weight-regular: 400;
143
- /* deprecated: Escala primitiva, en desuso para las apps (#228): sin estilo propio; usa el
144
- estilo de texto de su papel (--netzi-typography-body). Deprecado en @netzi/tokens
145
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
146
- --netzi-font-weight-medium: 500;
147
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el estilo de texto de su
148
- papel (--netzi-typography-title-sm, --netzi-typography-caption). Deprecado en @netzi/tokens
149
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
150
- --netzi-font-weight-semibold: 600;
151
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el estilo de texto de su
152
- papel (--netzi-typography-title, --netzi-typography-headline). Deprecado en @netzi/tokens
153
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
154
- --netzi-font-weight-bold: 700;
155
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el estilo de texto de su
156
- papel, --netzi-typography-display. Deprecado en @netzi/tokens 0.1.0-beta.10; se retira como
157
- pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
158
- --netzi-font-weight-extrabold: 800;
159
- /* Interlineado sin unidad: none para lo que mide una línea, tight para títulos, normal para
160
- texto corriente, relaxed para lectura larga. Materia prima de los estilos de texto
161
- (typography); pública solo mientras convive su desuso (#228). */
162
- /* deprecated: Escala primitiva, en desuso para las apps (#228): sin estilo propio; cada
163
- estilo de texto trae su interlineado (--netzi-typography-caption). Deprecado en
164
- @netzi/tokens 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando
165
- pasa a interna. */
166
- --netzi-line-height-none: 1;
167
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el estilo de texto de su
168
- papel (--netzi-typography-title, --netzi-typography-caption). Deprecado en @netzi/tokens
169
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
170
- --netzi-line-height-tight: 1.25;
171
- /* deprecated: Escala primitiva, en desuso para las apps (#228): usa el estilo de texto de su
172
- papel (--netzi-typography-body, --netzi-typography-body-sm). Deprecado en @netzi/tokens
173
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
174
- --netzi-line-height-normal: 1.5;
175
- /* deprecated: Escala primitiva, en desuso para las apps (#228): sin estilo propio; cada
176
- estilo de texto trae su interlineado (--netzi-typography-body). Deprecado en @netzi/tokens
177
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11, cuando pasa a interna. */
178
- --netzi-line-height-relaxed: 1.65;
179
- /* Capa 2 — SEMÁNTICOS. Papeles (superficie, texto, borde, primario, estado) con los valores
180
- del tema por defecto: claro, contraste normal y preset azul. Los ejes de tema (themes/) los
181
- redefinen. */
182
- /* Los colores de marca PARA TEXTO: su versión legible, oscurecida en claro y aclarada en
183
- oscuro solo donde hace falta. Si lo que pintas se lee, va el text; si es relleno, el de
184
- marca (el verde de relleno daba 1.63:1 como texto). */
30
+
31
+ /* --- Los mismos colores, pero PARA TEXTO ---
32
+ Los de arriba son de RELLENO: saturados para que un fondo, un punto de
33
+ estado o una barra de gráfica se vean. Sobre una superficie clara, como
34
+ texto, no llegan ni de lejos al mínimo (el verde daba 1.63:1). Estos son su
35
+ versión legible, oscurecida en claro y aclarada en oscuro solo donde hace
36
+ falta. Si lo que pintas se lee, va el `-text`; si es relleno, el de arriba. */
185
37
  --netzi-blue-text: #076aa3;
186
- /* Los colores de marca para lo NO textual que comunica un estado (#189): la estrella marcada,
187
- el punto de presencia, el icono de un resultado sobre su velo, el borde de un campo
188
- inválido. Llegan a 3:1 (WCAG 1.4.11) sobre cada superficie y sobre su propio velo (el veil
189
- del estado); el crudo no llega en claro (azul 2.08:1, verde 1.33, amarillo 1.41, rojo 2.15
190
- en el peor fondo) y el text cumple de sobra pero se aleja del color. Es el tono más cercano
191
- al crudo que llega; en oscuro, el crudo tal cual. */
192
- --netzi-blue-graphic: #0e84cd;
193
38
  --netzi-green-text: #05773a;
194
- --netzi-green-graphic: #069047;
195
39
  --netzi-yellow-text: #7d6421;
196
- --netzi-yellow-graphic: #9d7a0c;
197
40
  --netzi-red-text: #a34444;
198
- --netzi-red-graphic: #df464b;
199
41
  --netzi-purple-text: #743fd4;
200
- /* Alias de estado: el mismo color con el nombre de lo que significa. Existen porque toda app
201
- los da por hechos, y un token que no existe muere en silencio (la declaración entera se
202
- descarta sin aviso). Son referencias, no copias, para que sigan al color aunque cambie.
203
- Mismo reparto que la marca: el raíz es RELLENO y el text es lo que se lee. */
42
+
43
+ /* --- Alias SEMÁNTICOS de estado ---
44
+ El mismo color con el nombre de lo que significa. Existen porque toda app
45
+ los da por hechos, y un token que no existe **muere en silencio**: la
46
+ declaración entera se descarta (sin fondo, sin color, sin radio) sin un
47
+ aviso en build ni en consola. Se declaran como referencia, no como copia,
48
+ para que sigan al color aunque este cambie. Mismo reparto de papeles que
49
+ arriba: el crudo es RELLENO, el `-text` es lo que se lee. */
204
50
  --netzi-info: var(--netzi-blue);
205
51
  --netzi-success: var(--netzi-green);
206
52
  --netzi-warning: var(--netzi-yellow);
207
53
  --netzi-danger: var(--netzi-red);
208
- /* Superficies de contenido, de la más clara a la más marcada. */
209
- --netzi-surface: #ffffff;
210
- --netzi-surface-2: #f4f6f8;
211
- --netzi-surface-3: #eef1f4;
212
- /* Texto: tres niveles con papeles DISTINTOS, no tres grados de gris intercambiables. text es
213
- el texto principal; text-2 es CUALQUIER texto secundario legible (rótulos, ayudas, pies,
214
- metadatos, placeholders) y es el suelo de 4.5:1; text-3 es lo NO textual (iconos
215
- decorativos, separadores, chevrons, líneas, estados deshabilitados) y cumple 3:1, no 4.5:1.
216
- El papel de cada uno lo fija su contrato de contraste y lo comprueba el build: si lo que
217
- pintas se lee, va text-2. */
218
- --netzi-text: #1c252e;
219
- --netzi-text-2: #5c6b78;
220
- /* NO vale para texto legible (3:1, SC 1.4.11). Es el tono de lo que acompaña: chevrons,
221
- separadores, iconos decorativos, la barra de redimensionar, el brillo del esqueleto de
222
- carga y los estados deshabilitados. La regla existe porque con 45 archivos usándolo
223
- indistintamente el kit entero se quedó por debajo del mínimo sin que nadie lo notara. */
224
- --netzi-text-3: #7a8996;
225
- /* Radios por papel (#228): el de lo que se pulsa, el de una superficie y el de lo del todo
226
- redondeado. Apuntan a la escala de forma, así que la densidad compacta los reduce (salvo la
227
- píldora); se redeclaran en cada elemento con densidad propia. */
228
- /* Del todo redondeado: píldoras, puntos, avatares e interruptores; en una pieza cuadrada, un
229
- círculo. Sustituye a los 999px y 50% sueltos (#220). La densidad no lo toca. */
230
- --netzi-radius-pill: var(--netzi-radius-full);
231
- /* Color PRIMARIO, themeable por preset (eje preset); por defecto, el azul de marca. */
54
+ --netzi-info-text: var(--netzi-blue-text);
55
+ --netzi-success-text: var(--netzi-green-text);
56
+ --netzi-warning-text: var(--netzi-yellow-text);
57
+ --netzi-danger-text: var(--netzi-red-text);
58
+
59
+ /* --- Alias por SIMETRÍA ---
60
+ El primer escalón de cada escala no lleva número (`--netzi-surface` y
61
+ `--netzi-surface-2`), y quien ve el `-2` escribe el `-1`. Igual con el radio
62
+ medio, que se llama `--netzi-radius` a secas. Son el mismo valor con el
63
+ nombre que la app espera, por el mismo motivo que los de arriba. */
64
+ --netzi-surface-1: var(--netzi-surface);
65
+ --netzi-text-1: var(--netzi-text);
66
+ --netzi-radius-md: var(--netzi-radius);
67
+
68
+ /* --- Color PRIMARIO (themeable por preset; default = azul) --- */
232
69
  --netzi-primary: var(--netzi-blue);
233
- /* El relleno de lo marcado en checkbox, radio y toggle: frente a cualquier superficie, no
234
- solo al fondo de la página (#147). Lleva on-primary-dark encima. */
235
70
  --netzi-primary-dark: var(--netzi-blue-dark);
236
- /* El relleno del primario al pasar el puntero, con la MISMA tinta encima (on-primary). No es
237
- primary-dark: ese es el fondo de lo que lleva blanco (pulgar, marca del checkbox), y la
238
- tinta oscura sobre él se quedaba en 3.93:1 en azul y 4.0:1 en rojo (#68). Cada preset
239
- declara el suyo: más oscuro que su primario, pero sin bajar de 4.5:1 con su tinta. */
71
+ /* El relleno del primario al pasar el puntero, con la MISMA tinta encima
72
+ (`--netzi-on-primary`). No es `--netzi-primary-dark`: ese es el fondo de lo que
73
+ lleva blanco (pulgar, marca del checkbox), y la tinta oscura sobre él se quedaba
74
+ en 3.93:1 en azul y 4.0:1 en rojo (#68). Cada preset declara el suyo: más oscuro
75
+ que su primario, pero sin bajar de 4.5:1 con su tinta. */
240
76
  --netzi-primary-hover: #0a8fdd;
241
- /* El relleno del primario PRESIONADO (#156), con la misma tinta encima (on-primary). Es su
242
- hover con la capa de presionado (state.pressed.opacity, 12 %) del color que se aleja de su
243
- tinta: blanco si la tinta es oscura, negro si es blanca. No lleva la capa de texto de los
244
- demás controles porque sobre un relleno de marca acerca el fondo a la tinta y la dejaba en
245
- 3.8:1; así el presionado se distingue del hover y la tinta no baja de 4.5:1 en ningún
246
- preset ni tema. Cada preset declara el suyo. */
247
- --netzi-primary-pressed: #279ce1;
248
- /* El primario PARA TEXTO (enlaces, ítem activo del menú, cifras destacadas). Cada preset y
249
- cada tema lo redefinen: el crudo no llega a 4.5:1 sobre superficie clara. */
250
- --netzi-primary-text: #076ba6;
251
- /* El primario para lo NO textual que tiene que verse (#140): el relleno de una barra de
252
- progreso, el anillo de carga del rail, la barra de navegación. Llega a 3:1 (WCAG 1.4.11)
253
- sobre cada superficie y sobre el tinte del primario, que es el fondo del ítem activo. El
254
- crudo no llega en claro con azul (2.15:1), verde (1.36), naranja (1.78), rojo (2.51) ni
255
- cian (1.92), y primary-text cumple de sobra pero se aleja del color de marca: este es el
256
- tono más cercano al primario que llega, calibrado por preset y por tema. Algo que LATE en
257
- opacidad no vale con este: en el valle del latido baja de 3:1, y por eso esos indicadores
258
- van con primary-text. */
259
- --netzi-primary-graphic: #0987d2;
260
- /* La TINTA sobre el relleno del primario: cada preset usa la que cumple sobre SU color, no
261
- una fija (el blanco sobre el azul da 2.68:1 y la tinta oscura 7.05:1). */
77
+ /* La TINTA sobre el relleno del primario. Cada preset usa la que cumple sobre
78
+ SU color, no una fija: el blanco sobre el azul da 2.68:1 y la tinta oscura
79
+ 7.05:1. Verde, naranja y cyan ya venían así; el azul y el rojo se suman. */
262
80
  --netzi-on-primary: #04121f;
263
- /* Lo que va ENCIMA de primary-dark: el pulgar del interruptor, la marca del checkbox, el
264
- punto del radio. Blanco en todos los presets: el primario oscuro es lo bastante oscuro para
265
- que el blanco pase de 3:1 (el mínimo de un control) en cada uno. on-primary no sirve para
266
- esto: sobre el azul claro tiene que ser casi negro, y un pulgar negro sobre un interruptor
267
- azul se ve raro. */
81
+ /* El primario PARA TEXTO (enlaces, ítem activo del menú, cifras destacadas).
82
+ Cada preset lo redefine: el crudo no llega a 4.5:1 sobre superficie clara. */
83
+ --netzi-primary-text: #076ba6;
84
+ /* Lo que va ENCIMA de `--netzi-primary-dark`: el pulgar del interruptor, la
85
+ marca del checkbox, el punto del radio. Es blanco en TODOS los presets: el
86
+ primario oscuro es lo bastante oscuro para que el blanco pase de 3:1 (el
87
+ mínimo de un control) en cada uno —3.2 en naranja, 3.3 en verde, 4.7+ en
88
+ azul, rojo y morado—; lo verifica `tokens-contrast.spec.ts`. `--netzi-on-primary`
89
+ no sirve para esto: sobre el azul claro tiene que ser casi negro, y un pulgar
90
+ negro sobre un interruptor azul se ve raro (queja literal del usuario). */
268
91
  --netzi-on-primary-dark: #ffffff;
269
- /* Tinta sobre un relleno de color de marca (verde, rojo, amarillo…): todos son claros, así
270
- que la oscura es la que cumple. El blanco daba 2.78:1 sobre el rojo del contador de
271
- notificaciones. */
272
- --netzi-on-accent: #04121f;
273
- /* Estados de interacción como UN sistema (#156, criterio B.7): una capa translúcida por
274
- estado, que se pinta ENCIMA del fondo del control sin cambiarlo, con su opacidad. La capa
275
- es del color del texto (text), así que oscurece en claro y aclara en oscuro, y la etiqueta
276
- en text sigue a 4.5:1 sobre cada superficie con cualquier capa encima: el build lo
277
- comprueba. El orden de las opacidades es el del gesto, de menos a más: hover 8 %, foco 12
278
- %, presionado 12 % y arrastrado 16 %. Las opacidades van aparte (state.*.opacity) para la
279
- plataforma que pinta la capa con su propio color; en CSS la capa ya es un color
280
- (--netzi-state-pressed) y se pinta como imagen de fondo, que no se anima: aparece al
281
- instante, también con movimiento reducido y con el dedo. Un control con fondo de marca no
282
- lleva capa: tiene su propio color presionado (primary.pressed). El foco, además, conserva
283
- su anillo (focus-ring), que es lo que exige WCAG 2.4.7; el seleccionado es el tinte del
284
- primario. */
285
- --netzi-state-hover-opacity: 0.08;
286
- --netzi-state-focus-opacity: 0.12;
287
- --netzi-state-pressed-opacity: 0.12;
288
- --netzi-state-dragged-opacity: 0.16;
289
- /* El tratamiento de lo DESHABILITADO: una sola opacidad sobre el control entero (antes había
290
- seis, de 0.3 a 0.8). Un control deshabilitado no recibe el foco de teclado ni actúa, pero
291
- el lector de pantalla lo lee y lo anuncia como no disponible (disabled o aria-disabled).
292
- WCAG no pide contraste a lo deshabilitado; aun así su texto en text se queda a 3:1 sobre
293
- cada superficie, para que se vea que está ahí: el build lo comprueba. */
294
- --netzi-state-disabled-opacity: 0.6;
295
- /* Fondo de la página. */
92
+ /* El pulgar de un interruptor o de un deslizador. OPACO y literal a propósito:
93
+ iba con `--netzi-surface`, y dentro de un panel de cristal esa superficie es
94
+ un tinte translúcido (re-escopada por el panel), así que el pulgar salía como
95
+ un agujero oscuro con un aro. Blanco en los dos temas, como el de cualquier
96
+ sistema: sobre la pista apagada (`surface-3`) y sobre la encendida
97
+ (`primary-dark`) se ve igual de bien. */
98
+ --netzi-thumb: #ffffff;
99
+ /* Tinte del primario, derivado automáticamente (cambia con el preset). */
100
+ --netzi-primary-tint: color-mix(in srgb, var(--netzi-primary) 10%, transparent);
101
+ /* El mismo tinte, MARCADO. El del 10 % está calibrado para superficies
102
+ grandes (una fila resaltada, un panel); en una pieza pequeña —un chip, una
103
+ píldora— se pierde contra el fondo: sobre `surface-3` da 1.13:1 y no se
104
+ distingue. Este llega a 1.32:1, que sí se ve.
105
+ ⚠️ Su tinta es `--netzi-text` (≥9:1 con cualquier preset). NO vale
106
+ `--netzi-primary-text` encima: sobre este tinte se queda en 3.9:1, y por eso
107
+ el tinte suave no se puede subir sin más — es el techo del otro. */
108
+ --netzi-primary-tint-strong: color-mix(in srgb, var(--netzi-primary) 22%, transparent);
109
+
110
+ /* --- Tema CLARO: superficies --- */
296
111
  --netzi-bg: #f7f9fb;
297
- /* Bordes. */
112
+ --netzi-surface: #ffffff;
113
+ --netzi-surface-2: #f4f6f8;
114
+ --netzi-surface-3: #eef1f4;
115
+
116
+ /* --- Tema CLARO: bordes --- */
298
117
  --netzi-border: #eaedf0;
299
118
  --netzi-border-2: #dce0e5;
300
- /* Elevación (#220): cuánto se despega una pieza de lo que tiene debajo, con nombre de
301
- intención y de menos a más. En la web se pinta con sombra (y con el borde de cada pieza:
302
- una sombra nunca es lo único que separa una capa); en papel no hay ninguna. Cada plataforma
303
- la pinta a su manera, con el mismo nivel. */
304
- /* Base: pegado a su superficie, sin sombra. Para devolver a cero una pieza que hereda
305
- elevación. */
306
- --netzi-elevation-base: 0 0 rgba(0, 0, 0, 0);
307
- /* Elevado: una pieza levantada sobre su propia superficie, como el pulgar de un interruptor o
308
- de un deslizador, la pastilla del segmentado o una tecla. */
309
- --netzi-elevation-raised: 0 1px 2px rgba(145, 158, 171, 0.16);
310
- /* Flotante: algo pequeño y suelto que flota sobre el contenido, como el tooltip, la etiqueta
311
- de una gráfica, el enlace «Ir al contenido» o la esfera del reloj. */
312
- --netzi-elevation-floating:
313
- 0 0 2px rgba(145, 158, 171, 0.2), 0 12px 24px -4px rgba(145, 158, 171, 0.12);
314
- /* Modal: las capas que se abren encima del contenido y lo apartan, como los desplegables, los
315
- menús, el aviso, los diálogos y los paneles laterales. */
316
- --netzi-elevation-modal:
317
- 0 0 2px rgba(145, 158, 171, 0.2), 0 16px 40px -8px rgba(145, 158, 171, 0.24);
318
- /* Espaciado por papel (#228), de lo más cercano a lo más lejano: cuanto más relacionadas dos
319
- cosas, menos aire entre ellas (proximidad). Apuntan a la escala primitiva de base 4px; la
320
- densidad compacta reduce desde stack hacia arriba, y por eso esos se redeclaran en cada
321
- elemento con densidad propia. */
322
- /* Entre piezas de un mismo elemento: icono y texto, los chips de una lista, una rejilla de
323
- muestras, un campo y su ayuda. */
324
- --netzi-space-related: var(--netzi-space-2);
325
- /* Entre elementos hermanos de un grupo: los botones de una barra, la cabecera de un bloque y
326
- lo que tiene debajo. */
327
- --netzi-space-group: var(--netzi-space-3);
328
- /* Familias y escala de letra: papeles, no pasos. La app puede cambiar la familia en línea
329
- sobre la raíz; la escala la cambia el ajuste de tamaño de letra. */
330
- /* Familia tipográfica del texto. La app puede cambiarla en línea sobre la raíz. */
331
- --netzi-font-sans:
332
- 'Plus Jakarta Sans', 'Plus Jakarta Sans Fallback', -apple-system, system-ui, sans-serif;
333
- /* Familia monoespaciada: código, identificadores y cifras que se comparan columna a columna. */
334
- --netzi-font-mono: 'Fira Code', 'Fira Code Fallback', ui-monospace, monospace;
335
- /* Escala global del tamaño de letra (1 = 100 %). Los tamaños con fontScale la multiplican; la
336
- cambia el panel de ajustes (ajuste de accesibilidad). */
337
- --netzi-font-scale: 1;
338
- /* Estilos de texto (#220): familia, tamaño, peso, interlineado y espaciado entre letras
339
- juntos, con el nombre de su papel; cada paso de la escala tiene al menos uno. En CSS cada
340
- uno sale como el atajo font (que no incluye el espaciado), su -letter-spacing aparte y su
341
- -font-size, el tamaño solo, para quien pone el peso y el interlineado por su cuenta (#155).
342
- Todo escala con el ajuste de tamaño de letra: el tamaño porque es el de la escala y el
343
- espaciado porque el estilo lleva fontScale. */
344
- /* Cifras grandes y portadas: 3xl, extrabold, apretado. */
345
- --netzi-typography-display: var(--netzi-font-weight-extrabold) var(--netzi-font-size-3xl) /
346
- var(--netzi-line-height-tight) var(--netzi-font-sans);
347
- --netzi-typography-display-letter-spacing: calc(-0.5px * var(--netzi-font-scale, 1));
348
- --netzi-typography-display-font-size: var(--netzi-font-size-3xl);
349
- /* Títulos de página: 2xl, bold, apretado. */
350
- --netzi-typography-headline: var(--netzi-font-weight-bold) var(--netzi-font-size-2xl) /
351
- var(--netzi-line-height-tight) var(--netzi-font-sans);
352
- --netzi-typography-headline-letter-spacing: calc(-0.2px * var(--netzi-font-scale, 1));
353
- --netzi-typography-headline-font-size: var(--netzi-font-size-2xl);
354
- /* Títulos de sección: xl, bold, apretado. */
355
- --netzi-typography-title: var(--netzi-font-weight-bold) var(--netzi-font-size-xl) /
356
- var(--netzi-line-height-tight) var(--netzi-font-sans);
357
- --netzi-typography-title-letter-spacing: 0;
358
- --netzi-typography-title-font-size: var(--netzi-font-size-xl);
359
- /* Texto destacado y títulos de tarjeta: lg, semibold, apretado. */
360
- --netzi-typography-title-sm: var(--netzi-font-weight-semibold) var(--netzi-font-size-lg) /
361
- var(--netzi-line-height-tight) var(--netzi-font-sans);
362
- --netzi-typography-title-sm-letter-spacing: 0;
363
- --netzi-typography-title-sm-font-size: var(--netzi-font-size-lg);
364
- /* Texto corrido (#219, ADR 0015): párrafos, descripciones de bloque y contenido que se lee
365
- como prosa. lg (16 px, política 17 § 4), regular, interlineado normal. */
366
- --netzi-typography-body: var(--netzi-font-weight-regular) var(--netzi-font-size-lg) /
367
- var(--netzi-line-height-normal) var(--netzi-font-sans);
368
- --netzi-typography-body-letter-spacing: 0;
369
- --netzi-typography-body-font-size: var(--netzi-font-size-lg);
370
- /* Texto de interfaz (#219, ADR 0015): opciones de menú y de lista, pestañas, celdas de tabla
371
- y mensajes breves, lo que se recorre con la vista más que leerse. md (14 px), regular,
372
- interlineado normal. */
373
- --netzi-typography-body-ui: var(--netzi-font-weight-regular) var(--netzi-font-size-md) /
374
- var(--netzi-line-height-normal) var(--netzi-font-sans);
375
- --netzi-typography-body-ui-letter-spacing: 0;
376
- --netzi-typography-body-ui-font-size: var(--netzi-font-size-md);
377
- /* Líneas secundarias, pistas, pies y celdas compactas: sm, regular, interlineado normal. */
378
- --netzi-typography-body-sm: var(--netzi-font-weight-regular) var(--netzi-font-size-sm) /
379
- var(--netzi-line-height-normal) var(--netzi-font-sans);
380
- --netzi-typography-body-sm-letter-spacing: 0;
381
- --netzi-typography-body-sm-font-size: var(--netzi-font-size-sm);
382
- /* Metadatos y etiquetas cortas: xs, semibold, apretado y algo abierto (en mayúsculas, el
383
- text-transform lo pone quien lo usa). */
384
- --netzi-typography-caption: var(--netzi-font-weight-semibold) var(--netzi-font-size-xs) /
385
- var(--netzi-line-height-tight) var(--netzi-font-sans);
386
- --netzi-typography-caption-letter-spacing: calc(0.5px * var(--netzi-font-scale, 1));
387
- --netzi-typography-caption-font-size: var(--netzi-font-size-xs);
388
- /* Movimiento: duraciones por intención (#151). instant para la respuesta a una pulsación,
389
- fast para el hover y los cambios de color, base para lo que se despliega o gira, slow para
390
- los paneles y capas que entran. Son las de los fundidos: no cambian con movimiento
391
- reducido. Lo que desplaza, gira, escala o cambia la geometría usa las de move, que con
392
- movimiento reducido se anulan. flash y loop no son cambios de estado y quedan fuera de esa
393
- escala (#214). */
394
- --netzi-duration-instant: 100ms;
395
- --netzi-duration-fast: 150ms;
396
- --netzi-duration-base: 200ms;
397
- --netzi-duration-slow: 280ms;
398
- /* Un aviso que se enciende, se deja ver y se apaga solo, sin que nadie lo cierre (el destello
399
- de error de un ítem del menú). Largo a propósito: tiene que dar tiempo a verlo. */
400
- --netzi-duration-flash: 1400ms;
401
- /* Bucles (#214): la duración de UNA vuelta de una animación infinita, no la de un cambio de
402
- estado. spin para el giro de un indicador de carga; orbit para el anillo que recorre el
403
- borde de un ítem o de una pestaña que carga; pulse para el latido de opacidad que sustituye
404
- al giro con movimiento reducido; sweep para la barra de progreso indeterminada; shine para
405
- el brillo del skeleton. No cambian con movimiento reducido: cada hoja quita o sustituye su
406
- bucle en su propia regla. */
407
- --netzi-duration-loop-spin: 700ms;
408
- --netzi-duration-loop-orbit: 1100ms;
409
- --netzi-duration-loop-pulse: 1200ms;
410
- --netzi-duration-loop-sweep: 1200ms;
411
- --netzi-duration-loop-shine: 1300ms;
412
- /* Duraciones del DESPLAZAMIENTO (#151): transform, translate, rotate, scale, la posición y la
413
- geometría (alturas, filas de rejilla, márgenes). Valen lo mismo que fast, base y slow, pero
414
- con prefers-reduced-motion se anulan en un solo sitio (themes/motion) y lo que las usa
415
- salta sin moverse, mientras los fundidos de color y opacidad conservan su duración. */
416
- --netzi-duration-move-fast: var(--netzi-duration-fast);
417
- --netzi-duration-move-base: var(--netzi-duration-base);
418
- --netzi-duration-move-slow: var(--netzi-duration-slow);
419
- /* Movimiento: curvas por intención (#151). standard para lo que cambia sin entrar ni salir
420
- (la de ease de CSS), enter para lo que aparece (frena al llegar), exit para lo que se va
421
- (acelera al irse), settle para lo que llega, se pasa un poco y se asienta (un rebote
422
- sutil). Para los bucles (#214): swing para lo que va y vuelve, arrancando y frenando en
423
- cada vuelta (la de ease-in-out: latidos, barridos, brillos), y spin para lo que gira sin
424
- fin a velocidad constante (la de linear). */
425
- --netzi-ease-standard: cubic-bezier(0.25, 0.1, 0.25, 1);
426
- --netzi-ease-enter: cubic-bezier(0.16, 1, 0.3, 1);
427
- --netzi-ease-exit: cubic-bezier(0.4, 0, 1, 1);
428
- --netzi-ease-settle: cubic-bezier(0.34, 1.45, 0.5, 1);
429
- --netzi-ease-swing: cubic-bezier(0.42, 0, 0.58, 1);
430
- --netzi-ease-spin: cubic-bezier(0, 0, 1, 1);
431
- /* MEDIDA legible del texto corrido (#213, criterio C.9): el ancho máximo de un párrafo, una
432
- descripción o un aviso, para que cada línea se quede en unos 40 a 75 caracteres (64 de
433
- mediana) en cualquier ancho de ventana. Se aplica al TEXTO (max-inline-size en el párrafo),
434
- nunca a la página ni a la sección: el hueco junto al texto lo llenan el contenido auxiliar,
435
- las acciones, otro panel, la rejilla o la prosa en columnas entre párrafos; un párrafo no
436
- se parte en columnas, y una descripción corta puede dejar hueco. En em, que escala con el
437
- tamaño de letra del propio párrafo, y NO en ch: el ch es el ancho del cero de la fuente que
438
- pinta en ese momento, y el de la fuente de respaldo mide un 21 % menos que el de Plus
439
- Jakarta Sans, así que al llegar la fuente de marca cada párrafo acotado cambiaba de ancho y
440
- rehacía sus líneas (C.12). Solo CSS: el formato DTCG no admite em, y en móvil la medida la
441
- dan los paneles y las clases de tamaño. */
442
- --netzi-measure: 33em;
443
- /* Capas de apilamiento (#215): el z-index de lo que se pinta encima de otra cosa sale de
444
- aquí, con nombre de intención y en este orden, de abajo arriba. Un componente no escribe
445
- números sueltos: dentro de su propia capa solo usa -1, 0 o 1, o un escalón sobre su capa
446
- con calc(). Los huecos entre valores dejan sitio a la app sin tener que mover la escala. */
447
- /* El contenido de la página: el punto de partida. */
448
- --netzi-layer-content: 0;
449
- /* Pegajoso: encabezados y columnas fijas que se quedan quietos mientras el contenido desplaza
450
- por debajo (la tabla de datos). */
451
- --netzi-layer-sticky: 2;
452
- /* Barra: lo del marco de la app que pasa por encima del contenido, como el botón de plegar el
453
- menú lateral o la barra de carga. Una barra fija de la app va aquí, por encima de lo
454
- pegajoso. */
455
- --netzi-layer-bar: 20;
456
- /* Flotante del marco: submenús del menú lateral plegado y menús contextuales anclados al
457
- contenido. Por debajo del velo: un modal los tapa. */
458
- --netzi-layer-flyout: 50;
459
- /* Velo: lo que apaga la página detrás de un panel lateral que se abre por encima (el menú
460
- lateral en el móvil, que va un escalón por encima de su velo). */
461
- --netzi-layer-scrim: 80;
462
- /* Modal: diálogos y paneles laterales, con su velo dentro. */
463
- --netzi-layer-modal: 100;
464
- /* Desplegable: paneles anclados a un control (select, fecha, hora, autocompletado, popover,
465
- cron) y la paleta de comandos. Por encima del modal: la paleta de comandos se abre sobre
466
- cualquier diálogo. Dentro de un modal o de un panel lateral, un desplegable ya va en la
467
- capa de su contenedor. */
468
- --netzi-layer-dropdown: 120;
469
- /* Aviso: el toast, que tiene que verse aunque haya un modal o un desplegable abiertos. */
470
- --netzi-layer-toast: 200;
471
- /* Tooltip: la etiqueta de lo que tiene el puntero o el foco, encima de cualquier capa
472
- interactiva. */
473
- --netzi-layer-tooltip: 300;
474
- /* Bloqueante: la pantalla que impide usar la app hasta que se resuelve algo (sin conexión).
475
- Tapa todo lo anterior. */
476
- --netzi-layer-blocking: 1000;
477
- /* Tope: lo que nunca puede quedar tapado, como el enlace «Ir al contenido» cuando recibe el
478
- foco. */
479
- --netzi-layer-top: 10000;
480
- /* TAMAÑOS DE VENTANA con nombre (#157, ADR 0016, criterio C.8): los cortes de ancho de la
481
- ventana en los que cambia el diseño, siempre los mismos en el kit y en las apps. Cada
482
- valor, salvo reflow, es el ancho en el que EMPIEZA ese tamaño: compacto (menos de 600, un
483
- panel), medio (600 a 839, uno o dos), expandido (840 a 1199, dos), grande (1200 a 1599,
484
- dos, con el menú lateral completo) y extragrande (1600 o más, hasta tres). Una var() no
485
- vale dentro de @media: en las hojas se escribe el número del token en forma de rango,
486
- (width < 600px) o (width >= 600px), y un guardián del kit rechaza cualquier otro corte de
487
- ventana. Lo que depende de su propia caja y no de la ventana usa una consulta de contenedor
488
- con nombre. */
489
- /* Ancho de reflujo: por debajo (teléfonos de 320 y 360 px y el zoom al 400 % de WCAG 1.4.10)
490
- lo que va en fila dentro de un panel flotante se apila. Es un corte dentro de compacto, no
491
- un tamaño de ventana: no cambia el número de paneles. */
492
- --netzi-window-reflow: 375px;
493
- /* Empieza el tamaño medio: uno o dos paneles, y el menú lateral adaptable (netzi-layout con
494
- adaptive) pasa de cajón a riel. */
495
- --netzi-window-medium: 600px;
496
- /* Empieza el tamaño expandido: dos paneles, lista y detalle a la vez. */
497
- --netzi-window-expanded: 840px;
498
- /* Empieza el tamaño grande: dos paneles, y el menú lateral adaptable vuelve a ir completo,
499
- con sus etiquetas. */
500
- --netzi-window-large: 1200px;
501
- /* Empieza el tamaño extragrande: hasta tres paneles (lista, detalle y uno de apoyo). */
502
- --netzi-window-extra-large: 1600px;
503
- /* Capa 3 — DE COMPONENTE. Tokens con un uso concreto: capas flotantes, velo modal, indicador
504
- de foco, pulgar de los interruptores y medidas de los controles. */
505
- /* El pulgar de un interruptor o de un deslizador. OPACO y literal a propósito: iba con
506
- surface, y dentro de un panel de cristal esa superficie es un tinte translúcido, así que el
507
- pulgar salía como un agujero oscuro con un aro. Blanco en los dos temas, como el de
508
- cualquier sistema. */
509
- --netzi-thumb: #ffffff;
510
- /* Capas FLOTANTES: las que se pintan ENCIMA del contenido (topbar, sidebar, paneles anclados,
511
- modal, drawer, menús, tooltips y toasts). Por defecto son exactamente la superficie opaca
512
- de siempre; el eje glass las convierte en cristal sin que ningún componente tenga que
513
- enterarse. Los valores van literales, no como referencia a surface, porque el cristal los
514
- redefine y una referencia cruzada crearía un ciclo de custom properties. */
119
+
120
+ /* --- Capas FLOTANTES ---
121
+ Las que se pintan ENCIMA del contenido: topbar, sidebar, paneles anclados,
122
+ modal, drawer, menús, tooltips y toasts. Por defecto son exactamente la
123
+ superficie opaca de siempre; el eje [data-glass=on] las convierte en
124
+ cristal sin que ningún componente tenga que enterarse (ver GLASS abajo).
125
+ Los valores van literales, no como `var(--netzi-surface)`, porque el modo
126
+ cristal los redefine y una referencia cruzada crearía un ciclo. */
515
127
  --netzi-float-bg: #ffffff;
516
128
  --netzi-float-border: #eaedf0;
517
129
  --netzi-float-border-2: #dce0e5;
518
- /* Superficies ANIDADAS dentro de una capa flotante (una tarjeta en el drawer, la burbuja de
519
- un comentario, el hover de un botón). Sin cristal son las de siempre; con cristal, tintes
520
- translúcidos. Los paneles las re-escopan como surface para que todo lo que llevan dentro
521
- las herede. */
130
+ /* Superficies ANIDADAS dentro de una capa flotante (una tarjeta en el drawer,
131
+ la burbuja de un comentario en el popover, el hover de un botón). Sin
132
+ cristal son las superficies de siempre; con cristal, tintes translúcidos
133
+ (ver el bloque de cristal). Los paneles las re-escopan como
134
+ `--netzi-surface*` para que TODO lo que llevan dentro las herede. */
522
135
  --netzi-float-surface: #ffffff;
523
136
  --netzi-float-surface-2: #f4f6f8;
524
137
  --netzi-float-surface-3: #eef1f4;
525
138
  --netzi-float-filter: none;
526
- /* La MISMA cadena que float-filter, en identidad: el punto del que parte la entrada de un
527
- panel de cristal y al que llega su salida. El desenfoque se anima de 0 a su valor, porque
528
- un fundido por opacity apaga el cristal justo mientras entra. Mismo orden de funciones que
529
- el token real: si no coinciden, no se interpola. */
139
+ /* La MISMA cadena que `--netzi-float-filter`, en identidad. Es el punto del
140
+ que PARTE la entrada de un panel de cristal y al que llega su salida: el
141
+ desenfoque se anima de 0 a su valor (un tirón de foco), porque un fundido
142
+ por `opacity` no vale — con opacidad < 1 el elemento (o su capa) deja de
143
+ ver la página y el cristal se apaga justo mientras entra. Mismo orden de
144
+ funciones que el token real: si no coinciden, no se interpola. */
530
145
  --netzi-float-filter-identity: saturate(1) contrast(1) brightness(1) blur(0);
531
146
  --netzi-float-glow: 0 0 rgba(0, 0, 0, 0);
532
- /* VELO de las capas modales: lo que se pinta DETRÁS de un modal, un drawer o la paleta de
533
- comandos para apagar el contenido. Es un token porque cada componente lo llevaba escrito a
534
- mano y no coincidían. */
147
+
148
+ /* --- VELO de las capas modales ---
149
+ Lo que se pinta DETRÁS de un modal, un drawer o la paleta ⌘K para apagar el
150
+ contenido. Es un token porque cada componente lo llevaba escrito a mano y no
151
+ coincidían: modal, drawer y ⌘K iban a `rgba(8,12,20,.55)` con `blur(2px)` y
152
+ `connection-guard` a `.62` con `blur(3px)`, así que la misma app se oscurecía
153
+ distinto según qué capa la tapara. */
535
154
  --netzi-scrim: rgba(8, 12, 20, 0.28);
536
155
  --netzi-scrim-filter: blur(1px);
537
- /* Controles. py y px son el relleno interior; h, px y fs por tamaño (sm, md, lg) dan una
538
- altura FIJA para que todos los campos y el botón midan lo mismo y alineen en fila. Las
539
- alturas y los tamaños de letra escalan con font-scale (ajuste de accesibilidad) para no
540
- recortar el texto. La densidad compacta los reduce. */
156
+
157
+ /* --- Tema CLARO: texto ---
158
+ Tres niveles con papeles DISTINTOS, no tres grados de gris intercambiables
159
+ (ver la tabla de la sección TEXTO al final del archivo):
160
+ text → texto principal.
161
+ text-2 → CUALQUIER texto secundario legible (rótulos, ayudas, pies,
162
+ metadatos, placeholders). Es el suelo de 4.5:1 para texto.
163
+ text-3 → NO textual: iconos decorativos, separadores, chevrons, líneas y
164
+ estados deshabilitados. Cumple 3:1 (SC 1.4.11), no 4.5:1. */
165
+ --netzi-text: #1c252e;
166
+ --netzi-text-2: #5c6b78;
167
+ --netzi-text-3: #7a8996;
168
+ /* Tinta sobre un relleno de color de marca (verde, rojo, amarillo…): todos son
169
+ claros, así que la oscura es la que cumple. El blanco daba 2.78:1 sobre el
170
+ rojo del contador de notificaciones. */
171
+ --netzi-on-accent: #04121f;
172
+
173
+ /* --- Tema CLARO: sombras --- */
174
+ --netzi-shadow-sm: 0 1px 2px rgba(145, 158, 171, 0.16);
175
+ --netzi-shadow: 0 0 2px rgba(145, 158, 171, 0.2), 0 12px 24px -4px rgba(145, 158, 171, 0.12);
176
+ --netzi-shadow-lg: 0 0 2px rgba(145, 158, 171, 0.2), 0 16px 40px -8px rgba(145, 158, 171, 0.24);
177
+
178
+ /* --- Tinte de acento (alias histórico del tinte del primario) --- */
179
+ --netzi-tint: var(--netzi-primary-tint);
180
+
181
+ /* --- Forma --- */
182
+ --netzi-radius-sm: 6px;
183
+ --netzi-radius: 8px;
184
+ --netzi-radius-lg: 10px;
185
+
186
+ /* --- Espaciado ---
187
+ Escala de gaps/paddings de layout (base 4px). Usa estos tokens en la app en
188
+ vez de píxeles/rem sueltos para mantener el ritmo del sistema. La densidad
189
+ compacta reduce los pasos medios/grandes. */
190
+ --netzi-space-0: 0;
191
+ --netzi-space-1: 2px;
192
+ --netzi-space-2: 4px;
193
+ --netzi-space-3: 8px;
194
+ --netzi-space-4: 12px;
195
+ --netzi-space-5: 16px;
196
+ --netzi-space-6: 24px;
197
+ --netzi-space-7: 32px;
198
+ --netzi-space-8: 48px;
199
+
200
+ /* --- Densidad de controles (la cambia [data-density=compact]) --- */
541
201
  --netzi-control-py: 12px;
542
202
  --netzi-control-px: 14px;
543
- --netzi-control-px-sm: 12px;
544
- --netzi-control-px-md: 14px;
545
- --netzi-control-px-lg: 16px;
203
+
204
+ /* --- Tamaño estándar de controles (sm/md/lg) ---
205
+ Altura FIJA por tamaño para que todos los inputs de valor y el botón midan
206
+ exactamente lo mismo y alineen al ponerlos en fila. Escalan con
207
+ `--netzi-font-scale` (ajuste de accesibilidad) para no recortar el texto.
208
+ La densidad compacta las reduce. */
546
209
  --netzi-control-h-sm: calc(36px * var(--netzi-font-scale, 1));
547
210
  --netzi-control-h-md: calc(44px * var(--netzi-font-scale, 1));
548
211
  --netzi-control-h-lg: calc(52px * var(--netzi-font-scale, 1));
212
+ --netzi-control-px-sm: 12px;
213
+ --netzi-control-px-md: 14px;
214
+ --netzi-control-px-lg: 16px;
549
215
  --netzi-control-fs-sm: calc(13.5px * var(--netzi-font-scale, 1));
550
216
  --netzi-control-fs-md: calc(15px * var(--netzi-font-scale, 1));
551
217
  --netzi-control-fs-lg: calc(16px * var(--netzi-font-scale, 1));
552
- /* Objetivo táctil mínimo (#100): lado mínimo de lo que se toca con el dedo (WCAG 2.5.5). Con
553
- puntero táctil los controles no bajan de aquí, ni en tamaño sm ni con densidad compacta. */
554
- --netzi-touch-target: 44px;
555
- /* Separación táctil mínima (#210): hueco entre las zonas de toque de dos objetivos vecinos
556
- con puntero táctil (política 17, criterio A.5), medido entre zonas y no entre los dibujos.
557
- Cada componente la usa en su media pointer coarse; con ratón no cambia nada. Las piezas de
558
- un control segmentado se tocan y quedan fuera. */
559
- --netzi-touch-gap: 8px;
560
- /* Paleta CATEGÓRICA de las gráficas (#175): ocho colores para ocho series, sin repetirse, en
561
- el orden en que se asignan. Fija y no themeable por preset a propósito: si la serie 1 fuera
562
- el primario, con el preset verde o rojo chocaría con la serie del mismo tono, que era justo
563
- el fallo de la paleta anterior (azul repetido en las series 1 y 6). Elegida para que los
564
- pares se distingan también con protanopia, deuteranopia y tritanopia (simulación de Machado
565
- 2009; distancia mínima en OKLab de 10 sin daltonismo y 7 con él en claro, 8,9 y 6,2 en
566
- oscuro), alternando la luminosidad donde el tono se confunde. Cada color llega a 3:1 (WCAG
567
- 1.4.11) sobre cada superficie, en claro y en oscuro: el build lo comprueba. El color no
568
- basta solo: netzi-chart añade forma de marcador, trazo y trama por serie (WCAG 1.4.1). */
569
- /* Serie 1: azul. */
570
- --netzi-chart-1: #0072b2;
571
- /* Serie 2: naranja quemado. */
572
- --netzi-chart-2: #bd582d;
573
- /* Serie 3: verde azulado. */
574
- --netzi-chart-3: #2d968c;
575
- /* Serie 4: vino. */
576
- --netzi-chart-4: #7b2c51;
577
- /* Serie 5: oliva. */
578
- --netzi-chart-5: #4d5200;
579
- /* Serie 6: índigo. */
580
- --netzi-chart-6: #35468d;
581
- /* Serie 7: rosa rojizo. */
582
- --netzi-chart-7: #a73b50;
583
- /* Serie 8: pizarra. */
584
- --netzi-chart-8: #555f7b;
585
- /* Fondo ambiental: manchas de color suaves DEBAJO del contenido, que el layout pinta bajo bg.
586
- Sin el eje ambient, none. */
587
- --netzi-ambient: none;
588
- }
589
218
 
590
- :root,
591
- [data-theme] {
592
- --netzi-info-text: var(--netzi-blue-text);
593
- --netzi-info-graphic: var(--netzi-blue-graphic);
594
- /* El velo del 14 % del gráfico de su estado: el fondo redondo de un icono de estado
595
- (netzi-result, netzi-connection-guard). El gráfico se mide sobre él, compuesto sobre cada
596
- superficie, en su contrato. */
597
- --netzi-info-veil: color-mix(in srgb, var(--netzi-info-graphic) 14%, transparent);
598
- --netzi-success-text: var(--netzi-green-text);
599
- --netzi-success-graphic: var(--netzi-green-graphic);
600
- --netzi-success-veil: color-mix(in srgb, var(--netzi-success-graphic) 14%, transparent);
601
- --netzi-warning-text: var(--netzi-yellow-text);
602
- --netzi-warning-graphic: var(--netzi-yellow-graphic);
603
- --netzi-warning-veil: color-mix(in srgb, var(--netzi-warning-graphic) 14%, transparent);
604
- --netzi-danger-text: var(--netzi-red-text);
605
- --netzi-danger-graphic: var(--netzi-red-graphic);
606
- --netzi-danger-veil: color-mix(in srgb, var(--netzi-danger-graphic) 14%, transparent);
607
- /* Sinónimo de surface, que es el nombre canónico (#216): el primer escalón de la escala no
608
- lleva número. */
609
- /* deprecated: Usa surface (--netzi-surface). Deprecado en @netzi/tokens 0.1.0-beta.10; se
610
- retira como pronto en @netzi/tokens 0.1.0-beta.11. */
611
- --netzi-surface-1: var(--netzi-surface);
612
- /* Sinónimo de text, que es el nombre canónico (#216). */
613
- /* deprecated: Usa text (--netzi-text). Deprecado en @netzi/tokens 0.1.0-beta.10; se retira
614
- como pronto en @netzi/tokens 0.1.0-beta.11. */
615
- --netzi-text-1: var(--netzi-text);
616
- }
617
-
618
- :root,
619
- [data-density] {
620
- /* Controles y piezas pequeñas dentro de una superficie: botones, campos, ítems de menú,
621
- celdas, iconos en caja. */
622
- --netzi-radius-control: var(--netzi-radius-sm);
623
- /* Superficies: tarjetas, diálogos, popovers, paneles y la paleta de comandos. */
624
- --netzi-radius-surface: var(--netzi-radius-lg);
625
- /* Superficies pequeñas o pegadas a un control: el panel de un select, una fecha o una hora,
626
- un aviso, una tarjeta compacta. */
627
- --netzi-radius-surface-sm: var(--netzi-radius);
628
- /* Sinónimo del radio medio (#216), que hoy es el de una superficie pequeña (#228). */
629
- /* deprecated: Usa radius.surface.sm (--netzi-radius-surface-sm). Deprecado en @netzi/tokens
630
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11. */
631
- --netzi-radius-md: var(--netzi-radius-surface-sm);
632
- /* Entre grupos apilados dentro de un panel o un formulario: campos, filas, columnas. */
633
- --netzi-space-stack: var(--netzi-space-4);
634
- /* Relleno interior de un contenedor. */
635
- /* El de una tarjeta o un panel. */
636
- --netzi-space-inset: var(--netzi-space-5);
637
- /* El de un contenedor compacto: un panel flotante, un aviso, una sección dentro de un panel. */
638
- --netzi-space-inset-sm: var(--netzi-space-4);
639
- /* El de un diálogo, un panel lateral o una tarjeta grande. */
640
- --netzi-space-inset-lg: var(--netzi-space-6);
641
- /* Entre secciones de una página. */
642
- --netzi-space-section: var(--netzi-space-7);
643
- /* Entre las regiones grandes de una página y como su margen en pantallas anchas. */
644
- --netzi-space-page: var(--netzi-space-8);
645
- }
219
+ /* --- Tipografía --- */
220
+ --netzi-font-sans: 'Plus Jakarta Sans', -apple-system, system-ui, sans-serif;
221
+ --netzi-font-mono: 'Fira Code', ui-monospace, monospace;
646
222
 
647
- :root,
648
- [data-preset] {
649
- /* Tintes del primario, derivados del preset. El suave (10 %) es para superficies grandes, una
650
- fila resaltada o un panel, y aguanta primary-text encima justo. El marcado (22 %) es para
651
- piezas pequeñas, un chip o una píldora, donde el suave se pierde contra el fondo (1.13:1
652
- sobre surface-3); su tinta es text, NO primary-text, que sobre él se queda en 3.9:1. Por
653
- eso el suave no se puede subir sin más: es el techo del otro. */
654
- --netzi-primary-tint: color-mix(in srgb, var(--netzi-primary) 10%, transparent);
655
- --netzi-primary-tint-strong: color-mix(in srgb, var(--netzi-primary) 22%, transparent);
656
- }
223
+ /* Escala global del tamaño de letra (1 = 100%). Los componentes calculan
224
+ sus tamaños como calc(Npx * escala); la cambia el panel de ajustes. */
225
+ --netzi-font-scale: 1;
657
226
 
658
- :root,
659
- [data-theme],
660
- [data-contrast] {
661
- /* La capa de hover: el puntero encima. Los controles que ya pintaban su hover con una
662
- superficie (surface-2) o con el hover del primario lo conservan; la capa es para lo nuevo y
663
- para quien pinte sus propios controles. */
664
- --netzi-state-hover: color-mix(in srgb, var(--netzi-text) 8%, transparent);
665
- /* La capa de foco: para lo que se enfoca DENTRO de un compuesto (la opción activa de una
666
- lista que se recorre con flechas). El foco de un control siempre lleva además su anillo
667
- (focus-ring). */
668
- --netzi-state-focus: color-mix(in srgb, var(--netzi-text) 12%, transparent);
669
- /* La capa de presionado: mientras el dedo o el botón del ratón están abajo. Se suma al hover
670
- (en táctil, a lo que haya), y se ve en menos de 100 ms porque no se anima. */
671
- --netzi-state-pressed: color-mix(in srgb, var(--netzi-text) 12%, transparent);
672
- /* La capa de arrastrado: la pieza que se lleva con el puntero (la pestaña que se reordena). */
673
- --netzi-state-dragged: color-mix(in srgb, var(--netzi-text) 16%, transparent);
674
- /* Sombras: los nombres de antes de la escala de elevación, en desuso (#220). Apuntan a su
675
- nivel y se redeclaran en cada sección con tema o contraste propios, como el anillo de foco,
676
- para que sigan al tema donde se usen. */
677
- /* deprecated: Usa elevation.floating (--netzi-elevation-floating). Deprecado en @netzi/tokens
678
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11. */
679
- --netzi-shadow: var(--netzi-elevation-floating);
680
- /* deprecated: Usa elevation.raised (--netzi-elevation-raised). Deprecado en @netzi/tokens
681
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11. */
682
- --netzi-shadow-sm: var(--netzi-elevation-raised);
683
- /* deprecated: Usa elevation.modal (--netzi-elevation-modal). Deprecado en @netzi/tokens
684
- 0.1.0-beta.10; se retira como pronto en @netzi/tokens 0.1.0-beta.11. */
685
- --netzi-shadow-lg: var(--netzi-elevation-modal);
686
- }
227
+ /* --- Escala tipográfica (#51) ---
228
+ El texto que NO es un control (una línea secundaria bajo un título, un pie
229
+ de tarjeta, un metadato, un título de página) no tenía token: cada app se
230
+ inventaba la suya y `var(--netzi-font-size-xs)` —que parece existir— se
231
+ descartaba en silencio. Los pasos salen de lo que el kit ya usa (picos en
232
+ 11 · 12.5 · 14 · 16 · 18 · 22 · 28), con ratio ≈1.15 y **md = 14px**, que es
233
+ el 100 % de `--netzi-font-scale` (el panel de ajustes divide entre 14).
234
+ Todos escalan con el ajuste de accesibilidad, como los controles. Los
235
+ tamaños de los controles (`--netzi-control-fs-*`) son otra familia: van
236
+ ligados a la altura del control, no a esta escala. La densidad compacta no
237
+ los toca: el texto no se encoge, se encoge el aire. */
238
+ --netzi-font-size-xs: calc(11px * var(--netzi-font-scale, 1));
239
+ --netzi-font-size-sm: calc(12.5px * var(--netzi-font-scale, 1));
240
+ --netzi-font-size-md: calc(14px * var(--netzi-font-scale, 1));
241
+ --netzi-font-size-lg: calc(16px * var(--netzi-font-scale, 1));
242
+ --netzi-font-size-xl: calc(18px * var(--netzi-font-scale, 1));
243
+ --netzi-font-size-2xl: calc(22px * var(--netzi-font-scale, 1));
244
+ --netzi-font-size-3xl: calc(28px * var(--netzi-font-scale, 1));
687
245
 
688
- :root,
689
- [data-theme],
690
- [data-preset] {
691
- /* El fondo de lo SELECCIONADO o activo (la opción elegida, el ítem actual del menú): el tinte
692
- suave del primario, el mismo de siempre. El color no basta solo: lo elegido lleva además su
693
- marca (aria-selected, aria-current o aria-pressed, y un indicador que no es solo color). */
694
- --netzi-state-selected: var(--netzi-primary-tint);
695
- /* Tinte de acento: alias histórico del tinte del primario. */
696
- --netzi-tint: var(--netzi-primary-tint);
697
- /* El indicador de FOCO: contorno o borde del control enfocado. Tiene que llegar a 3:1 sobre
698
- el fondo (WCAG 1.4.11) y el primario no llega en claro con azul, verde, naranja ni cian
699
- (#76): se apoya en el primario para texto, que cada preset y cada tema calibran. Se declara
700
- también en cada elemento con tema o preset propios (redeclareOn): una variable que usa otra
701
- se resuelve donde se declara, y una sección oscura dentro de una página clara heredaría el
702
- anillo claro. */
703
- --netzi-focus-ring: var(--netzi-primary-text);
704
- }
246
+ /* Interlineado, sin unidad (se multiplica por el tamaño de cada elemento).
247
+ `none` para lo que mide una línea (controles, chips); `tight` para
248
+ títulos; `normal` para texto corriente; `relaxed` para lectura larga. */
249
+ --netzi-line-height-none: 1;
250
+ --netzi-line-height-tight: 1.25;
251
+ --netzi-line-height-normal: 1.5;
252
+ --netzi-line-height-relaxed: 1.65;
705
253
 
706
- :root,
707
- [data-theme],
708
- [data-contrast],
709
- [data-glass] {
710
- /* El PULGAR de las barras de desplazamiento finas del kit (la tabla de datos, #117), sobre un
711
- carril transparente: lo que se ve es el pulgar contra la superficie, y es lo que se
712
- arrastra, así que pide 3:1 (WCAG 1.4.11). Las barras superpuestas (móvil, macOS) se pintan
713
- ENCIMA de las filas, así que también se mide sobre el tinte del primario (fila seleccionada
714
- o con el puntero encima): ahí text-3 se quedaba en 2.4–2.98:1, y por eso es text-2. El modo
715
- de alto contraste, que se pone en la raíz, lo sube con él. Se declara también en cada
716
- elemento con tema propio (redeclareOn), como el anillo de foco: una sección oscura dentro
717
- de una página clara heredaría el pulgar claro. */
718
- --netzi-scrollbar-thumb: var(--netzi-text-2);
254
+ /* Peso. Los cinco que usa el kit; la fuente cargada (`fonts.css`) trae
255
+ exactamente estos cortes, otro valor se sintetiza y se ve peor. */
256
+ --netzi-font-weight-regular: 400;
257
+ --netzi-font-weight-medium: 500;
258
+ --netzi-font-weight-semibold: 600;
259
+ --netzi-font-weight-bold: 700;
260
+ --netzi-font-weight-extrabold: 800;
719
261
  }
720
262
 
721
- /* Eje MODO (data-theme). Sin el atributo, claro: los valores de las capas. dark los redefine. */
722
-
723
- @media not print {
724
- [data-theme='light'] {
725
- /* El valor por defecto puesto en un elemento: los de las capas otra vez, para una sección
726
- dentro de otra con data-theme. */
727
- --netzi-blue-text: #076aa3;
728
- --netzi-blue-graphic: #0e84cd;
729
- --netzi-green-text: #05773a;
730
- --netzi-green-graphic: #069047;
731
- --netzi-purple-text: #743fd4;
732
- --netzi-red-text: #a34444;
733
- --netzi-red-graphic: #df464b;
734
- --netzi-yellow-text: #7d6421;
735
- --netzi-yellow-graphic: #9d7a0c;
736
- --netzi-surface-2: #f4f6f8;
737
- --netzi-surface-3: #eef1f4;
738
- --netzi-surface: #ffffff;
739
- --netzi-text-2: #5c6b78;
740
- --netzi-text-3: #7a8996;
741
- --netzi-text: #1c252e;
742
- --netzi-primary-dark: var(--netzi-blue-dark);
743
- --netzi-primary-hover: #0a8fdd;
744
- --netzi-primary-pressed: #279ce1;
745
- --netzi-primary-text: #076ba6;
746
- --netzi-primary-graphic: #0987d2;
747
- --netzi-primary-tint: color-mix(in srgb, var(--netzi-primary) 10%, transparent);
748
- --netzi-primary-tint-strong: color-mix(in srgb, var(--netzi-primary) 22%, transparent);
749
- --netzi-on-accent: #04121f;
750
- --netzi-bg: #f7f9fb;
751
- --netzi-border-2: #dce0e5;
752
- --netzi-border: #eaedf0;
753
- --netzi-elevation-raised: 0 1px 2px rgba(145, 158, 171, 0.16);
754
- --netzi-elevation-floating:
755
- 0 0 2px rgba(145, 158, 171, 0.2), 0 12px 24px -4px rgba(145, 158, 171, 0.12);
756
- --netzi-elevation-modal:
757
- 0 0 2px rgba(145, 158, 171, 0.2), 0 16px 40px -8px rgba(145, 158, 171, 0.24);
758
- --netzi-float-bg: #ffffff;
759
- --netzi-float-border-2: #dce0e5;
760
- --netzi-float-border: #eaedf0;
761
- --netzi-float-surface-2: #f4f6f8;
762
- --netzi-float-surface-3: #eef1f4;
763
- --netzi-float-surface: #ffffff;
764
- --netzi-float-filter: none;
765
- --netzi-float-glow: 0 0 rgba(0, 0, 0, 0);
766
- --netzi-scrim: rgba(8, 12, 20, 0.28);
767
- --netzi-chart-1: #0072b2;
768
- --netzi-chart-2: #bd582d;
769
- --netzi-chart-3: #2d968c;
770
- --netzi-chart-4: #7b2c51;
771
- --netzi-chart-5: #4d5200;
772
- --netzi-chart-6: #35468d;
773
- --netzi-chart-7: #a73b50;
774
- --netzi-chart-8: #555f7b;
775
- --netzi-ambient: none;
776
- }
263
+ [data-theme='dark'] {
264
+ /* --- Tema OSCURO: superficies --- */
265
+ --netzi-bg: #0d1117;
266
+ --netzi-surface: #161b22;
267
+ --netzi-surface-2: #0d1117;
268
+ --netzi-surface-3: #21262d;
269
+
270
+ /* --- Tema OSCURO: bordes --- */
271
+ --netzi-border: #21262d;
272
+ --netzi-border-2: #30363d;
273
+
274
+ /* --- Tema OSCURO: capas flotantes --- */
275
+ --netzi-float-bg: #161b22;
276
+ --netzi-float-border: #21262d;
277
+ --netzi-float-border-2: #30363d;
278
+ --netzi-float-surface: #161b22;
279
+ --netzi-float-surface-2: #0d1117;
280
+ --netzi-float-surface-3: #21262d;
281
+
282
+ /* --- Tema OSCURO: los semánticos como texto ---
283
+ Sobre fondo oscuro casi todos sirven crudos; solo el rojo y el morado piden
284
+ un punto de luz para llegar al mínimo. */
285
+ --netzi-blue-text: #0ba5ff;
286
+ --netzi-green-text: #0ae972;
287
+ --netzi-yellow-text: #f0c040;
288
+ --netzi-red-text: #ff7272;
289
+ --netzi-purple-text: #ac7eff;
290
+ --netzi-primary-text: #0da6ff;
291
+
292
+ /* --- Tema OSCURO: texto (mismos papeles que en claro) --- */
293
+ --netzi-text: #ffffff;
294
+ --netzi-text-2: #8b949e;
295
+ --netzi-text-3: #6e7681;
296
+ --netzi-on-accent: #04121f;
777
297
 
778
- [data-theme='dark'] {
779
- /* Tema OSCURO. Sobre fondo oscuro casi todos los colores de marca sirven crudos como texto;
780
- solo el rojo y el morado piden un punto de luz para llegar al mínimo. Por lo mismo, el
781
- gráfico de cada estado (#189) es su crudo. El tinte del primario sube un poco de alfa. */
782
- --netzi-bg: #0d1117;
783
- --netzi-surface: #161b22;
784
- --netzi-surface-2: #0d1117;
785
- --netzi-surface-3: #21262d;
786
- --netzi-border: #21262d;
787
- --netzi-border-2: #30363d;
788
- --netzi-float-bg: #161b22;
789
- --netzi-float-border: #21262d;
790
- --netzi-float-border-2: #30363d;
791
- --netzi-float-surface: #161b22;
792
- --netzi-float-surface-2: #0d1117;
793
- --netzi-float-surface-3: #21262d;
794
- --netzi-blue-text: #0ba5ff;
795
- --netzi-blue-graphic: var(--netzi-blue);
796
- --netzi-green-text: #0ae972;
797
- --netzi-green-graphic: var(--netzi-green);
798
- --netzi-yellow-text: #f0c040;
799
- --netzi-yellow-graphic: var(--netzi-yellow);
800
- --netzi-red-text: #ff7272;
801
- --netzi-red-graphic: var(--netzi-red);
802
- --netzi-purple-text: #ac7eff;
803
- --netzi-primary-text: #0da6ff;
804
- --netzi-primary-graphic: #0ba5ff;
805
- --netzi-primary-tint: color-mix(in srgb, var(--netzi-primary) 15%, transparent);
806
- --netzi-primary-tint-strong: color-mix(in srgb, var(--netzi-primary) 28%, transparent);
807
- --netzi-text: #ffffff;
808
- --netzi-text-2: #8b949e;
809
- --netzi-text-3: #6e7681;
810
- /* La paleta de las gráficas en oscuro: mismos tonos, aclarados hasta 3:1 sobre la
811
- superficie más clara del tema (surface-3). */
812
- --netzi-chart-1: #3d9df0;
813
- --netzi-chart-2: #e37b51;
814
- --netzi-chart-3: #52ddd8;
815
- --netzi-chart-4: #c062a0;
816
- --netzi-chart-5: #cfa726;
817
- --netzi-chart-6: #5c7ec7;
818
- --netzi-chart-7: #d55444;
819
- --netzi-chart-8: #9aa4b2;
820
- --netzi-on-accent: #04121f;
821
- --netzi-elevation-raised: 0 1px 2px rgba(0, 0, 0, 0.4);
822
- --netzi-elevation-floating: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 28px -6px rgba(0, 0, 0, 0.45);
823
- --netzi-elevation-modal: 0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 50px -10px rgba(0, 0, 0, 0.6);
824
- }
298
+ /* --- Tema OSCURO: sombras --- */
299
+ --netzi-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
300
+ --netzi-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 28px -6px rgba(0, 0, 0, 0.45);
301
+ --netzi-shadow-lg: 0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 50px -10px rgba(0, 0, 0, 0.6);
825
302
 
826
- [data-theme='dark'] :where([data-preset]:not([data-theme])) {
827
- --netzi-primary-tint: color-mix(in srgb, var(--netzi-primary) 15%, transparent);
828
- --netzi-primary-tint-strong: color-mix(in srgb, var(--netzi-primary) 28%, transparent);
829
- }
303
+ /* El tinte sube un poco de alfa en oscuro. */
304
+ --netzi-primary-tint: color-mix(in srgb, var(--netzi-primary) 15%, transparent);
305
+ --netzi-primary-tint-strong: color-mix(in srgb, var(--netzi-primary) 28%, transparent);
830
306
  }
831
307
 
832
- /* Eje PRESET (data-preset): el color primario. Sin el atributo, azul (el de las capas). Cada
833
- contexto con mode además del preset es la combinación de los dos: gana a cada eje por
834
- separado. */
835
-
308
+ /* ===========================================================
309
+ PRESETS de color primario
310
+ =========================================================== */
836
311
  [data-preset='green'] {
837
312
  --netzi-primary: #0ae972;
838
- --netzi-primary-dark: #079b4e;
313
+ --netzi-primary-dark: #07a352;
839
314
  --netzi-primary-hover: #07a352;
840
- --netzi-primary-pressed: #25ae67;
841
- --netzi-primary-text: #05793b;
842
- --netzi-primary-graphic: #06984a;
843
315
  --netzi-on-primary: #04121f;
316
+ --netzi-primary-text: #05793b;
844
317
  }
845
-
846
- @media not print {
847
- [data-preset='green'] :where([data-theme='light']:not([data-preset])) {
848
- --netzi-primary-dark: #079b4e;
849
- --netzi-primary-hover: #07a352;
850
- --netzi-primary-pressed: #25ae67;
851
- --netzi-primary-text: #05793b;
852
- --netzi-primary-graphic: #06984a;
853
- }
854
- }
855
-
318
+ /* El morado es el ÚNICO preset donde la tinta oscura no cumple (4.13): se queda
319
+ en blanco, que sí (4.57). La regla es «la que cumpla sobre su color». */
856
320
  [data-preset='purple'] {
857
- /* El morado es el ÚNICO preset donde la tinta oscura no cumple (4.13): se queda en blanco,
858
- que sí (4.57). La regla es la que cumpla sobre su color. */
859
321
  --netzi-primary: #8c4cff;
860
322
  --netzi-primary-dark: #6a2fd6;
861
323
  --netzi-primary-hover: #6a2fd6;
862
- --netzi-primary-pressed: #5d29bc;
863
- --netzi-primary-text: #7841db;
864
- --netzi-primary-graphic: #8c4cff;
865
324
  --netzi-on-primary: #ffffff;
325
+ --netzi-primary-text: #7841db;
866
326
  }
867
-
868
- @media not print {
869
- [data-preset='purple'] :where([data-theme='light']:not([data-preset])) {
870
- --netzi-primary-dark: #6a2fd6;
871
- --netzi-primary-hover: #6a2fd6;
872
- --netzi-primary-pressed: #5d29bc;
873
- --netzi-primary-text: #7841db;
874
- --netzi-primary-graphic: #8c4cff;
875
- }
876
- }
877
-
878
327
  [data-preset='orange'] {
879
328
  --netzi-primary: #f0a020;
880
- --netzi-primary-dark: #bf780a;
329
+ --netzi-primary-dark: #c97e0a;
881
330
  --netzi-primary-hover: #c97e0a;
882
- --netzi-primary-pressed: #cf8d27;
883
- --netzi-primary-text: #8e5e13;
884
- --netzi-primary-graphic: #b47718;
885
331
  --netzi-on-primary: #04121f;
332
+ --netzi-primary-text: #8e5e13;
886
333
  }
887
-
888
- @media not print {
889
- [data-preset='orange'] :where([data-theme='light']:not([data-preset])) {
890
- --netzi-primary-dark: #bf780a;
891
- --netzi-primary-hover: #c97e0a;
892
- --netzi-primary-pressed: #cf8d27;
893
- --netzi-primary-text: #8e5e13;
894
- --netzi-primary-graphic: #b47718;
895
- }
896
- }
897
-
898
334
  [data-preset='red'] {
899
335
  --netzi-primary: #ff5630;
900
336
  --netzi-primary-dark: #d63916;
901
337
  --netzi-primary-hover: #eb4a25;
902
- --netzi-primary-pressed: #ed603f;
903
- --netzi-primary-text: #b53d22;
904
- --netzi-primary-graphic: #e34d2b;
905
338
  --netzi-on-primary: #04121f;
339
+ --netzi-primary-text: #b53d22;
906
340
  }
907
-
908
- @media not print {
909
- [data-preset='red'] :where([data-theme='light']:not([data-preset])) {
910
- --netzi-primary-dark: #d63916;
911
- --netzi-primary-hover: #eb4a25;
912
- --netzi-primary-pressed: #ed603f;
913
- --netzi-primary-text: #b53d22;
914
- --netzi-primary-graphic: #e34d2b;
915
- }
916
- }
917
-
918
341
  [data-preset='cyan'] {
919
342
  --netzi-primary: #00b8d9;
920
343
  --netzi-primary-dark: #0892ab;
921
344
  --netzi-primary-hover: #0892ab;
922
- --netzi-primary-pressed: #269fb5;
923
- --netzi-primary-text: #007287;
924
- --netzi-primary-graphic: #008ea8;
925
345
  --netzi-on-primary: #04121f;
346
+ --netzi-primary-text: #007287;
926
347
  }
927
348
 
928
- @media not print {
929
- [data-preset='cyan'] :where([data-theme='light']:not([data-preset])) {
930
- --netzi-primary-dark: #0892ab;
931
- --netzi-primary-hover: #0892ab;
932
- --netzi-primary-pressed: #269fb5;
933
- --netzi-primary-text: #007287;
934
- --netzi-primary-graphic: #008ea8;
935
- }
936
-
937
- [data-theme='dark'][data-preset='green'] {
938
- --netzi-primary-text: #0ae972;
939
- --netzi-primary-graphic: #0ae972;
940
- }
941
-
942
- [data-theme='dark'][data-preset='purple'] {
943
- /* El morado oscuro de claro (#6a2fd6) se hunde en el fondo oscuro (2.7:1): un interruptor
944
- encendido apenas se distinguía de la tarjeta. */
945
- --netzi-primary-text: #ad80ff;
946
- --netzi-primary-graphic: #9255ff;
947
- --netzi-primary-dark: #824ff3;
948
- --netzi-primary-hover: #7d48f2;
949
- --netzi-primary-pressed: #6e3fd5;
950
- }
951
-
952
- [data-theme='dark'][data-preset='orange'] {
953
- --netzi-primary-text: #f0a020;
954
- --netzi-primary-graphic: #f0a020;
955
- }
956
-
957
- [data-theme='dark'][data-preset='red'] {
958
- --netzi-primary-text: #ff6a49;
959
- --netzi-primary-graphic: #ff5630;
960
- }
961
-
962
- [data-theme='dark'][data-preset='cyan'] {
963
- --netzi-primary-text: #00b8d9;
964
- --netzi-primary-graphic: #00b8d9;
965
- }
349
+ /* El indicador de FOCO: contorno o borde que marca el control enfocado. Tiene que
350
+ llegar a 3:1 sobre el fondo (WCAG 1.4.11) y el primario no llega en claro con azul,
351
+ verde, naranja ni cian (2.7, 1.6, 2.2 y 2.4:1; #76). Se apoya en el primario para
352
+ texto, que cada preset y cada tema ya calibran a 4.5:1 sobre las superficies. Se
353
+ declara también en cada elemento con tema o preset propios: una variable que usa
354
+ otra se resuelve donde se declara, y una sección oscura dentro de una página clara
355
+ heredaría el anillo claro. */
356
+ :root,
357
+ [data-theme],
358
+ [data-preset] {
359
+ --netzi-focus-ring: var(--netzi-primary-text);
966
360
  }
967
361
 
968
- /* Eje CONTRASTE (data-contrast): bordes y texto más marcados. Cada nivel de texto toma el valor
969
- del nivel de encima, así que text-3 pasa de 3:1 a más de 4.5:1 y el modo cumple incluso donde
970
- alguien lo haya usado para texto, que es justo lo que se espera de un modo de alto contraste.
971
- El borde fuerte (border-2) llega a 3:1 sobre cada superficie, como el borde de un control
972
- (WCAG 1.4.11). Sin el atributo en la página, sigue al sistema (#150): con prefers-contrast
973
- more aplica el alto, y data-contrast='normal' lo deja fuera a mano. */
362
+ /* El primario para TEXTO en oscuro: los saturados ya sirven, salvo dos. */
363
+ [data-theme='dark'][data-preset='green'] {
364
+ --netzi-primary-text: #0ae972;
365
+ }
366
+ [data-theme='dark'][data-preset='purple'] {
367
+ --netzi-primary-text: #ad80ff;
368
+ /* El morado oscuro de claro (#6a2fd6) se hunde en el fondo oscuro (2.7:1): un
369
+ interruptor encendido apenas se distinguía de la tarjeta. */
370
+ --netzi-primary-dark: #7d48f2;
371
+ --netzi-primary-hover: #7d48f2;
372
+ }
373
+ [data-theme='dark'][data-preset='orange'] {
374
+ --netzi-primary-text: #f0a020;
375
+ }
376
+ [data-theme='dark'][data-preset='red'] {
377
+ --netzi-primary-text: #ff6a49;
378
+ }
379
+ [data-theme='dark'][data-preset='cyan'] {
380
+ --netzi-primary-text: #00b8d9;
381
+ }
974
382
 
383
+ /* ===========================================================
384
+ CONTRASTE alto: bordes y texto más marcados
385
+ =========================================================== */
386
+ /* Cada nivel de texto toma el valor del nivel de encima: text-2 sube al tono
387
+ reforzado y text-3 hereda el text-2 por defecto. Así `text-3` pasa de 3:1 a
388
+ más de 4.5:1 y el modo cumple incluso donde alguien lo haya usado para texto,
389
+ que es justo lo que se espera de un modo de alto contraste. */
975
390
  [data-contrast='high'] {
976
391
  --netzi-border: #c9cfd6;
977
- --netzi-border-2: #828b94;
392
+ --netzi-border-2: #aeb6bf;
978
393
  --netzi-text-2: #2a333c;
979
394
  --netzi-text-3: #5c6b78;
980
395
  --netzi-float-border: #c9cfd6;
981
- --netzi-float-border-2: #828b94;
982
- --netzi-elevation-raised: 0 1px 2px rgba(99, 115, 129, 0.3);
396
+ --netzi-float-border-2: #aeb6bf;
397
+ --netzi-shadow-sm: 0 1px 2px rgba(99, 115, 129, 0.3);
983
398
  }
984
399
 
985
- @media not print {
986
- [data-contrast='high'] :where([data-theme='light']:not([data-contrast])) {
987
- --netzi-border-2: #828b94;
988
- --netzi-border: #c9cfd6;
989
- --netzi-text-2: #2a333c;
990
- --netzi-text-3: #5c6b78;
991
- --netzi-float-border-2: #828b94;
992
- --netzi-float-border: #c9cfd6;
993
- --netzi-elevation-raised: 0 1px 2px rgba(99, 115, 129, 0.3);
994
- }
400
+ [data-theme='dark'][data-contrast='high'] {
401
+ --netzi-border: #3a434d;
402
+ --netzi-border-2: #525c66;
403
+ --netzi-text-2: #aab4bf;
404
+ --netzi-text-3: #8b949e;
405
+ --netzi-float-border: #3a434d;
406
+ --netzi-float-border-2: #525c66;
995
407
  }
996
408
 
997
- @media (prefers-contrast: more) {
998
- :root:where(:not([data-contrast])) {
999
- /* Lo mismo cuando lo pide el sistema y nadie fija data-contrast. */
1000
- --netzi-border: #c9cfd6;
1001
- --netzi-border-2: #828b94;
1002
- --netzi-text-2: #2a333c;
1003
- --netzi-text-3: #5c6b78;
1004
- --netzi-float-border: #c9cfd6;
1005
- --netzi-float-border-2: #828b94;
1006
- --netzi-elevation-raised: 0 1px 2px rgba(99, 115, 129, 0.3);
1007
- }
409
+ /* ===========================================================
410
+ GLASS: las capas flotantes, de cristal
411
+
412
+ Translúcidas, con el fondo desenfocado y saturado por detrás. Se activa con
413
+ `data-glass="on"` en <html> (lo pone NetziThemeService), y solo afecta a las
414
+ capas que FLOTAN sobre el contenido: las superficies de contenido —tarjetas,
415
+ secciones, tablas— siguen opacas, porque ahí el cristal solo resta.
416
+
417
+ Sobre cristal el texto NO se lee contra una superficie fija: se lee contra lo
418
+ que haya debajo, que cambia al scrollear. El peor caso es "negro puro debajo"
419
+ en claro y "blanco puro debajo" en oscuro, y es ESE el que tiene que cumplir
420
+ 4.5:1. Hay dos formas de conseguirlo y solo una deja que esto parezca cristal:
421
+
422
+ - Subir el alfa hasta tapar el fondo. Cumple, pero con 0.82 ya es
423
+ prácticamente una superficie opaca: se pierde el efecto entero.
424
+ - **Acotar el fondo con el propio filtro**: `contrast()` comprime su rango
425
+ hacia el medio y `brightness()` lo empuja hacia el tono de la capa (arriba
426
+ en claro, abajo en oscuro). Al entrar ya acotado, un alfa MUCHO más bajo
427
+ basta para el mismo suelo — y con menos alfa se ve lo de detrás, que es
428
+ justo de lo que va el efecto.
429
+
430
+ Por eso el filtro no es solo `blur`: cada función tiene un papel.
431
+ saturate(180%) el color de debajo se aviva al difuminarse (lo vistoso).
432
+ contrast(0.6) comprime el rango del fondo hacia el gris medio.
433
+ brightness() lo empuja al tono de la capa: >1 en claro, <1 en oscuro.
434
+ blur(28px) lo difumina; cuanto más ancho, más "vidrio esmerilado".
435
+
436
+ Medido con esa cadena: en los dos temas el plano
437
+ contrario puro debajo (negro en claro, blanco en oscuro) es el único caso
438
+ donde el texto secundario cede a ~4.1-4.2:1 con el primario ≥ 4.5; contra
439
+ el fondo del kit y contra un gris medio, ambos pasan de 6:1. Sin la compresión del filtro haría falta alfa 0.82 para
440
+ el mismo suelo, y a 0.82 esto ya no parece cristal. Lo verifica
441
+ `tokens-contrast.spec.ts`, que modela la cadena entera —no solo el alfa—, así
442
+ que tocar cualquiera de las cuatro funciones o el alfa mueve el veredicto.
443
+ =========================================================== */
444
+ [data-glass='on'] {
445
+ --netzi-float-bg: rgba(255, 255, 255, 0.44);
446
+ --netzi-float-border: rgba(255, 255, 255, 0.7);
447
+ --netzi-float-border-2: rgba(255, 255, 255, 0.85);
448
+ /* Lo anidado en un panel de cristal también es vidrio: una tarjeta opaca
449
+ dentro de un drawer translúcido era exactamente la queja («hay elementos
450
+ que no se ponen translúcidos»). Los tres son tintes OSCUROS que se alejan
451
+ de la lámina un paso, dos y tres, a LA MISMA distancia que las superficies
452
+ opacas que imitan: `#f4f6f8` es un 4,5 % de tinta sobre blanco y `#eef1f4`
453
+ un 7,5 %. Más fuertes (se probó al 9 y al 13 %) las tarjetas de un drawer
454
+ salían como placas grises, como deshabilitadas. Un tinte blanco no vale: la
455
+ lámina clara ya es casi blanca y la burbuja de un comentario desaparecía
456
+ sobre ella. Oscurecer resta contraste al texto, y por eso `--netzi-text-2`
457
+ baja aquí dos niveles; el guardián comprueba que sobre el tinte más fuerte
458
+ sigue por encima de 4.5:1 con negro puro debajo. */
459
+ --netzi-float-surface: rgba(28, 37, 46, 0.02);
460
+ --netzi-float-surface-2: rgba(28, 37, 46, 0.045);
461
+ --netzi-float-surface-3: rgba(28, 37, 46, 0.075);
462
+ /* Alfa 0.44, brillo 1.25 y solo 10 px de desenfoque, para que se vea lo que
463
+ hay debajo (el usuario eligió este punto tras probar cinco niveles, del más
464
+ denso al más abierto; se quedó el más abierto). El brillo bajó
465
+ de 1.5 a 1.25 porque a 1.5 todo lo de debajo se lavaba a blanco y en claro el
466
+ cristal «no se notaba». El precio, como en oscuro, lo paga el caso extremo:
467
+ con un plano NEGRO debajo el texto secundario (#2a333c) queda en 4.2:1 (el
468
+ primario en 5.1); sobre la tinta del kit, 5.1; contra el fondo del kit y
469
+ contra un gris medio, ambos pasan de 7:1. */
470
+ --netzi-float-filter: saturate(200%) contrast(0.6) brightness(1.25) blur(10px);
471
+ /* El texto sube un nivel con el cristal, en TODA la página y no solo dentro de
472
+ las capas. Va aquí, en el mismo elemento que `data-theme`, y con literales:
473
+ declararlo en cada capa como `var(--netzi-float-text-2)` NO se reinvalidaba
474
+ al cambiar de tema en caliente y el sidebar se quedaba con el color del tema
475
+ anterior (medido: 3.08:1 tras pulsar el interruptor). En una superficie
476
+ opaca el refuerzo tampoco estorba: sube de 4.84 a 7.94. */
477
+ --netzi-text-2: #2a333c;
478
+ --netzi-text-3: #5c6b78;
479
+ /* --- LENTE del canto (opt-in: `data-glass-lens="on"`) ---
480
+ Un anillo con su PROPIO desenfoque, más fuerte que el del interior, para que
481
+ el canto parezca tener espesor. No es refracción de verdad —el fondo no se
482
+ curva— porque eso hoy no se puede: Chromium acepta
483
+ `backdrop-filter: url(#filtro-svg)` y luego lo IGNORA (comprobado: el mismo
484
+ filtro como `filter` normal sí deforma), y Safari ni lo acepta.
485
+ La cadena sale de destripar una implementación que sí convence (tonnitools):
486
+ lo que parece refracción es `invert()` + `contrast()` alto en un anillo
487
+ ESTRECHO del canto. Ellos apilan siete capas de `backdrop-filter` para
488
+ conseguirlo; aquí va una sola, porque cada capa cuesta frames.
489
+ ⚠️ Cuesta caro: es una segunda capa de `backdrop-filter`. Con una sola capa
490
+ en pantalla sale gratis (60 fps medidos); apilada con el shell cae a ~26.
491
+ Por eso es opt-in y por eso el shell se apaga cuando hay una capa encima. */
492
+ --netzi-float-lens: blur(18px) invert(0.18) brightness(1.1) saturate(1.3) hue-rotate(-10deg)
493
+ contrast(2);
494
+ --netzi-float-lens-size: 5px;
495
+
496
+ /* Filo especular, con GROSOR: luz en el canto de arriba y sombra dentro del de
497
+ abajo. Un solo filo de luz se lee como un velo iluminado; los dos juntos dan
498
+ el canto de una pieza de vidrio, que es la diferencia entre «translúcido» y
499
+ «cristal». No toca ni el alfa ni el filtro, así que no mueve el contraste. */
500
+ --netzi-float-glow:
501
+ inset 0 1px 0 rgba(255, 255, 255, 0.75), inset 0 -1px 0 rgba(11, 22, 34, 0.07);
502
+ }
503
+
504
+ [data-theme='dark'][data-glass='on'] {
505
+ --netzi-float-bg: rgba(22, 27, 34, 0.12);
506
+ --netzi-float-border: rgba(255, 255, 255, 0.14);
507
+ --netzi-float-border-2: rgba(255, 255, 255, 0.22);
508
+ /* En oscuro los tintes van hacia el negro, que aquí además MEJORA el
509
+ contraste del texto claro. */
510
+ --netzi-float-surface: rgba(0, 0, 0, 0.12);
511
+ --netzi-float-surface-2: rgba(0, 0, 0, 0.22);
512
+ --netzi-float-surface-3: rgba(0, 0, 0, 0.32);
513
+ /* Alfa 0.12, brillo 0.56 y 10 px de desenfoque (el usuario eligió este punto
514
+ tras probar cinco niveles; se quedó el más abierto). El alfa es lo que deja
515
+ pasar el COLOR de debajo; el brillo lo topa el contraste: el texto primario
516
+ tiene que quedar en 4.5:1 con un plano BLANCO debajo (5.1 aquí) y el
517
+ secundario, subido dos niveles (#d8dee6), en 3:1 (4.1). Sobre el fondo del
518
+ kit y sobre un gris medio (una tarjeta clara o una foto) los tres siguen
519
+ holgados. Los números los fija `tokens-contrast.spec.ts`. */
520
+ --netzi-float-filter: saturate(200%) contrast(0.6) brightness(0.56) blur(10px);
521
+ --netzi-text-2: #d8dee6;
522
+ --netzi-text-3: #aab4bf;
523
+ /* En oscuro la sombra del canto inferior es más marcada: sobre fondo oscuro una
524
+ línea negra se pierde, y sin ella el canto vuelve a no tener grosor. */
525
+ --netzi-float-glow: inset 0 1px 0 rgba(255, 255, 255, 0.12), inset 0 -1px 0 rgba(0, 0, 0, 0.35);
526
+ /* El velo también se hunde en oscuro: el mismo alfa sobre un fondo ya oscuro
527
+ apenas separa la capa del contenido. */
528
+ --netzi-scrim: rgba(2, 5, 10, 0.45);
529
+ /* En oscuro se invierte menos: sobre fondo oscuro, invertir de más convierte el
530
+ canto en un halo blanco. */
531
+ --netzi-float-lens: blur(18px) invert(0.12) brightness(1.05) saturate(1.3) hue-rotate(-10deg)
532
+ contrast(1.7);
1008
533
  }
1009
534
 
1010
- @media not print {
1011
- @media (prefers-contrast: more) {
1012
- :root:where(:not([data-contrast])) :where([data-theme='light']:not([data-contrast])) {
1013
- --netzi-border-2: #828b94;
1014
- --netzi-border: #c9cfd6;
1015
- --netzi-text-2: #2a333c;
1016
- --netzi-text-3: #5c6b78;
1017
- --netzi-float-border-2: #828b94;
1018
- --netzi-float-border: #c9cfd6;
1019
- --netzi-elevation-raised: 0 1px 2px rgba(99, 115, 129, 0.3);
1020
- }
1021
- }
535
+ /* ===========================================================
536
+ FONDO AMBIENTAL (`data-ambient="on"`, lo pone `NetziThemeService`)
537
+ Manchas de color suaves DEBAJO del contenido, que el layout pinta bajo
538
+ `--netzi-bg`. Es lo que le da al cristal algo que desenfocar: sobre un fondo
539
+ liso, un panel translúcido se ve como una placa gris; sobre esto, como
540
+ vidrio. Va con los colores del preset (`--netzi-primary`) y dos de marca.
541
+ Los porcentajes son bajos a propósito: el texto se sigue leyendo encima
542
+ (medido: `--netzi-text-3` sobre la mancha más fuerte, 4.4:1 en claro y 4.6:1
543
+ en oscuro, por encima del 3:1 que exige). En alto contraste se apaga.
544
+ =========================================================== */
545
+ :root {
546
+ --netzi-ambient: none;
1022
547
  }
1023
548
 
1024
- @media not print {
1025
- [data-theme='dark'][data-contrast='high'] {
1026
- --netzi-border: #3a434d;
1027
- --netzi-border-2: #68717b;
1028
- --netzi-text-2: #aab4bf;
1029
- --netzi-text-3: #8b949e;
1030
- --netzi-float-border: #3a434d;
1031
- --netzi-float-border-2: #68717b;
1032
- }
549
+ [data-ambient='on'] {
550
+ --netzi-ambient:
551
+ radial-gradient(
552
+ 55% 45% at 8% 0%,
553
+ color-mix(in srgb, var(--netzi-primary) 16%, transparent),
554
+ transparent 70%
555
+ ),
556
+ radial-gradient(
557
+ 45% 40% at 100% 100%,
558
+ color-mix(in srgb, var(--netzi-purple) 12%, transparent),
559
+ transparent 70%
560
+ ),
561
+ radial-gradient(
562
+ 35% 30% at 70% 10%,
563
+ color-mix(in srgb, var(--netzi-green) 8%, transparent),
564
+ transparent 70%
565
+ );
1033
566
  }
1034
567
 
1035
- @media not print {
1036
- @media (prefers-contrast: more) {
1037
- [data-theme='dark']:not([data-contrast]):where(:not([data-contrast] *)) {
1038
- /* Lo mismo cuando lo pide el sistema y nadie fija data-contrast. */
1039
- --netzi-border: #3a434d;
1040
- --netzi-border-2: #68717b;
1041
- --netzi-text-2: #aab4bf;
1042
- --netzi-text-3: #8b949e;
1043
- --netzi-float-border: #3a434d;
1044
- --netzi-float-border-2: #68717b;
1045
- }
1046
- }
568
+ [data-theme='dark'][data-ambient='on'] {
569
+ --netzi-ambient:
570
+ radial-gradient(
571
+ 55% 45% at 8% 0%,
572
+ color-mix(in srgb, var(--netzi-primary) 22%, transparent),
573
+ transparent 70%
574
+ ),
575
+ radial-gradient(
576
+ 45% 40% at 100% 100%,
577
+ color-mix(in srgb, var(--netzi-purple) 18%, transparent),
578
+ transparent 70%
579
+ ),
580
+ radial-gradient(
581
+ 35% 30% at 70% 10%,
582
+ color-mix(in srgb, var(--netzi-green) 10%, transparent),
583
+ transparent 70%
584
+ );
1047
585
  }
1048
586
 
1049
- /* Eje CRISTAL (data-glass): las capas flotantes, translúcidas, con el fondo desenfocado y
1050
- saturado por detrás. Solo afecta a lo que FLOTA sobre el contenido; las superficies de
1051
- contenido siguen opacas, porque ahí el cristal solo resta. Sobre cristal el texto se lee
1052
- contra lo que haya debajo, que cambia al desplazar: el peor caso es negro puro debajo en
1053
- claro y blanco puro en oscuro. El suelo no lo pone solo el alfa: el filtro ACOTA el fondo
1054
- antes de componerlo (saturate aviva el color, contrast comprime el rango hacia el gris medio,
1055
- brightness lo empuja al tono de la capa, blur lo difumina), y por eso basta un alfa mucho más
1056
- bajo, con el que se ve lo de detrás. Lo mide el test de contraste modelando la cadena entera:
1057
- tocar cualquiera de las funciones o el alfa mueve el veredicto. Quien pide menos
1058
- transparencia, o un navegador sin backdrop-filter, recibe la superficie opaca de siempre. */
587
+ /* En alto contraste no hay manchas: el fondo vuelve a ser liso. */
588
+ [data-contrast='high'][data-ambient='on'] {
589
+ --netzi-ambient: none;
590
+ }
1059
591
 
1060
- @media not print {
592
+ /* Quien pide menos transparencia recibe la superficie opaca de siempre, igual
593
+ que el kit ya respeta `prefers-reduced-motion` en las animaciones. */
594
+ @media (prefers-reduced-transparency: reduce) {
1061
595
  [data-glass='on'] {
1062
- /* Alfa 0.44, brillo 1.25 y 10 px de desenfoque, para que se vea lo que hay debajo (el
1063
- usuario eligió el más abierto de cinco niveles). Lo anidado en un panel de cristal
1064
- también es vidrio: tintes OSCUROS que se alejan de la lámina a la misma distancia que las
1065
- superficies opacas que imitan; más fuertes las tarjetas salían como placas grises.
1066
- Oscurecer resta contraste al texto, y por eso text-2 sube aquí dos niveles, en TODA la
1067
- página y con literales: declararlo por capa como referencia no se reinvalidaba al cambiar
1068
- de tema en caliente. La LENTE del canto (opt-in, data-glass-lens) es un anillo con su
1069
- propio desenfoque para que el canto parezca tener espesor; cuesta una segunda capa de
1070
- backdrop-filter. El filo especular (glow) da luz arriba y sombra abajo: el canto de una
1071
- pieza de vidrio; no toca ni el alfa ni el filtro. */
1072
- --netzi-float-bg: rgba(255, 255, 255, 0.44);
1073
- --netzi-float-border: rgba(255, 255, 255, 0.7);
1074
- --netzi-float-border-2: rgba(255, 255, 255, 0.85);
1075
- --netzi-float-surface: rgba(28, 37, 46, 0.02);
1076
- --netzi-float-surface-2: rgba(28, 37, 46, 0.045);
1077
- --netzi-float-surface-3: rgba(28, 37, 46, 0.075);
1078
- --netzi-float-filter: saturate(200%) contrast(0.6) brightness(1.25) blur(10px);
1079
- --netzi-float-lens: blur(18px) invert(0.18) brightness(1.1) saturate(1.3) hue-rotate(-10deg)
1080
- contrast(2);
1081
- --netzi-float-lens-size: 5px;
1082
- --netzi-float-glow:
1083
- inset 0 1px 0 rgba(255, 255, 255, 0.75), inset 0 -1px 0 rgba(11, 22, 34, 0.07);
1084
- --netzi-text-2: #2a333c;
1085
- --netzi-text-3: #5c6b78;
1086
- }
1087
-
1088
- [data-glass='on'] :where([data-theme='light']:not([data-glass])) {
1089
- --netzi-float-bg: rgba(255, 255, 255, 0.44);
1090
- --netzi-float-border-2: rgba(255, 255, 255, 0.85);
1091
- --netzi-float-border: rgba(255, 255, 255, 0.7);
1092
- --netzi-float-surface-2: rgba(28, 37, 46, 0.045);
1093
- --netzi-float-surface-3: rgba(28, 37, 46, 0.075);
1094
- --netzi-float-surface: rgba(28, 37, 46, 0.02);
1095
- --netzi-float-filter: saturate(200%) contrast(0.6) brightness(1.25) blur(10px);
1096
- --netzi-float-lens: blur(18px) invert(0.18) brightness(1.1) saturate(1.3) hue-rotate(-10deg)
1097
- contrast(2);
1098
- --netzi-float-glow:
1099
- inset 0 1px 0 rgba(255, 255, 255, 0.75), inset 0 -1px 0 rgba(11, 22, 34, 0.07);
1100
- --netzi-text-2: #2a333c;
1101
- --netzi-text-3: #5c6b78;
596
+ --netzi-float-bg: #ffffff;
597
+ --netzi-float-border: #eaedf0;
598
+ --netzi-float-border-2: #dce0e5;
599
+ --netzi-float-surface: #ffffff;
600
+ --netzi-float-surface-2: #f4f6f8;
601
+ --netzi-float-surface-3: #eef1f4;
602
+ --netzi-float-filter: none;
603
+ --netzi-float-glow: 0 0 rgba(0, 0, 0, 0);
604
+ --netzi-scrim-filter: none;
1102
605
  }
1103
606
 
1104
607
  [data-theme='dark'][data-glass='on'] {
1105
- /* En oscuro los tintes van hacia el negro, que aquí MEJORA el contraste del texto claro.
1106
- Alfa 0.12 y brillo 0.56: el texto primario queda en 4.5:1 con un plano blanco debajo y el
1107
- secundario, subido dos niveles, en 3:1. La sombra del canto inferior es más marcada, el
1108
- velo se hunde y la lente invierte menos, para no convertir el canto en un halo blanco. */
1109
- --netzi-float-bg: rgba(22, 27, 34, 0.12);
1110
- --netzi-float-border: rgba(255, 255, 255, 0.14);
1111
- --netzi-float-border-2: rgba(255, 255, 255, 0.22);
1112
- --netzi-float-surface: rgba(0, 0, 0, 0.12);
1113
- --netzi-float-surface-2: rgba(0, 0, 0, 0.22);
1114
- --netzi-float-surface-3: rgba(0, 0, 0, 0.32);
1115
- --netzi-float-filter: saturate(200%) contrast(0.6) brightness(0.56) blur(10px);
1116
- --netzi-float-glow: inset 0 1px 0 rgba(255, 255, 255, 0.12), inset 0 -1px 0 rgba(0, 0, 0, 0.35);
1117
- --netzi-float-lens: blur(18px) invert(0.12) brightness(1.05) saturate(1.3) hue-rotate(-10deg)
1118
- contrast(1.7);
1119
- --netzi-text-2: #d8dee6;
1120
- --netzi-text-3: #aab4bf;
1121
- --netzi-scrim: rgba(2, 5, 10, 0.45);
1122
- }
1123
- }
1124
-
1125
- @media not print {
1126
- @media (prefers-reduced-transparency: reduce) {
1127
- [data-glass='on'] {
1128
- /* Quien pide menos transparencia recibe la superficie opaca de siempre. */
1129
- --netzi-float-bg: #ffffff;
1130
- --netzi-float-border: #eaedf0;
1131
- --netzi-float-border-2: #dce0e5;
1132
- --netzi-float-surface: #ffffff;
1133
- --netzi-float-surface-2: #f4f6f8;
1134
- --netzi-float-surface-3: #eef1f4;
1135
- --netzi-float-filter: none;
1136
- --netzi-float-glow: 0 0 rgba(0, 0, 0, 0);
1137
- --netzi-scrim-filter: none;
1138
- }
1139
-
1140
- [data-glass='on'] :where([data-theme='light']:not([data-glass])) {
1141
- --netzi-float-bg: #ffffff;
1142
- --netzi-float-border-2: #dce0e5;
1143
- --netzi-float-border: #eaedf0;
1144
- --netzi-float-surface-2: #f4f6f8;
1145
- --netzi-float-surface-3: #eef1f4;
1146
- --netzi-float-surface: #ffffff;
1147
- --netzi-float-filter: none;
1148
- --netzi-float-glow: 0 0 rgba(0, 0, 0, 0);
1149
- }
1150
-
1151
- [data-theme='dark'][data-glass='on'] {
1152
- --netzi-float-bg: #161b22;
1153
- --netzi-float-border: #21262d;
1154
- --netzi-float-border-2: #30363d;
1155
- --netzi-float-surface: #161b22;
1156
- --netzi-float-surface-2: #0d1117;
1157
- --netzi-float-surface-3: #21262d;
1158
- }
1159
- }
1160
- }
1161
-
1162
- @media not print {
1163
- @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
1164
- [data-glass='on'] {
1165
- /* Sin backdrop-filter no hay desenfoque, y una capa translúcida sin desenfocar lo que hay
1166
- debajo es texto sobre ruido: se degrada a opaco entero. */
1167
- --netzi-float-bg: #ffffff;
1168
- --netzi-float-border: #eaedf0;
1169
- --netzi-float-border-2: #dce0e5;
1170
- --netzi-float-surface: #ffffff;
1171
- --netzi-float-surface-2: #f4f6f8;
1172
- --netzi-float-surface-3: #eef1f4;
1173
- }
1174
-
1175
- [data-glass='on'] :where([data-theme='light']:not([data-glass])) {
1176
- --netzi-float-bg: #ffffff;
1177
- --netzi-float-border-2: #dce0e5;
1178
- --netzi-float-border: #eaedf0;
1179
- --netzi-float-surface-2: #f4f6f8;
1180
- --netzi-float-surface-3: #eef1f4;
1181
- --netzi-float-surface: #ffffff;
1182
- }
1183
-
1184
- [data-theme='dark'][data-glass='on'] {
1185
- --netzi-float-bg: #161b22;
1186
- --netzi-float-border: #21262d;
1187
- --netzi-float-border-2: #30363d;
1188
- --netzi-float-surface: #161b22;
1189
- --netzi-float-surface-2: #0d1117;
1190
- --netzi-float-surface-3: #21262d;
1191
- }
608
+ --netzi-float-bg: #161b22;
609
+ --netzi-float-border: #21262d;
610
+ --netzi-float-border-2: #30363d;
611
+ --netzi-float-surface: #161b22;
612
+ --netzi-float-surface-2: #0d1117;
613
+ --netzi-float-surface-3: #21262d;
1192
614
  }
1193
615
  }
1194
616
 
1195
- /* Eje FONDO AMBIENTAL (data-ambient): manchas de color suaves debajo del contenido, con el
1196
- primario del preset y dos de marca, que le dan al cristal algo que desenfocar. Los
1197
- porcentajes son bajos a propósito: el texto se sigue leyendo encima (text-3 sobre la mancha
1198
- más fuerte, 4.4:1 en claro y 4.6:1 en oscuro). En alto contraste se apaga. */
1199
-
1200
- @media not print {
1201
- [data-ambient='on'] {
1202
- --netzi-ambient:
1203
- radial-gradient(
1204
- 55% 45% at 8% 0%,
1205
- color-mix(in srgb, var(--netzi-primary) 16%, transparent),
1206
- transparent 70%
1207
- ),
1208
- radial-gradient(
1209
- 45% 40% at 100% 100%,
1210
- color-mix(in srgb, var(--netzi-purple) 12%, transparent),
1211
- transparent 70%
1212
- ),
1213
- radial-gradient(
1214
- 35% 30% at 70% 10%,
1215
- color-mix(in srgb, var(--netzi-green) 8%, transparent),
1216
- transparent 70%
1217
- );
1218
- }
1219
-
1220
- [data-ambient='on'] :where([data-preset]:not([data-ambient])) {
1221
- --netzi-ambient:
1222
- radial-gradient(
1223
- 55% 45% at 8% 0%,
1224
- color-mix(in srgb, var(--netzi-primary) 16%, transparent),
1225
- transparent 70%
1226
- ),
1227
- radial-gradient(
1228
- 45% 40% at 100% 100%,
1229
- color-mix(in srgb, var(--netzi-purple) 12%, transparent),
1230
- transparent 70%
1231
- ),
1232
- radial-gradient(
1233
- 35% 30% at 70% 10%,
1234
- color-mix(in srgb, var(--netzi-green) 8%, transparent),
1235
- transparent 70%
1236
- );
1237
- }
1238
-
1239
- [data-ambient='on'] :where([data-theme='light']:not([data-ambient])) {
1240
- --netzi-ambient:
1241
- radial-gradient(
1242
- 55% 45% at 8% 0%,
1243
- color-mix(in srgb, var(--netzi-primary) 16%, transparent),
1244
- transparent 70%
1245
- ),
1246
- radial-gradient(
1247
- 45% 40% at 100% 100%,
1248
- color-mix(in srgb, var(--netzi-purple) 12%, transparent),
1249
- transparent 70%
1250
- ),
1251
- radial-gradient(
1252
- 35% 30% at 70% 10%,
1253
- color-mix(in srgb, var(--netzi-green) 8%, transparent),
1254
- transparent 70%
1255
- );
1256
- }
1257
-
1258
- [data-theme='dark'][data-ambient='on'] {
1259
- --netzi-ambient:
1260
- radial-gradient(
1261
- 55% 45% at 8% 0%,
1262
- color-mix(in srgb, var(--netzi-primary) 22%, transparent),
1263
- transparent 70%
1264
- ),
1265
- radial-gradient(
1266
- 45% 40% at 100% 100%,
1267
- color-mix(in srgb, var(--netzi-purple) 18%, transparent),
1268
- transparent 70%
1269
- ),
1270
- radial-gradient(
1271
- 35% 30% at 70% 10%,
1272
- color-mix(in srgb, var(--netzi-green) 10%, transparent),
1273
- transparent 70%
1274
- );
1275
- }
1276
-
1277
- [data-theme='dark'][data-ambient='on'] :where([data-preset]:not([data-theme], [data-ambient])) {
1278
- --netzi-ambient:
1279
- radial-gradient(
1280
- 55% 45% at 8% 0%,
1281
- color-mix(in srgb, var(--netzi-primary) 22%, transparent),
1282
- transparent 70%
1283
- ),
1284
- radial-gradient(
1285
- 45% 40% at 100% 100%,
1286
- color-mix(in srgb, var(--netzi-purple) 18%, transparent),
1287
- transparent 70%
1288
- ),
1289
- radial-gradient(
1290
- 35% 30% at 70% 10%,
1291
- color-mix(in srgb, var(--netzi-green) 10%, transparent),
1292
- transparent 70%
1293
- );
1294
- }
1295
-
1296
- [data-contrast='high'][data-ambient='on'] {
1297
- /* En alto contraste no hay manchas: el fondo vuelve a ser liso. */
1298
- --netzi-ambient: none;
1299
- }
1300
-
1301
- [data-contrast='high'][data-ambient='on']
1302
- :where([data-preset]:not([data-contrast], [data-ambient])) {
1303
- --netzi-ambient: none;
1304
- }
1305
-
1306
- [data-contrast='high'][data-ambient='on']
1307
- :where([data-theme='light']:not([data-contrast], [data-ambient])) {
1308
- --netzi-ambient: none;
617
+ /* Sin `backdrop-filter` no hay desenfoque, y una capa translúcida sin desenfocar
618
+ lo que hay debajo es texto sobre ruido: se degrada a opaco entero. */
619
+ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
620
+ [data-glass='on'] {
621
+ --netzi-float-bg: #ffffff;
622
+ --netzi-float-border: #eaedf0;
623
+ --netzi-float-border-2: #dce0e5;
624
+ --netzi-float-surface: #ffffff;
625
+ --netzi-float-surface-2: #f4f6f8;
626
+ --netzi-float-surface-3: #eef1f4;
1309
627
  }
1310
- }
1311
-
1312
- @media not print {
1313
- @media (prefers-contrast: more) {
1314
- [data-ambient='on']:not([data-contrast]):where(:not([data-contrast] *)) {
1315
- /* Lo mismo cuando lo pide el sistema y nadie fija data-contrast. */
1316
- --netzi-ambient: none;
1317
- }
1318
628
 
1319
- [data-ambient='on']:not([data-contrast]):where(:not([data-contrast] *))
1320
- :where([data-preset]:not([data-contrast], [data-ambient])) {
1321
- --netzi-ambient: none;
1322
- }
1323
-
1324
- [data-ambient='on']:not([data-contrast]):where(:not([data-contrast] *))
1325
- :where([data-theme='light']:not([data-contrast], [data-ambient])) {
1326
- --netzi-ambient: none;
1327
- }
629
+ [data-theme='dark'][data-glass='on'] {
630
+ --netzi-float-bg: #161b22;
631
+ --netzi-float-border: #21262d;
632
+ --netzi-float-border-2: #30363d;
633
+ --netzi-float-surface: #161b22;
634
+ --netzi-float-surface-2: #0d1117;
635
+ --netzi-float-surface-3: #21262d;
1328
636
  }
1329
637
  }
1330
638
 
1331
- /* Eje DENSIDAD (data-density): controles, radios y espaciado más ajustados (los pasos finos no
1332
- cambian). La tipografía no se toca: se encoge el aire, no la letra. */
1333
-
639
+ /* ===========================================================
640
+ DENSIDAD compacta: controles y radios más ajustados
641
+ =========================================================== */
1334
642
  [data-density='compact'] {
1335
643
  --netzi-control-py: 8px;
1336
644
  --netzi-control-px: 11px;
1337
- --netzi-control-px-sm: 10px;
1338
- --netzi-control-px-md: 11px;
1339
- --netzi-control-px-lg: 13px;
1340
645
  --netzi-control-h-sm: calc(32px * var(--netzi-font-scale, 1));
1341
646
  --netzi-control-h-md: calc(40px * var(--netzi-font-scale, 1));
1342
647
  --netzi-control-h-lg: calc(46px * var(--netzi-font-scale, 1));
1343
- --netzi-radius: 6px;
648
+ --netzi-control-px-sm: 10px;
649
+ --netzi-control-px-md: 11px;
650
+ --netzi-control-px-lg: 13px;
1344
651
  --netzi-radius-sm: 5px;
652
+ --netzi-radius: 6px;
1345
653
  --netzi-radius-lg: 8px;
654
+ /* Espaciado más ajustado (pasos medios/grandes; los finos no cambian). */
1346
655
  --netzi-space-4: 10px;
1347
656
  --netzi-space-5: 12px;
1348
657
  --netzi-space-6: 20px;
@@ -1350,77 +659,32 @@
1350
659
  --netzi-space-8: 40px;
1351
660
  }
1352
661
 
1353
- /* Entorno PUNTERO TÁCTIL (media pointer coarse): ningún control por debajo del objetivo táctil
1354
- (#100). Más 2 px porque el borde de 1 px del contenedor no le quita al campo que se toca;
1355
- cada densidad parte de su propia base, que con la letra más grande es la que manda. Va
1356
- después de la densidad para ganarle con la misma especificidad. */
1357
-
1358
- @media (pointer: coarse) {
1359
- :root {
1360
- --netzi-control-h-sm: max(
1361
- calc(var(--netzi-touch-target) + 2px),
1362
- calc(36px * var(--netzi-font-scale, 1))
1363
- );
1364
- --netzi-control-h-md: max(
1365
- calc(var(--netzi-touch-target) + 2px),
1366
- calc(44px * var(--netzi-font-scale, 1))
1367
- );
1368
- }
1369
-
1370
- [data-density='compact'] {
1371
- --netzi-control-h-sm: max(
1372
- calc(var(--netzi-touch-target) + 2px),
1373
- calc(32px * var(--netzi-font-scale, 1))
1374
- );
1375
- --netzi-control-h-md: max(
1376
- calc(var(--netzi-touch-target) + 2px),
1377
- calc(40px * var(--netzi-font-scale, 1))
1378
- );
1379
- }
1380
- }
1381
-
1382
- /* Entorno MOVIMIENTO REDUCIDO (media prefers-reduced-motion reduce, #151, WCAG 2.3.3): las
1383
- duraciones del desplazamiento se anulan aquí, en un solo sitio, y lo que se desplaza, gira o
1384
- escala salta a su estado final. Van a 0.01 ms y no a 0 para que transitionend y animationend
1385
- sigan llegando a quien los espera. Los fundidos (las duraciones sin move) no cambian: un
1386
- cambio de color u opacidad no es movimiento. */
1387
-
1388
- @media (prefers-reduced-motion: reduce) {
1389
- :root {
1390
- --netzi-duration-move-fast: 0.01ms;
1391
- --netzi-duration-move-base: 0.01ms;
1392
- --netzi-duration-move-slow: 0.01ms;
1393
- }
1394
- }
1395
-
1396
- /* Entorno IMPRESIÓN (media print, #176): el papel es claro, opaco y quieto. El tema oscuro, el
1397
- cristal y el fondo ambiental vuelven a su valor por defecto (resets): cada bloque que los
1398
- fija va en el CSS dentro de @media not print, así que al imprimir no aplica y sale el claro y
1399
- opaco de siempre, con el preset, el contraste y la densidad intactos. En papel un fondo
1400
- oscuro gasta tinta y el navegador lo quita a medias, y un vidrio desenfoca lo que no se va a
1401
- ver. Las sombras desaparecen, porque en papel no despegan nada y salen como manchas grises;
1402
- el velo de las capas modales no desenfoca; y las duraciones bajan a 0.01 ms para que nada se
1403
- imprima a medio camino. */
1404
-
1405
- @media print {
1406
- :root,
1407
- [data-contrast='high'] {
1408
- --netzi-elevation-floating: 0 0 rgba(0, 0, 0, 0);
1409
- --netzi-elevation-raised: 0 0 rgba(0, 0, 0, 0);
1410
- --netzi-elevation-modal: 0 0 rgba(0, 0, 0, 0);
1411
- --netzi-scrim-filter: none;
1412
- --netzi-duration-instant: 0.01ms;
1413
- --netzi-duration-fast: 0.01ms;
1414
- --netzi-duration-base: 0.01ms;
1415
- --netzi-duration-slow: 0.01ms;
1416
- --netzi-duration-flash: 0.01ms;
1417
- --netzi-duration-loop-spin: 0.01ms;
1418
- --netzi-duration-loop-orbit: 0.01ms;
1419
- --netzi-duration-loop-pulse: 0.01ms;
1420
- --netzi-duration-loop-sweep: 0.01ms;
1421
- --netzi-duration-loop-shine: 0.01ms;
1422
- --netzi-duration-move-fast: 0.01ms;
1423
- --netzi-duration-move-base: 0.01ms;
1424
- --netzi-duration-move-slow: 0.01ms;
1425
- }
1426
- }
662
+ /* ===========================================================
663
+ TEXTO: para qué sirve cada nivel y cuánto contrasta
664
+
665
+ Ratios WCAG 2.1 medidos contra las cuatro superficies del tema (bg, surface,
666
+ surface-2 y surface-3); se anota el PEOR de los cuatro, que siempre es
667
+ surface-3. Los verifica `tokens-contrast.spec.ts` en @netzi/ui, así que si
668
+ alguien cambia un valor aquí y deja de cumplir, la suite se pone roja.
669
+
670
+ Mínimos que se aplican
671
+ texto normal 4.5:1 (SC 1.4.3)
672
+ no textual 3:1 (SC 1.4.11, iconos y bordes con significado)
673
+ deshabilitado exento (SC 1.4.3 no cubre componentes inactivos)
674
+
675
+ nivel claro oscuro para qué
676
+ text #1c252e 13.69 #ffffff 15.22 texto principal
677
+ text-2 #5c6b78 4.84 #8b949e 4.95 TODO texto secundario
678
+ text-3 #7a8996 3.17 #6e7681 3.31 NO textual, ver abajo
679
+
680
+ Con alto contraste
681
+ text-2 #45525e 7.07 #aab4bf 7.24
682
+ text-3 #5c6b78 4.84 #8b949e 4.95
683
+
684
+ `text-3` NO vale para texto legible. Es el tono de lo que acompaña: chevrons,
685
+ separadores, iconos decorativos, la barra de redimensionar, el brillo del
686
+ esqueleto de carga y los estados deshabilitados. Para un rótulo, una ayuda, un
687
+ pie, un metadato o un placeholder va SIEMPRE `text-2`. La regla existe porque
688
+ con 45 archivos usándolo indistintamente el kit entero se quedó por debajo del
689
+ mínimo sin que nadie lo notara.
690
+ =========================================================== */