orbital-ds 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.css CHANGED
@@ -1,427 +1,486 @@
1
- /*
2
- * orbital-ds · ESTRUCTURA común a todos los temas.
3
- *
4
- * Mapea las variables del tema a utilidades de Tailwind (@theme), define los
5
- * tokens semánticos (radios, alturas, sombras, capas, movimiento, foco) y la
6
- * capa base de interacción. NO contiene colores ni fuentes: eso lo aporta un
7
- * TEMA (un fichero de `orbital-ds/themes/`). Uso en la app:
8
- *
9
- * @import 'tailwindcss';
10
- * @import 'orbital-ds/themes/qratia.css'; <- un tema
11
- * @import 'orbital-ds/styles.css'; <- la estructura
12
- * @source '../node_modules/orbital-ds/dist'; <- que Tailwind vea las clases
13
- */
14
-
15
- @custom-variant dark (&:is(.dark *));
16
-
17
- @theme inline {
18
-
19
- /*
20
- * ESCALA DE TEXTO — solo utilidades ESTÁNDAR de Tailwind, sin tamaños a medida.
21
- *
22
- * Display text-4xl 36/700 nota y cifras clave
23
- * H1 text-3xl 30/700 título de vista
24
- * H2 text-xl 20/700 detalle, modal
25
- * H3 text-base 16/600 título de tarjeta
26
- * H4 text-sm 14/600 grupo dentro de tarjeta
27
- * Cuerpo text-sm 14/400 campos y texto corriente
28
- * Fila text-sm 14/400-500
29
- * Etiqueta text-xs 12/600 en mayúsculas, con tracking ancho
30
- * Ayuda text-xs 12/400 notas y validación
31
- * Dato text-xs 12/500 mono y `tabular-nums`
32
- *
33
- * REGLAS: la jerarquía la hacen TAMAÑO Y PESO, nunca el color · interlineado
34
- * ajustado en títulos, holgado en cuerpo · tracking negativo solo de 20px para
35
- * arriba; ancho solo en mayúsculas · máximo tres niveles de título por vista ·
36
- * números comparables SIEMPRE `tabular-nums`.
37
- */
38
-
39
- /*
40
- * Rampa de radios, toda derivada de `--radius`: cambiar la redondez del
41
- * producto es mover UN valor.
42
- * sm chip · md control · lg fila · xl tarjeta · 2xl vista y barra
43
- */
44
- --radius-sm: calc(var(--radius) - 4px);
45
- --radius-md: calc(var(--radius) - 2px);
46
- --radius-lg: var(--radius);
47
- --radius-xl: calc(var(--radius) + 6px);
48
- --radius-2xl: calc(var(--radius) + 8px);
49
-
50
- /*
51
- * RADIOS SEMÁNTICOS — los componentes piden el hueco, no una medida. Un tema
52
- * que quiera controles menos redondos cambia ESTOS valores (p. ej.
53
- * `var(--radius-md)`), sin tocar ningún componente.
54
- * control campos, selects, pestañas, tooltips · overlay diálogos y paneles
55
- */
56
- --radius-control: 2rem;
57
- --radius-overlay: 2rem;
58
-
59
- /*
60
- * ALTURA DE CONTROLES — una escala para botón, campo y select, así alineados
61
- * en una misma fila. Densidad distinta = cambiar estos tres valores.
62
- */
63
- --ds-control-h-sm: 2rem;
64
- --ds-control-h-md: 2.25rem;
65
- --ds-control-h-lg: 2.5rem;
66
-
67
- /*
68
- * SOMBRAS SEMÁNTICAS — un hueco por tipo de pieza. `control` vale `none` hoy:
69
- * el hueco existe para que un tema pueda dar relieve a los campos con solo
70
- * dar valor aquí (sin tocar el componente).
71
- * control campos y botones · raised piezas que flotan · overlay capas
72
- */
73
- --shadow-control: none;
74
- --shadow-raised: var(--shadow-sm);
75
- --shadow-overlay: var(--shadow-lg);
76
- --shadow-popover: var(--shadow-2xl);
77
-
78
- /*
79
- * CAPAS (z-index) — tres niveles con nombre; ningún componente escribe un
80
- * número. raised: pieza que sobresale de su fila · sticky: barras fijas ·
81
- * overlay: menús, diálogos, paneles y tooltips.
82
- */
83
- --ds-z-raised: 10;
84
- --ds-z-sticky: 20;
85
- --ds-z-overlay: 50;
86
-
87
- /* MOVIMIENTO — dos duraciones; `reduced-motion` ya se respeta en la capa base. */
88
- --ds-motion-fast: 100ms;
89
- --ds-motion-base: 200ms;
90
- --ds-motion-slow: 300ms;
91
- --ds-motion-blink: 1s;
92
-
93
- /* FOCO — grosor del anillo de foco/error de todos los controles. */
94
- --ds-ring-width: 3px;
95
-
96
- /* Velo tras diálogos y paneles. Un tema puede cambiar su tinte u opacidad. */
97
- --color-scrim: oklch(0 0 0 / 0.8);
98
-
99
- /*
100
- * ESPACIADO INTERNO — un hueco por tipo de pieza. Los valores por defecto son
101
- * los de siempre; un tema que quiera más aire (o más densidad) los cambia aquí,
102
- * sin tocar ningún componente.
103
- * card tarjeta (md / sm) · field campos y selects · cell celdas de tabla
104
- * overlay diálogos y paneles · popover · menu / menu-item menús
105
- * button botones por tamaño · badge etiquetas
106
- */
107
- --ds-card-spacing: 1.5rem;
108
- --ds-card-spacing-sm: 1rem;
109
- --ds-field-px: 0.75rem;
110
- --ds-cell-p: 0.75rem;
111
- --ds-overlay-p: 1.5rem;
112
- --ds-overlay-gap: 1.5rem;
113
- --ds-popover-p: 1rem;
114
- --ds-menu-p: 0.25rem;
115
- --ds-menu-item-px: 0.75rem;
116
- --ds-menu-item-py: 0.5rem;
117
- --ds-button-px-sm: 0.75rem;
118
- --ds-button-px-md: 1rem;
119
- --ds-button-px-lg: 1rem;
120
- --ds-badge-px: 0.625rem;
121
-
122
- /*
123
- * MATERIALES — cómo es la superficie, más allá del color. Cada valor es un
124
- * `backdrop-filter` completo (p. ej. `blur(28px) saturate(180%)`). Por defecto
125
- * `none`: sin efecto y sin coste. Un tema de cristal (zyro) les da valores.
126
- * El fondo de la página (`page-backdrop`) es un `background-image`: degradado,
127
- * imagen o `none`.
128
- */
129
- --ds-card-backdrop: none;
130
- --ds-overlay-backdrop: none;
131
- --ds-field-backdrop: none;
132
- --ds-sidebar-backdrop: none;
133
- --ds-page-backdrop: none;
134
-
135
- /*
136
- * Dos sombras y no más: reposo y realce al señalar. Una tarjeta que no se
137
- * puede pulsar no necesita la segunda.
138
- *
139
- * EN CAPAS y TINTADAS CON LA TINTA del tema (no negro): una capa tensa
140
- * pegada a la pieza + una suave más lejana — es lo que hace que la tarjeta
141
- * se despegue del lienzo de forma natural y armónica (patrón validado:
142
- * joshwcomeau.com/css/designing-shadows).
143
- */
144
- --shadow-card:
145
- 0 1px 2px oklch(0.186 0.048 269 / 0.08),
146
- 0 3px 10px oklch(0.186 0.048 269 / 0.07);
147
- --shadow-card-hover:
148
- 0 3px 6px oklch(0.186 0.048 269 / 0.1),
149
- 0 14px 32px oklch(0.186 0.048 269 / 0.14);
150
-
151
- --color-background: var(--background);
152
- --color-foreground: var(--foreground);
153
- --color-card: var(--card);
154
- --color-card-foreground: var(--card-foreground);
155
- --color-inset: var(--inset);
156
- --color-inset-foreground: var(--inset-foreground);
157
- --color-popover: var(--popover);
158
- --color-popover-foreground: var(--popover-foreground);
159
- --color-primary: var(--primary);
160
- --color-primary-foreground: var(--primary-foreground);
161
- /* Texto acentuado: SIEMPRE `text-link`, nunca el cian de marca — no alcanza
162
- * contraste de texto sobre las superficies claras. */
163
- --color-link: var(--link);
164
- --color-link-hover: var(--link-hover);
165
- --color-brand: var(--brand);
166
- --color-brand-bright: var(--brand-bright);
167
- --color-secondary: var(--secondary);
168
- --color-secondary-foreground: var(--secondary-foreground);
169
- --color-muted: var(--muted);
170
- --color-muted-foreground: var(--muted-foreground);
171
- --color-accent: var(--accent);
172
- --color-accent-foreground: var(--accent-foreground);
173
- --color-destructive: var(--destructive);
174
- --color-destructive-foreground: var(--destructive-foreground);
175
- --color-border: var(--border);
176
- --color-input: var(--input);
177
- --color-ring: var(--ring);
178
-
179
- --color-chart-1: var(--chart-1);
180
- --color-chart-2: var(--chart-2);
181
- --color-chart-3: var(--chart-3);
182
- --color-chart-4: var(--chart-4);
183
- --color-chart-5: var(--chart-5);
184
-
185
- /*
186
- * Estado. Se exponen como colores del tema para que una etiqueta de severidad
187
- * se escriba `bg-danger-soft text-danger` y nadie tenga que
188
- * volver a elegir un rojo a mano.
189
- */
190
- --color-danger: var(--danger);
191
- --color-danger-soft: var(--danger-soft);
192
- --color-warning: var(--warning);
193
- --color-warning-soft: var(--warning-soft);
194
- --color-success: var(--success);
195
- --color-success-soft: var(--success-soft);
196
- --color-neutral: var(--neutral);
197
- --color-neutral-soft: var(--neutral-soft);
198
- --color-info: var(--info);
199
- --color-info-soft: var(--info-soft);
200
-
201
- --color-sidebar: var(--sidebar);
202
- --color-sidebar-foreground: var(--sidebar-foreground);
203
- --color-sidebar-primary: var(--sidebar-primary);
204
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
205
- --color-sidebar-accent: var(--sidebar-accent);
206
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
207
- --color-sidebar-border: var(--sidebar-border);
208
- --color-sidebar-ring: var(--sidebar-ring);
209
- }
210
-
211
- @layer base {
212
- * {
213
- @apply border-border outline-ring/50;
214
- }
215
-
216
- html,
217
- body,
218
- #app {
219
- min-height: 100%;
220
- }
221
-
222
- body {
223
- @apply bg-background text-foreground font-sans m-0 antialiased;
224
- }
225
-
226
- a {
227
- @apply text-link hover:text-link-hover;
228
- }
229
-
230
- /*
231
- * EXCEPCIÓN: los enlaces del CHROME no son enlaces de texto — son destinos de
232
- * navegación. Deben heredar el color de su superficie (blanco en la barra de
233
- * marca, tinta en un menú), no el azul de enlace, que rompería el diseño.
234
- */
235
- [data-slot='sidebar'] a,
236
- [data-sidebar='menu-button'],
237
- [role='menuitem'],
238
- [role='menuitemradio'],
239
- [role='option'] {
240
- color: inherit;
241
- }
242
- [data-slot='sidebar'] a:hover,
243
- [data-sidebar='menu-button']:hover {
244
- color: inherit;
245
- }
246
-
247
- ::selection {
248
- background: color-mix(in oklch, var(--brand) 25%, transparent);
249
- }
250
-
251
- /*
252
- * TARJETA según el design system: radio `xl`, borde de 1px y sombra de reposo.
253
- * `components/ui/card.tsx` ya lleva estos valores; esta regla los GARANTIZA
254
- * aunque alguien regenere el primitivo con la CLI de shadcn (que los pisa).
255
- */
256
- [data-slot='card'] {
257
- @apply rounded-xl shadow-card ring-0;
258
- border: 1px solid var(--border);
259
- }
260
-
261
- /*
262
- * Estados de interacción, resueltos UNA vez para toda la aplicación
263
- * (spec `sistema-de-componentes`). Ninguna feature debería volver a declarar
264
- * cursor ni anillo de foco.
265
- *
266
- * La lista de roles cubre lo que Base UI renderiza sin <button>: opciones de
267
- * menú y select, pestañas, switches, checkboxes y radios — con hover pero sin
268
- * pointer quedarían «muertos» al señalar.
269
- */
270
- button:not(:disabled),
271
- select:not(:disabled),
272
- [role='button']:not([aria-disabled='true'], [data-disabled]),
273
- [role='menuitem']:not([aria-disabled='true'], [data-disabled]),
274
- [role='menuitemcheckbox']:not([aria-disabled='true'], [data-disabled]),
275
- [role='menuitemradio']:not([aria-disabled='true'], [data-disabled]),
276
- [role='option']:not([aria-disabled='true'], [data-disabled]),
277
- [role='tab']:not([aria-disabled='true'], [data-disabled]),
278
- [role='switch']:not([aria-disabled='true'], [data-disabled]),
279
- [role='checkbox']:not([aria-disabled='true'], [data-disabled]),
280
- [role='radio']:not([aria-disabled='true'], [data-disabled]),
281
- [role='combobox']:not([aria-disabled='true'], [data-disabled]),
282
- [role='slider']:not([aria-disabled='true'], [data-disabled]),
283
- input[type='checkbox']:not(:disabled),
284
- input[type='radio']:not(:disabled),
285
- input[type='range']:not(:disabled),
286
- a[href],
287
- label[for],
288
- summary {
289
- @apply cursor-pointer;
290
- }
291
-
292
- button:disabled,
293
- select:disabled,
294
- input:disabled,
295
- [aria-disabled='true'],
296
- [data-disabled] {
297
- @apply cursor-not-allowed;
298
- }
299
-
300
- /*
301
- * Los primitivos de menú/select traen `cursor-default` como UTILIDAD, y las
302
- * utilidades ganan a la capa base por orden de cascada — por eso una opción
303
- * de idioma quedaba sin pointer aunque la regla de arriba la cubriera. Como
304
- * los primitivos no se editan a mano, aquí el pointer se IMPONE para los
305
- * roles interactivos de esos popups.
306
- */
307
- [role='menuitem']:not([aria-disabled='true'], [data-disabled]),
308
- [role='menuitemcheckbox']:not([aria-disabled='true'], [data-disabled]),
309
- [role='menuitemradio']:not([aria-disabled='true'], [data-disabled]),
310
- [role='option']:not([aria-disabled='true'], [data-disabled]) {
311
- cursor: pointer !important;
312
- }
313
-
314
- [aria-busy='true'] {
315
- @apply cursor-progress;
316
- }
317
-
318
- /* Foco visible solo en navegación por teclado, nunca al hacer clic. */
319
- :focus-visible {
320
- @apply outline-ring outline-2 outline-offset-2;
321
- }
322
-
323
- /*
324
- * SIDEBAR FLOTANTE según el DS (Identity Hub): la barra va despegada del
325
- * borde, con el radio de vista (2xl ≈ 20px) y la sombra de realce — el efecto
326
- * flotante es lo que le da el aire moderno. El primitivo trae `rounded-lg` y
327
- * `shadow-sm` en su variante floating; se corrige AQUÍ (capa base), no en el
328
- * componente, igual que la tarjeta.
329
- */
330
- [data-variant='floating'] [data-slot='sidebar-inner'] {
331
- border-radius: var(--radius-2xl);
332
- box-shadow: var(--shadow-card-hover);
333
- }
334
-
335
- /*
336
- * TAMAÑO Y ALINEACIÓN DE LA NAVEGACIÓN — ajuste del DS sobre el primitivo
337
- * (capa base, no el componente; los `!important` baten a las utilidades que
338
- * el primitivo trae escritas).
339
- *
340
- * EXPANDIDO: filas de 40px con icono de 18px — las medidas del diseño
341
- * («Revisión de llamada»), más presentes que las del shadcn por defecto.
342
- *
343
- * COLAPSADO: el primitivo amplió el raíl a 64px (`--sidebar-width-icon: 4rem`)
344
- * pero conservó botones de 32px y `p-2` (8px) pensados para el raíl de 48px
345
- * del shadcn original: quedaban pegados a la izquierda con 24px muertos.
346
- * Botón a 40px y padding lateral a (64−40)/2 = 12px → centrado.
347
- */
348
- [data-sidebar='menu-button'] {
349
- min-height: 2.5rem;
350
- }
351
- [data-sidebar='menu-button'] svg {
352
- width: 1.125rem !important;
353
- height: 1.125rem !important;
354
- }
355
-
356
- [data-collapsible='icon'] [data-slot='sidebar-header'],
357
- [data-collapsible='icon'] [data-slot='sidebar-footer'],
358
- [data-collapsible='icon'] [data-slot='sidebar-group'] {
359
- padding-inline: 0.75rem;
360
- }
361
- [data-collapsible='icon'] [data-sidebar='menu-button'] {
362
- width: 2.5rem !important;
363
- height: 2.5rem !important;
364
- padding: 0.6875rem !important; /* (40−18)/2: icono centrado */
365
- }
366
- /* Botones lg (marca, cuenta): dentro va una ficha de 32px → 4px de aire. */
367
- [data-collapsible='icon'] [data-sidebar='menu-button'][data-size='lg'] {
368
- padding: 0.25rem !important;
369
- }
370
-
371
- /*
372
- * `prefers-reduced-motion` no es un detalle de cortesía: en un chrome que se
373
- * pliega a diario, la animación de anchura es justo el movimiento que molesta
374
- * a quien pide menos movimiento.
375
- */
376
- @media (prefers-reduced-motion: reduce) {
377
- *,
378
- *::before,
379
- *::after {
380
- transition-duration: 0.01ms !important;
381
- animation-duration: 0.01ms !important;
382
- animation-iteration-count: 1 !important;
383
- }
384
- }
385
- }
386
-
387
-
388
- /*
389
- * Aplicación de los MATERIALES. Va en la capa base para que cualquier utilidad
390
- * explícita la gane, y por `data-slot` para que lo hereden todos los
391
- * componentes del paquete sin que ninguno escriba un efecto propio.
392
- */
393
- @layer base {
394
- body {
395
- background-image: var(--ds-page-backdrop);
396
- background-attachment: fixed;
397
- background-size: cover;
398
- }
399
-
400
- [data-slot='card'] {
401
- -webkit-backdrop-filter: var(--ds-card-backdrop);
402
- backdrop-filter: var(--ds-card-backdrop);
403
- }
404
-
405
- [data-slot='popover-content'],
406
- [data-slot='dropdown-menu-content'],
407
- [data-slot='dropdown-menu-sub-content'],
408
- [data-slot='select-content'],
409
- [data-slot='dialog-content'],
410
- [data-slot='sheet-content'],
411
- [data-slot='tooltip-content'] {
412
- -webkit-backdrop-filter: var(--ds-overlay-backdrop);
413
- backdrop-filter: var(--ds-overlay-backdrop);
414
- }
415
-
416
- [data-slot='input'],
417
- [data-slot='textarea'],
418
- [data-slot='select-trigger'] {
419
- -webkit-backdrop-filter: var(--ds-field-backdrop);
420
- backdrop-filter: var(--ds-field-backdrop);
421
- }
422
-
423
- [data-slot='sidebar-inner'] {
424
- -webkit-backdrop-filter: var(--ds-sidebar-backdrop);
425
- backdrop-filter: var(--ds-sidebar-backdrop);
426
- }
427
- }
1
+ /*
2
+ * orbital-ds · ESTRUCTURA común a todos los temas.
3
+ *
4
+ * Reglas de oro:
5
+ * 1. Esta hoja no tiene colores ni fuentes concretas: eso lo aporta un TEMA
6
+ * (`orbital-ds/themes/<tema>.css`).
7
+ * 2. Todo lo que cambia el aspecto es una VARIABLE (`--ds-*` o las de color
8
+ * de shadcn). Las utilidades de Tailwind solo REFERENCIAN variables
9
+ * (`@theme inline { --radius-sm: var(--ds-radius-sm) }`), nunca llevan un
10
+ * valor escrito. Por eso un tema puede cambiar formas, sombras, fuentes,
11
+ * escala tipográfica y anatomía del sidebar, también EN VIVO.
12
+ * 3. Los valores por defecto viven en la capa `theme` de Tailwind: cualquier
13
+ * tema (CSS sin capa) los gana, sea cual sea el orden de importación.
14
+ *
15
+ * Uso en la app:
16
+ *
17
+ * @import 'orbital-ds/themes/<tema>.fonts.css'; <- lo PRIMERO del fichero
18
+ * @import 'tailwindcss';
19
+ * @import 'orbital-ds/styles.css'; <- esta estructura
20
+ * @import 'orbital-ds/themes/<tema>.css'; <- un tema
21
+ * @source '../node_modules/orbital-ds/dist'; <- que Tailwind vea las clases
22
+ */
23
+
24
+ @custom-variant dark (&:is(.dark *));
25
+
26
+ @theme inline {
27
+ /* ── TIPOGRAFÍA ─────────────────────────────────────────────────────────
28
+ * Familias: las define el tema (--ds-font-sans / -display / -mono).
29
+ * Escala: Tailwind la lee de variables; cada paso lleva tamaño e interlineado.
30
+ * Los tamaños por defecto son los estándar de Tailwind.
31
+ * Display text-4xl · H1 text-3xl · H2 text-xl · H3 text-base · cuerpo text-sm
32
+ * etiqueta/ayuda text-xs. La jerarquía la hacen tamaño y peso, no el color.
33
+ */
34
+ --font-sans: var(--ds-font-sans);
35
+ --font-display: var(--ds-font-display);
36
+ --font-mono: var(--ds-font-mono);
37
+
38
+ --ds-font-sans: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
39
+ --ds-font-display: var(--ds-font-sans);
40
+ --ds-font-mono: ui-monospace, 'SF Mono', Menlo, monospace;
41
+
42
+ --text-xs: var(--ds-text-xs);
43
+ --text-xs--line-height: var(--ds-leading-xs);
44
+ --text-sm: var(--ds-text-sm);
45
+ --text-sm--line-height: var(--ds-leading-sm);
46
+ --text-base: var(--ds-text-base);
47
+ --text-base--line-height: var(--ds-leading-base);
48
+ --text-lg: var(--ds-text-lg);
49
+ --text-lg--line-height: var(--ds-leading-lg);
50
+ --text-xl: var(--ds-text-xl);
51
+ --text-xl--line-height: var(--ds-leading-xl);
52
+ --text-2xl: var(--ds-text-2xl);
53
+ --text-2xl--line-height: var(--ds-leading-2xl);
54
+ --text-3xl: var(--ds-text-3xl);
55
+ --text-3xl--line-height: var(--ds-leading-3xl);
56
+ --text-4xl: var(--ds-text-4xl);
57
+ --text-4xl--line-height: var(--ds-leading-4xl);
58
+
59
+ --ds-text-xs: 0.75rem;
60
+ --ds-leading-xs: 1.3333;
61
+ --ds-text-sm: 0.875rem;
62
+ --ds-leading-sm: 1.4286;
63
+ --ds-text-base: 1rem;
64
+ --ds-leading-base: 1.5;
65
+ --ds-text-lg: 1.125rem;
66
+ --ds-leading-lg: 1.5556;
67
+ --ds-text-xl: 1.25rem;
68
+ --ds-leading-xl: 1.4;
69
+ --ds-text-2xl: 1.5rem;
70
+ --ds-leading-2xl: 1.3333;
71
+ --ds-text-3xl: 1.875rem;
72
+ --ds-leading-3xl: 1.2;
73
+ --ds-text-4xl: 2.25rem;
74
+ --ds-leading-4xl: 1.1111;
75
+
76
+ /* Espaciado de letras de los títulos (los que no traen su propia clase). */
77
+ --ds-heading-tracking: normal;
78
+
79
+ /* ── FORMAS ─────────────────────────────────────────────────────────────
80
+ * Rampa de radios, derivada de `--radius` (mover UN valor cambia la redondez
81
+ * del producto). Un tema puede además fijar cada paso con --ds-radius-*.
82
+ * sm chip · md control · lg fila · xl tarjeta · 2xl vista y barra
83
+ * Radios semánticos: los componentes piden el hueco, no una medida.
84
+ * control: campos, selects, pestañas, tooltips · overlay: diálogos y paneles
85
+ */
86
+ --radius-sm: var(--ds-radius-sm);
87
+ --radius-md: var(--ds-radius-md);
88
+ --radius-lg: var(--ds-radius-lg);
89
+ --radius-xl: var(--ds-radius-xl);
90
+ --radius-2xl: var(--ds-radius-2xl);
91
+ --radius-control: var(--ds-radius-control);
92
+ --radius-overlay: var(--ds-radius-overlay);
93
+
94
+ --ds-radius-sm: calc(var(--radius) - 4px);
95
+ --ds-radius-md: calc(var(--radius) - 2px);
96
+ --ds-radius-lg: var(--radius);
97
+ --ds-radius-xl: calc(var(--radius) + 6px);
98
+ --ds-radius-2xl: calc(var(--radius) + 8px);
99
+ --ds-radius-control: 2rem;
100
+ --ds-radius-overlay: 2rem;
101
+
102
+ /* ── ALTURA DE CONTROLES ────────────────────────────────────────────────
103
+ * Una escala para botón, campo y select: alineados en una misma fila.
104
+ */
105
+ --ds-control-h-sm: 2rem;
106
+ --ds-control-h-md: 2.25rem;
107
+ --ds-control-h-lg: 2.5rem;
108
+
109
+ /* ── SOMBRAS ────────────────────────────────────────────────────────────
110
+ * Un hueco por tipo de pieza. `control` está vacío (`0 0 #0000`): existe para que
111
+ * un tema pueda dar relieve a los campos con solo dar valor. «Sin sombra» se
112
+ * escribe `0 0 #0000`, NUNCA `none`: una variable con `none` invalida todo el
113
+ * `box-shadow` (anillo de foco incluido).
114
+ * card/-hover: tarjeta (reposo y realce) · raised: piezas que flotan
115
+ * overlay: capas · popover: menús y selects
116
+ */
117
+ --shadow-card: var(--ds-shadow-card);
118
+ --shadow-card-hover: var(--ds-shadow-card-hover);
119
+ --shadow-control: var(--ds-shadow-control);
120
+ --shadow-raised: var(--ds-shadow-raised);
121
+ --shadow-overlay: var(--ds-shadow-overlay);
122
+ --shadow-popover: var(--ds-shadow-popover);
123
+
124
+ --ds-shadow-card: 0 1px 2px rgb(0 0 0 / 0.08), 0 3px 10px rgb(0 0 0 / 0.07);
125
+ --ds-shadow-card-hover: 0 3px 6px rgb(0 0 0 / 0.1), 0 14px 32px rgb(0 0 0 / 0.14);
126
+ --ds-shadow-control: 0 0 #0000;
127
+ --ds-shadow-raised: 0 1px 3px rgb(0 0 0 / 0.1), 0 1px 2px rgb(0 0 0 / 0.1);
128
+ --ds-shadow-overlay: 0 10px 15px rgb(0 0 0 / 0.1), 0 4px 6px rgb(0 0 0 / 0.1);
129
+ --ds-shadow-popover: 0 25px 50px rgb(0 0 0 / 0.25);
130
+
131
+ /* ── CAPAS, MOVIMIENTO, FOCO, VELO ──────────────────────────────────────
132
+ * z-index con nombre (nunca un número en un componente) · dos o tres
133
+ * duraciones · grosor del anillo de foco · velo tras diálogos y paneles.
134
+ */
135
+ --ds-z-raised: 10;
136
+ --ds-z-sticky: 20;
137
+ --ds-z-overlay: 50;
138
+
139
+ --ds-motion-fast: 100ms;
140
+ --ds-motion-base: 200ms;
141
+ --ds-motion-slow: 300ms;
142
+ --ds-motion-blink: 1s;
143
+
144
+ --ds-ring-width: 3px;
145
+
146
+ --color-scrim: var(--ds-scrim);
147
+ --ds-scrim: oklch(0 0 0 / 0.8);
148
+
149
+ /* ── ESPACIADO INTERNO ──────────────────────────────────────────────────
150
+ * Un hueco por tipo de pieza. Más aire o más densidad = cambiar estos valores.
151
+ * card (md / sm) · field: campos y selects · cell: celdas de tabla
152
+ * overlay: diálogos y paneles · popover · menu / menu-item: menús
153
+ * button: por tamaño · badge: etiquetas
154
+ */
155
+ --ds-card-spacing: 1.5rem;
156
+ --ds-card-spacing-sm: 1rem;
157
+ --ds-field-px: 0.75rem;
158
+ --ds-cell-p: 0.75rem;
159
+ --ds-overlay-p: 1.5rem;
160
+ --ds-overlay-gap: 1.5rem;
161
+ --ds-popover-p: 1rem;
162
+ --ds-menu-p: 0.25rem;
163
+ --ds-menu-item-px: 0.75rem;
164
+ --ds-menu-item-py: 0.5rem;
165
+ --ds-button-px-sm: 0.75rem;
166
+ --ds-button-px-md: 1rem;
167
+ --ds-button-px-lg: 1rem;
168
+ --ds-badge-px: 0.625rem;
169
+
170
+ /* ── MATERIALES ─────────────────────────────────────────────────────────
171
+ * Cómo es la superficie, más allá del color. Cada valor es un
172
+ * `backdrop-filter` completo (p. ej. `blur(28px) saturate(180%)`); por
173
+ * defecto `none`: sin efecto y sin coste. `page-backdrop` es un
174
+ * `background-image` para el fondo de la página (degradado, imagen o none).
175
+ * Referencia: el cristal va en la capa de navegación y controles; el
176
+ * contenido (tarjetas) se mantiene casi opaco.
177
+ */
178
+ --ds-card-backdrop: none;
179
+ --ds-overlay-backdrop: none;
180
+ --ds-field-backdrop: none;
181
+ --ds-sidebar-backdrop: none;
182
+ --ds-page-backdrop: none;
183
+
184
+ /* ── SIDEBAR: ANATOMÍA ──────────────────────────────────────────────────
185
+ * pad: hueco entre la barra y el borde de la ventana (0 = pegada al borde).
186
+ * radius / shadow / ring: silueta de la barra flotante (0 / none = sin silueta).
187
+ * edge: borde de la barra pegada (p. ej. `1px solid var(--sidebar-border)`).
188
+ * item-radius / item-h: forma y altura de las filas de navegación.
189
+ */
190
+ --ds-sidebar-pad: 0.5rem;
191
+ --ds-sidebar-radius: var(--ds-radius-lg);
192
+ --ds-sidebar-shadow: var(--ds-shadow-raised);
193
+ --ds-sidebar-ring: 1px;
194
+ --ds-sidebar-edge: 0 solid transparent;
195
+ --ds-sidebar-item-radius: var(--ds-radius-md);
196
+ --ds-sidebar-item-h: 2.5rem;
197
+
198
+ /* ── COLOR (mapeo de las variables del tema a utilidades) ─────────────── */
199
+ --color-background: var(--background);
200
+ --color-foreground: var(--foreground);
201
+ --color-card: var(--card);
202
+ --color-card-foreground: var(--card-foreground);
203
+ --color-inset: var(--inset);
204
+ --color-inset-foreground: var(--inset-foreground);
205
+ --color-popover: var(--popover);
206
+ --color-popover-foreground: var(--popover-foreground);
207
+ --color-primary: var(--primary);
208
+ --color-primary-foreground: var(--primary-foreground);
209
+ /* Texto acentuado: SIEMPRE `text-link`, nunca el color de marca — puede no
210
+ * alcanzar contraste de texto sobre las superficies claras. */
211
+ --color-link: var(--link);
212
+ --color-link-hover: var(--link-hover);
213
+ --color-brand: var(--brand);
214
+ --color-brand-bright: var(--brand-bright);
215
+ --color-secondary: var(--secondary);
216
+ --color-secondary-foreground: var(--secondary-foreground);
217
+ --color-muted: var(--muted);
218
+ --color-muted-foreground: var(--muted-foreground);
219
+ --color-accent: var(--accent);
220
+ --color-accent-foreground: var(--accent-foreground);
221
+ --color-destructive: var(--destructive);
222
+ --color-destructive-foreground: var(--destructive-foreground);
223
+ --color-border: var(--border);
224
+ --color-input: var(--input);
225
+ --color-ring: var(--ring);
226
+
227
+ --color-chart-1: var(--chart-1);
228
+ --color-chart-2: var(--chart-2);
229
+ --color-chart-3: var(--chart-3);
230
+ --color-chart-4: var(--chart-4);
231
+ --color-chart-5: var(--chart-5);
232
+
233
+ /*
234
+ * Estado. Se exponen como colores del tema para que una etiqueta de severidad
235
+ * se escriba `bg-danger-soft text-danger` y nadie tenga que elegir un rojo a
236
+ * mano.
237
+ */
238
+ --color-danger: var(--danger);
239
+ --color-danger-soft: var(--danger-soft);
240
+ --color-warning: var(--warning);
241
+ --color-warning-soft: var(--warning-soft);
242
+ --color-success: var(--success);
243
+ --color-success-soft: var(--success-soft);
244
+ --color-neutral: var(--neutral);
245
+ --color-neutral-soft: var(--neutral-soft);
246
+ --color-info: var(--info);
247
+ --color-info-soft: var(--info-soft);
248
+
249
+ --color-sidebar: var(--sidebar);
250
+ --color-sidebar-foreground: var(--sidebar-foreground);
251
+ --color-sidebar-primary: var(--sidebar-primary);
252
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
253
+ --color-sidebar-accent: var(--sidebar-accent);
254
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
255
+ --color-sidebar-border: var(--sidebar-border);
256
+ --color-sidebar-ring: var(--sidebar-ring);
257
+ }
258
+
259
+ @layer base {
260
+ * {
261
+ @apply border-border outline-ring/50;
262
+ }
263
+
264
+ html,
265
+ body,
266
+ #app {
267
+ min-height: 100%;
268
+ }
269
+
270
+ body {
271
+ @apply bg-background text-foreground font-sans m-0 antialiased;
272
+ background-image: var(--ds-page-backdrop);
273
+ background-attachment: fixed;
274
+ background-size: cover;
275
+ }
276
+
277
+ a {
278
+ @apply text-link hover:text-link-hover;
279
+ }
280
+
281
+ /* Títulos: fuente de titulares del tema (`--ds-font-display`). */
282
+ h1,
283
+ h2,
284
+ h3,
285
+ h4,
286
+ [data-slot='card-title'],
287
+ [data-slot='dialog-title'],
288
+ [data-slot='sheet-title'],
289
+ [data-slot='alert-title'] {
290
+ font-family: var(--font-display);
291
+ }
292
+
293
+ h1,
294
+ h2,
295
+ h3,
296
+ h4 {
297
+ letter-spacing: var(--ds-heading-tracking);
298
+ }
299
+
300
+ /*
301
+ * EXCEPCIÓN: los enlaces del CHROME no son enlaces de texto — son destinos de
302
+ * navegación. Deben heredar el color de su superficie, no el color de enlace.
303
+ */
304
+ [data-slot='sidebar'] a,
305
+ [data-sidebar='menu-button'],
306
+ [role='menuitem'],
307
+ [role='menuitemradio'],
308
+ [role='option'] {
309
+ color: inherit;
310
+ }
311
+ [data-slot='sidebar'] a:hover,
312
+ [data-sidebar='menu-button']:hover {
313
+ color: inherit;
314
+ }
315
+
316
+ ::selection {
317
+ background: color-mix(in oklch, var(--brand) 25%, transparent);
318
+ }
319
+
320
+ /*
321
+ * TARJETA: radio `xl`, borde de 1px y sombra de reposo. `ui/card.tsx` ya lleva
322
+ * estos valores; esta regla los GARANTIZA aunque se regenere con la CLI de
323
+ * shadcn. Material de la tarjeta: `--ds-card-backdrop`.
324
+ */
325
+ [data-slot='card'] {
326
+ @apply rounded-xl shadow-card ring-0;
327
+ border: 1px solid var(--border);
328
+ -webkit-backdrop-filter: var(--ds-card-backdrop);
329
+ backdrop-filter: var(--ds-card-backdrop);
330
+ }
331
+
332
+ /*
333
+ * Estados de interacción, resueltos UNA vez para toda la aplicación. La lista
334
+ * de roles cubre lo que Base UI renderiza sin <button>: opciones de menú y
335
+ * select, pestañas, switches, checkboxes y radios.
336
+ */
337
+ button:not(:disabled),
338
+ select:not(:disabled),
339
+ [role='button']:not([aria-disabled='true'], [data-disabled]),
340
+ [role='menuitem']:not([aria-disabled='true'], [data-disabled]),
341
+ [role='menuitemcheckbox']:not([aria-disabled='true'], [data-disabled]),
342
+ [role='menuitemradio']:not([aria-disabled='true'], [data-disabled]),
343
+ [role='option']:not([aria-disabled='true'], [data-disabled]),
344
+ [role='tab']:not([aria-disabled='true'], [data-disabled]),
345
+ [role='switch']:not([aria-disabled='true'], [data-disabled]),
346
+ [role='checkbox']:not([aria-disabled='true'], [data-disabled]),
347
+ [role='radio']:not([aria-disabled='true'], [data-disabled]),
348
+ [role='combobox']:not([aria-disabled='true'], [data-disabled]),
349
+ [role='slider']:not([aria-disabled='true'], [data-disabled]),
350
+ input[type='checkbox']:not(:disabled),
351
+ input[type='radio']:not(:disabled),
352
+ input[type='range']:not(:disabled),
353
+ a[href],
354
+ label[for],
355
+ summary {
356
+ @apply cursor-pointer;
357
+ }
358
+
359
+ button:disabled,
360
+ select:disabled,
361
+ input:disabled,
362
+ [aria-disabled='true'],
363
+ [data-disabled] {
364
+ @apply cursor-not-allowed;
365
+ }
366
+
367
+ /*
368
+ * Los primitivos de menú/select traen `cursor-default` como UTILIDAD, y las
369
+ * utilidades ganan a la capa base: aquí el pointer se IMPONE para los roles
370
+ * interactivos de esos popups.
371
+ */
372
+ [role='menuitem']:not([aria-disabled='true'], [data-disabled]),
373
+ [role='menuitemcheckbox']:not([aria-disabled='true'], [data-disabled]),
374
+ [role='menuitemradio']:not([aria-disabled='true'], [data-disabled]),
375
+ [role='option']:not([aria-disabled='true'], [data-disabled]) {
376
+ cursor: pointer !important;
377
+ }
378
+
379
+ [aria-busy='true'] {
380
+ @apply cursor-progress;
381
+ }
382
+
383
+ /* Foco visible solo en navegación por teclado, nunca al hacer clic. */
384
+ :focus-visible {
385
+ @apply outline-ring outline-2 outline-offset-2;
386
+ }
387
+
388
+ /*
389
+ * MATERIALES de la capa de controles y navegación. Por `data-slot`, para que
390
+ * los hereden todos los componentes sin que ninguno escriba un efecto propio.
391
+ */
392
+ [data-slot='popover-content'],
393
+ [data-slot='dropdown-menu-content'],
394
+ [data-slot='dropdown-menu-sub-content'],
395
+ [data-slot='select-content'],
396
+ [data-slot='dialog-content'],
397
+ [data-slot='sheet-content'],
398
+ [data-slot='tooltip-content'] {
399
+ -webkit-backdrop-filter: var(--ds-overlay-backdrop);
400
+ backdrop-filter: var(--ds-overlay-backdrop);
401
+ }
402
+
403
+ [data-slot='input'],
404
+ [data-slot='textarea'],
405
+ [data-slot='select-trigger'] {
406
+ -webkit-backdrop-filter: var(--ds-field-backdrop);
407
+ backdrop-filter: var(--ds-field-backdrop);
408
+ }
409
+
410
+ /*
411
+ * SIDEBAR. Anatomía por variables (ver arriba): `--ds-sidebar-*`. El borde de
412
+ * la barra pegada y el material de cristal van aquí; la silueta flotante
413
+ * (pad, radio, sombra, anillo) la aplica el propio componente.
414
+ */
415
+ [data-slot='sidebar-inner'] {
416
+ border-right: var(--ds-sidebar-edge);
417
+ -webkit-backdrop-filter: var(--ds-sidebar-backdrop);
418
+ backdrop-filter: var(--ds-sidebar-backdrop);
419
+ }
420
+
421
+ /*
422
+ * TAMAÑO Y ALINEACIÓN DE LA NAVEGACIÓN (los `!important` baten a las
423
+ * utilidades que el primitivo trae escritas).
424
+ * EXPANDIDO: filas de altura `--ds-sidebar-item-h` con icono de 18px.
425
+ * COLAPSADO: botón de 40px y padding lateral de 12px → icono centrado en el
426
+ * raíl de 64px.
427
+ */
428
+ [data-sidebar='menu-button'] {
429
+ min-height: var(--ds-sidebar-item-h);
430
+ }
431
+ [data-sidebar='menu-button'] svg {
432
+ width: 1.125rem !important;
433
+ height: 1.125rem !important;
434
+ }
435
+
436
+ [data-collapsible='icon'] [data-slot='sidebar-header'],
437
+ [data-collapsible='icon'] [data-slot='sidebar-footer'],
438
+ [data-collapsible='icon'] [data-slot='sidebar-group'] {
439
+ padding-inline: 0.75rem;
440
+ }
441
+ [data-collapsible='icon'] [data-sidebar='menu-button'] {
442
+ width: 2.5rem !important;
443
+ height: 2.5rem !important;
444
+ padding: 0.6875rem !important; /* (40−18)/2: icono centrado */
445
+ }
446
+ /* Botones lg (marca, cuenta): dentro va una ficha de 32px → 4px de aire. */
447
+ [data-collapsible='icon'] [data-sidebar='menu-button'][data-size='lg'] {
448
+ padding: 0.25rem !important;
449
+ }
450
+
451
+ /*
452
+ * `prefers-reduced-motion`: en un chrome que se pliega a diario, la animación
453
+ * de anchura es justo el movimiento que molesta a quien pide menos movimiento.
454
+ */
455
+ @media (prefers-reduced-motion: reduce) {
456
+ *,
457
+ *::before,
458
+ *::after {
459
+ transition-duration: 0.01ms !important;
460
+ animation-duration: 0.01ms !important;
461
+ animation-iteration-count: 1 !important;
462
+ }
463
+ }
464
+
465
+ /*
466
+ * `prefers-reduced-transparency`: quien lo pide no debe recibir cristal. Se
467
+ * anulan los materiales (el soporte del navegador aún es limitado).
468
+ */
469
+ @media (prefers-reduced-transparency: reduce) {
470
+ [data-slot='card'],
471
+ [data-slot='popover-content'],
472
+ [data-slot='dropdown-menu-content'],
473
+ [data-slot='dropdown-menu-sub-content'],
474
+ [data-slot='select-content'],
475
+ [data-slot='dialog-content'],
476
+ [data-slot='sheet-content'],
477
+ [data-slot='tooltip-content'],
478
+ [data-slot='input'],
479
+ [data-slot='textarea'],
480
+ [data-slot='select-trigger'],
481
+ [data-slot='sidebar-inner'] {
482
+ -webkit-backdrop-filter: none;
483
+ backdrop-filter: none;
484
+ }
485
+ }
486
+ }