@higinio/design-mcp 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +4 -4
- package/README.md +5 -5
- package/assets/bruma/design-doc.md +1083 -0
- package/assets/bruma/framework.css +1374 -0
- package/assets/bruma/styleguide.html +4687 -0
- package/assets/bruma/tailwind.css +121 -0
- package/assets/bruma/tokens.css +147 -0
- package/assets/bruma/tokens.json +995 -0
- package/assets/celeste/design-doc.md +56 -5
- package/assets/celeste/framework.css +93 -4
- package/assets/celeste/styleguide.html +252 -19
- package/assets/nocturna/design-doc.md +63 -12
- package/assets/nocturna/framework.css +102 -7
- package/assets/nocturna/styleguide.html +341 -118
- package/assets/nocturna/tokens.css +8 -3
- package/assets/salvia/design-doc.md +1067 -0
- package/assets/salvia/framework.css +1370 -0
- package/assets/salvia/styleguide.html +4670 -0
- package/assets/salvia/tailwind.css +121 -0
- package/assets/salvia/tokens.css +143 -0
- package/assets/salvia/tokens.json +1023 -0
- package/dist/cli.js +7 -5
- package/dist/doc.js +18 -4
- package/dist/preflight.js +2 -1
- package/dist/server.js +7 -7
- package/dist/systems.js +27 -1
- package/dist/validate.js +13 -5
- package/package.json +9 -31
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
| `night-900` | `#0b0d12` | Superficie 1 (tarjetas) |
|
|
74
74
|
| `night-800` | `#14171f` | Superficie 2 (modales, dropdowns, inputs) |
|
|
75
75
|
| `night-700` | `#1e222d` | Superficie 3, hover de filas |
|
|
76
|
-
| `night-600` | `#2a2f3d` |
|
|
76
|
+
| `night-600` | `#2a2f3d` | Superficie de separadores sutiles |
|
|
77
77
|
| `night-500` | `#3d4354` | Bordes por defecto |
|
|
78
78
|
| `night-450` | `#5f6575` | Solo light: texto atenuado (`--text-muted`) — `night-400` caía a 4.27:1 sobre `--surface-3` |
|
|
79
79
|
| `night-400` | `#6b7183` | Deshabilitado, placeholders |
|
|
@@ -102,7 +102,7 @@ Los componentes **nunca** usan los primitivos de arriba directamente: consumen e
|
|
|
102
102
|
| `--surface-2` | `night-800` | Modales, dropdowns, inputs |
|
|
103
103
|
| `--surface-3` | `night-700` | Hover de filas, superficie máxima |
|
|
104
104
|
| `--border` | `night-500` | Borde por defecto |
|
|
105
|
-
| `--border-strong` | `night-
|
|
105
|
+
| `--border-strong` | `night-400` | Borde de énfasis (elevación 3, accordion). **Mismo valor en ambos temas**: un gris medio cumple 1.4.11 sobre fondos oscuros (3.26-3.99:1) y claros (4.27-4.87:1). En dark **sube** de peldaño respecto a `--border` (night-500) porque oscurecerlo lo debilitaría (principio 2) |
|
|
106
106
|
| `--text-primary` | `night-100` | Texto principal |
|
|
107
107
|
| `--text-secondary` | `night-200` | Texto secundario |
|
|
108
108
|
| `--text-muted` | `night-300` | Terciario, captions |
|
|
@@ -210,7 +210,7 @@ Tokens compuestos:
|
|
|
210
210
|
|
|
211
211
|
```css
|
|
212
212
|
--border-default: 1px solid var(--night-500);
|
|
213
|
-
--border-emphasis: 1px solid var(--night-
|
|
213
|
+
--border-emphasis: 1px solid var(--night-400);
|
|
214
214
|
--focus-ring: 2px solid var(--accent-400);
|
|
215
215
|
--focus-ring-offset: 2px;
|
|
216
216
|
```
|
|
@@ -228,7 +228,7 @@ En fondo casi negro las sombras solas no bastan: la elevación combina **superfi
|
|
|
228
228
|
| 0 | `night-950` | — | Fondo de la app |
|
|
229
229
|
| 1 | `night-900` | `--shadow-1: 0 1px 2px rgba(0,0,0,.4)` | Tarjetas en reposo |
|
|
230
230
|
| 2 | `night-800` | `--shadow-2: 0 4px 12px rgba(0,0,0,.5)` | Dropdowns, hover |
|
|
231
|
-
| 3 | `night-800` + borde `night-
|
|
231
|
+
| 3 | `night-800` + borde `night-400` | `--shadow-3: 0 12px 32px rgba(0,0,0,.6)` | Modales, popovers |
|
|
232
232
|
|
|
233
233
|
Tokens adicionales:
|
|
234
234
|
|
|
@@ -323,7 +323,7 @@ Indicador de carga circular; también sustituye el label del botón en estado lo
|
|
|
323
323
|
|
|
324
324
|
### 10.5 Textarea
|
|
325
325
|
|
|
326
|
-
Mismos estados que input. Min-height 96px, `resize: vertical`. Contador `text-xs`
|
|
326
|
+
Mismos estados que input. Min-height 96px, `resize: vertical`. Contador (`.field__count`) `text-xs` `--text-muted` a la derecha.
|
|
327
327
|
|
|
328
328
|
### 10.6 Select
|
|
329
329
|
|
|
@@ -399,7 +399,7 @@ Banner de estado **inline y persistente** (a diferencia del Toast, transitorio):
|
|
|
399
399
|
|
|
400
400
|
- 4 tonos: fondo suave semántico (`*-bg`, ej. `info-bg` `#0d1f2c`), borde `1px` del tono al 30%, radius `md`, padding 14px 16px.
|
|
401
401
|
- Icono semántico 20px en el color del tono: info→`info` · success→`circle-check` · warning→`triangle-alert` · danger→`circle-alert`.
|
|
402
|
-
- Título `text-sm` semibold y cuerpo `text-sm`, ambos en el color del tono (
|
|
402
|
+
- Título `text-sm` semibold y cuerpo `text-sm`, ambos en el color del tono, a contraste completo (sin opacidad).
|
|
403
403
|
- Botón cerrar opcional (× 18px, hit-área 36px) vía `onDismiss`; hover fondo `currentColor` al 14%. `role="status"`.
|
|
404
404
|
|
|
405
405
|
### 10.17 Toast
|
|
@@ -466,6 +466,26 @@ Lista de acciones desplegable anclada a un trigger (acciones de fila, botón "m
|
|
|
466
466
|
- Gap vertical 6px. Formato numérico con `Intl.NumberFormat` (`es-MX`).
|
|
467
467
|
- **Count-up** 350ms ease-out al montar (techo `--duration-page`); con `prefers-reduced-motion` muestra el valor directo.
|
|
468
468
|
|
|
469
|
+
#### Chip de métrica
|
|
470
|
+
|
|
471
|
+
Pastilla de una línea con rótulo y valor —por ejemplo, «Agua **3 de 8**»— para métricas secundarias que no merecen una cifra de Stat.
|
|
472
|
+
|
|
473
|
+
- **`.metric-chip`:** radius `full`, borde `1px --border`, fondo `--surface-1`, padding `4px 12px`, `text-sm` `--text-secondary`; icono opcional de 16px en `--text-muted`.
|
|
474
|
+
- **Contenido:** `.metric-chip__label` y `.metric-chip__value`; el valor va en JetBrains Mono peso 500, `--link` y `tabular-nums`.
|
|
475
|
+
- **Fila:** `.metric-chips` agrupa los chips con `display: flex; flex-wrap: wrap; gap: var(--space-2)`, así que saltan de línea en móvil.
|
|
476
|
+
- **Interactivo** (por ejemplo, «sumar un vaso»): `button.metric-chip`, con `min-height: var(--touch-target)` (44px) y padding horizontal de 16px. Hover: fondo `--outline-hover-bg` y borde `--border-hover` (como Outline). Active: fondo `--outline-active-bg` y `scale(.98)`. Foco de teclado con el anillo del sistema (`--focus-ring`, offset `--focus-ring-offset`).
|
|
477
|
+
|
|
478
|
+
```html
|
|
479
|
+
<div class="metric-chips">
|
|
480
|
+
<span class="metric-chip"><span class="metric-chip__label">Latencia</span> <span class="metric-chip__value">42 ms</span></span>
|
|
481
|
+
<button class="metric-chip" type="button" aria-label="Agua: 3 de 8 vasos. Sumar un vaso">
|
|
482
|
+
<span class="metric-chip__label">Agua</span> <span class="metric-chip__value">3 de 8</span>
|
|
483
|
+
</button>
|
|
484
|
+
</div>
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
**Accesibilidad:** el chip interactivo es un `<button>` con nombre accesible completo, no solo la cifra («Agua: 3 de 8 vasos. Sumar un vaso»); el nombre empieza por el texto visible (WCAG 2.5.3) y se actualiza con el valor. El chip informativo es texto: no lleva rol ni foco.
|
|
488
|
+
|
|
469
489
|
### 10.23 Pagination
|
|
470
490
|
|
|
471
491
|
Navegador de páginas 1-indexado, bajo tablas y listas largas.
|
|
@@ -482,6 +502,37 @@ Navegador de páginas 1-indexado, bajo tablas y listas largas.
|
|
|
482
502
|
- **Tabs:** borde inferior `night-600`; indicador 2px `accent-400` que se desliza (250ms).
|
|
483
503
|
- **Breadcrumbs:** `text-sm` `night-300`, separador `/` (tipográfico), último ítem `night-100`.
|
|
484
504
|
|
|
505
|
+
#### Sidebar contraíble
|
|
506
|
+
|
|
507
|
+
Variante del Sidebar para consolas y dashboards con muchas secciones: alterna entre la lista con rótulos y un riel de iconos que deja más ancho a los datos.
|
|
508
|
+
|
|
509
|
+
- **Estados:** expandido, 260px (el ancho del Sidebar), y contraído, riel de iconos de 64px. Clases: `.sidebar.sidebar--collapsible` y, contraído, `.is-collapsed`. Cada ítem lleva su icono de 18px y el nombre en `.sidebar__label`.
|
|
510
|
+
- **Grupos con rótulo:** `.sidebar__group` con un `.sidebar__group-label` (overline: 12px, peso 500, mayúsculas, tracking `+0.08em`, `--text-muted`); 16px entre grupos. En el riel, cada rótulo se convierte en un separador de 1px `--border` y el del primer grupo se oculta.
|
|
511
|
+
- **Ítem activo:** `.is-active` (fondo `--sidebar-active-bg`, texto `--sidebar-active-text`, icono `--sidebar-active-icon`) más `aria-current="page"`.
|
|
512
|
+
- **Botón de contraer:** `.sidebar__item.sidebar__toggle` al pie (`margin-top: auto`), con un chevron que apunta hacia donde se moverá el borde (rota 180° en el riel) y el texto «Contraer» / «Expandir» en su `.sidebar__label`.
|
|
513
|
+
- **Tooltip en el riel:** el `.sidebar__label` queda oculto a la vista pero accesible (patrón de `.visually-hidden`) y aparece a la derecha del ítem con el look del Tooltip (§10.15: `--tooltip-bg`, borde `--tooltip-border`, `text-xs`, delay 400ms) al pasar el cursor —solo con puntero real, §11.5— y al enfocar el ítem con teclado (`:focus-visible`). No es un `title`: el nombre sigue en el árbol de accesibilidad. El riel no recorta su contenido (sin `overflow`) para que el tooltip pueda salir de él.
|
|
514
|
+
- **Por debajo de 768px** (`md`), `.sidebar--collapsible` no se muestra fijo: lo sustituye un botón ☰ `.icon-btn.sidebar-trigger` que abre un Drawer `.drawer--left` (§10.29) con los mismos grupos e ítems. El framework muestra `.sidebar-trigger` solo por debajo de 768px y lo oculta (`display: none`) desde 768px, el mismo breakpoint en el que aparece el sidebar fijo; así el ☰ y el sidebar nunca conviven. El botón lleva `aria-label="Abrir navegación"`, `aria-controls` con el id del drawer y `aria-expanded` (`false` cerrado, `true` abierto).
|
|
515
|
+
- **Motion:** el ancho cambia sin transición —el layout usa `grid-template-columns: auto 1fr` y el contenido sigue al sidebar al instante—; nunca se anima `width`. Solo los rótulos se funden al expandir: `opacity` 0→1 y `translateX(-4px)`→0 en `--duration-fast` `--ease-out`. Con `prefers-reduced-motion`, sin animación (piso global, §11.5).
|
|
516
|
+
|
|
517
|
+
```html
|
|
518
|
+
<nav class="sidebar sidebar--collapsible" id="appNav" aria-label="Principal">
|
|
519
|
+
<div class="sidebar__group" role="group" aria-labelledby="navMonitor">
|
|
520
|
+
<span class="sidebar__group-label" id="navMonitor">Monitoreo</span>
|
|
521
|
+
<a class="sidebar__item is-active" href="/" aria-current="page"><svg aria-hidden="true">…</svg><span class="sidebar__label">Inicio</span></a>
|
|
522
|
+
</div>
|
|
523
|
+
<button class="sidebar__item sidebar__toggle" type="button" aria-expanded="true" aria-controls="appNav">
|
|
524
|
+
<svg aria-hidden="true">…</svg><span class="sidebar__label">Contraer</span>
|
|
525
|
+
</button>
|
|
526
|
+
</nav>
|
|
527
|
+
|
|
528
|
+
<!-- Solo por debajo de 768px -->
|
|
529
|
+
<button class="icon-btn sidebar-trigger" type="button" aria-label="Abrir navegación"
|
|
530
|
+
aria-expanded="false" aria-controls="navDrawer">…menu…</button>
|
|
531
|
+
<div class="drawer drawer--left" id="navDrawer">…los mismos grupos e ítems…</div>
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
**Accesibilidad:** el contenedor es un `<nav>` con `aria-label`; el ítem de la página actual lleva `aria-current="page"` además de `.is-active`. El toggle es un `<button>` real con `aria-expanded` (`true` expandido, `false` en el riel) y `aria-controls` con el id del sidebar; al alternar, su texto cambia entre «Contraer» y «Expandir». El ☰ `.sidebar-trigger` también es un `<button>` con `aria-expanded` y `aria-controls` hacia el drawer, y el drawer sigue el contrato de foco del Modal. Los grupos se declaran con `role="group"` y `aria-labelledby` apuntando a su rótulo, que en el riel sigue siendo legible para el lector aunque se vea como una línea.
|
|
535
|
+
|
|
485
536
|
### 10.25 Skeleton y EmptyState
|
|
486
537
|
|
|
487
538
|
- Skeleton: fondo `night-800` con shimmer (gradiente hacia `night-700`, barre en 1.4s loop). Radius igual al contenido.
|
|
@@ -551,8 +602,8 @@ Campo de fecha con calendario en popover: el input abre el calendario al hacer c
|
|
|
551
602
|
|
|
552
603
|
- **`.datepicker`** (ancla `relative`), **`.datepicker__input`** (reutiliza el `Input` §10.4, con icono de calendario leading vía `.input-wrap`), **`.datepicker__calendar`** = panel que reutiliza el look de `.popover__panel` sobre `--surface-2` (borde `--border-strong`, sombra nivel 3, radius `md`).
|
|
553
604
|
- **Calendario:** `.calendar` contiene `.calendar__header` (mes y año + prev/next con los iconos chevron-left/chevron-right), `.calendar__weekdays` (iniciales L–D) y `.calendar__grid` con los `.calendar__day`.
|
|
554
|
-
- **Estados del día:** `.calendar__day--today` (aro interior `accent-400` + texto `accent-300`), `.calendar__day--selected` (fondo `accent-400`, texto `--text-on-accent`, peso 600 — no depende solo del color), `.calendar__day--outside` (días del mes vecino,
|
|
555
|
-
- **Accesibilidad:** `.
|
|
605
|
+
- **Estados del día:** `.calendar__day--today` (aro interior `accent-400` + texto `accent-300`), `.calendar__day--selected` (fondo `accent-400`, texto `--text-on-accent`, peso 600 — no depende solo del color), `.calendar__day--outside` (días del mes vecino, `--text-muted`) y `[disabled]` (fuera de rango).
|
|
606
|
+
- **Accesibilidad:** `.calendar__table` es `role="grid"` (con `aria-labelledby` al título del mes) y envuelve la fila de cabeceras `.calendar__weekdays` (`role="row"`) y `.calendar__grid` (`role="rowgroup"`) con filas `.calendar__week` (`role="row"`, con `display: contents` para no romper el grid); el input lleva `aria-haspopup="dialog"` + `aria-controls`, sin `aria-expanded` (no es válido en un `input` de texto); los encabezados de día son `role="columnheader"` con `aria-label` del nombre completo (lunes…); cada día es un `<button role="gridcell">` con `aria-label` de la fecha completa ("10 de julio de 2026") y `aria-selected`. El día de hoy lleva `aria-current="date"`.
|
|
556
607
|
- **Teclado:** el input abre el calendario (clic) y mueve el foco al día activo; las flechas mueven el día enfocado (±1 día, ±7 una semana) cruzando meses, Inicio/Fin saltan a los extremos de la semana, Enter/Espacio eligen y Esc cierra devolviendo el foco al input. Roving tabindex: solo el día activo es tabulable.
|
|
557
608
|
|
|
558
609
|
### 10.33 FileUpload
|
|
@@ -756,7 +807,7 @@ Ratios calculados con la fórmula WCAG 2.x (no estimados). El test `mcp/test/con
|
|
|
756
807
|
/* == Aliases semánticos (los componentes consumen SOLO esto) == */
|
|
757
808
|
--bg: var(--night-950);
|
|
758
809
|
--surface-1: var(--night-900); --surface-2: var(--night-800); --surface-3: var(--night-700);
|
|
759
|
-
--border: var(--night-500); --border-strong: var(--night-
|
|
810
|
+
--border: var(--night-500); --border-strong: var(--night-400);
|
|
760
811
|
--text-primary: var(--night-100); --text-secondary: var(--night-200);
|
|
761
812
|
--text-muted: var(--night-300); --text-disabled: var(--night-400);
|
|
762
813
|
--text-heading: var(--night-50); --text-on-accent: var(--night-950);
|
|
@@ -792,7 +843,7 @@ Ratios calculados con la fórmula WCAG 2.x (no estimados). El test `mcp/test/con
|
|
|
792
843
|
--radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px;
|
|
793
844
|
--radius-xl: 24px; --radius-full: 9999px;
|
|
794
845
|
--border-default: 1px solid var(--night-500);
|
|
795
|
-
--border-emphasis: 1px solid var(--night-
|
|
846
|
+
--border-emphasis: 1px solid var(--night-400);
|
|
796
847
|
--focus-ring: 2px solid var(--accent-400);
|
|
797
848
|
--focus-ring-offset: 2px;
|
|
798
849
|
|
|
@@ -859,7 +910,7 @@ El sistema es **dark-first**, pero todo producto debe soportar light mode con lo
|
|
|
859
910
|
| `--surface-2` | `#14171f` | `#ffffff` |
|
|
860
911
|
| `--surface-3` | `#1e222d` | `#eaf1f7` |
|
|
861
912
|
| `--border` | `#3d4354` | `#d8dfe8` |
|
|
862
|
-
| `--border-strong` | `#
|
|
913
|
+
| `--border-strong` | `#6b7183` (`night-400`) | `#6b7183` (`night-400`, mismo valor) |
|
|
863
914
|
| `--text-primary` | `#e8eaef` | `#14171f` |
|
|
864
915
|
| `--text-secondary` | `#c5c9d4` | `#3d4354` |
|
|
865
916
|
| `--text-muted` | `#9aa0b0` | `#5f6575` (`night-450`) |
|
|
@@ -951,7 +1002,7 @@ Ratios calculados con la fórmula WCAG 2.x, no estimados a ojo. `mcp/test/contra
|
|
|
951
1002
|
--surface-2: #ffffff;
|
|
952
1003
|
--surface-3: #eaf1f7;
|
|
953
1004
|
--border: #d8dfe8;
|
|
954
|
-
--border-strong:
|
|
1005
|
+
/* --border-strong no se redefine: night-400 (:root) cumple 1.4.11 en los dos temas */
|
|
955
1006
|
--text-primary: #14171f;
|
|
956
1007
|
--text-secondary: #3d4354;
|
|
957
1008
|
--text-muted: #5f6575; /* night-450 — night-400 caía a 4.27:1 sobre --surface-3 */
|
|
@@ -41,7 +41,10 @@
|
|
|
41
41
|
--surface-2: var(--night-800);
|
|
42
42
|
--surface-3: var(--night-700);
|
|
43
43
|
--border: var(--night-500);
|
|
44
|
-
|
|
44
|
+
/* night-400, no night-600: en dark, oscurecer un borde lo DEBILITA (principio 2:
|
|
45
|
+
la elevación se comunica aclarando). n600 daba 1.46:1 vs los 1.97:1 del borde
|
|
46
|
+
normal — el énfasis se veía más flojo. n400 = 3.99:1 sobre surface-1. */
|
|
47
|
+
--border-strong: var(--night-400);
|
|
45
48
|
--text-primary: var(--night-100);
|
|
46
49
|
--text-secondary: var(--night-200);
|
|
47
50
|
--text-muted: var(--night-300);
|
|
@@ -112,7 +115,7 @@
|
|
|
112
115
|
--radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px;
|
|
113
116
|
--radius-xl: 24px; --radius-full: 9999px;
|
|
114
117
|
--border-default: 1px solid var(--night-500);
|
|
115
|
-
--border-emphasis: 1px solid var(--night-
|
|
118
|
+
--border-emphasis: 1px solid var(--night-400); /* ver --border-strong */
|
|
116
119
|
--focus-ring: 2px solid var(--accent-400);
|
|
117
120
|
--focus-ring-offset: 2px;
|
|
118
121
|
|
|
@@ -145,7 +148,9 @@
|
|
|
145
148
|
--surface-2: #ffffff;
|
|
146
149
|
--surface-3: #eaf1f7;
|
|
147
150
|
--border: #d8dfe8;
|
|
148
|
-
|
|
151
|
+
/* border-strong NO se redefine en claro: night-400 (:root) cumple 1.4.11 en
|
|
152
|
+
AMBOS temas (3.26-3.99:1 en dark, 4.27-4.87:1 en light). #b9c3d1 daba
|
|
153
|
+
1.53-1.78:1, bajo el mínimo de 3:1 para UI. */
|
|
149
154
|
--text-primary: #14171f;
|
|
150
155
|
--text-secondary: #3d4354;
|
|
151
156
|
--text-muted: #5f6575; /* night-400 caía a 4.27:1 sobre --surface-3 */
|
|
@@ -416,7 +421,7 @@ a:hover { color: var(--link-hover); }
|
|
|
416
421
|
.input-wrap .input { padding-left: 40px; }
|
|
417
422
|
|
|
418
423
|
.textarea { min-height: 96px; padding: 12px 14px; resize: vertical; line-height: var(--leading-body); }
|
|
419
|
-
.field__count { font-size: var(--text-xs); color: var(--text-
|
|
424
|
+
.field__count { font-size: var(--text-xs); color: var(--text-muted); text-align: right; margin-top: 4px; }
|
|
420
425
|
|
|
421
426
|
/* Select nativo estilizado (el menú custom usa .menu) */
|
|
422
427
|
.select-wrap { position: relative; }
|
|
@@ -657,6 +662,32 @@ a:hover { color: var(--link-hover); }
|
|
|
657
662
|
.stat__delta--down { color: var(--danger); }
|
|
658
663
|
.stat__delta--neutral { color: var(--text-muted); }
|
|
659
664
|
|
|
665
|
+
/* Chip de métrica: rótulo y valor en una pastilla de una línea */
|
|
666
|
+
.metric-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
|
|
667
|
+
.metric-chip {
|
|
668
|
+
display: inline-flex; align-items: center; gap: var(--space-2);
|
|
669
|
+
padding: var(--space-1) var(--space-3);
|
|
670
|
+
border: 1px solid var(--border); border-radius: var(--radius-full);
|
|
671
|
+
background: var(--surface-1);
|
|
672
|
+
font-family: var(--font-body); font-size: var(--text-sm); line-height: var(--leading-snug);
|
|
673
|
+
color: var(--text-secondary); white-space: nowrap;
|
|
674
|
+
}
|
|
675
|
+
.metric-chip svg { width: 16px; height: 16px; flex: none; color: var(--text-muted); }
|
|
676
|
+
.metric-chip__value {
|
|
677
|
+
font-family: var(--font-mono); font-weight: 500; color: var(--link);
|
|
678
|
+
font-variant-numeric: tabular-nums;
|
|
679
|
+
}
|
|
680
|
+
button.metric-chip {
|
|
681
|
+
min-height: var(--touch-target); padding-inline: var(--space-4);
|
|
682
|
+
cursor: pointer; text-align: left;
|
|
683
|
+
transition: background-color var(--duration-fast) var(--ease-out),
|
|
684
|
+
border-color var(--duration-fast) var(--ease-out),
|
|
685
|
+
transform var(--duration-instant) var(--ease-out);
|
|
686
|
+
}
|
|
687
|
+
button.metric-chip:hover { background: var(--outline-hover-bg); border-color: var(--border-hover); }
|
|
688
|
+
button.metric-chip:active { background: var(--outline-active-bg); transform: scale(.98); }
|
|
689
|
+
button.metric-chip:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); }
|
|
690
|
+
|
|
660
691
|
/* ============================================================
|
|
661
692
|
13B. PAGINATION — .pagination (activa: fondo acento + peso, no solo color)
|
|
662
693
|
============================================================ */
|
|
@@ -739,7 +770,7 @@ a:hover { color: var(--link-hover); }
|
|
|
739
770
|
}
|
|
740
771
|
.alert > svg { width: 20px; height: 20px; flex: none; margin-top: 1px; }
|
|
741
772
|
.alert__title { font-weight: 600; margin: 0 0 2px; color: inherit; }
|
|
742
|
-
.alert__body { margin: 0; color: inherit;
|
|
773
|
+
.alert__body { margin: 0; color: inherit; }
|
|
743
774
|
.alert__close {
|
|
744
775
|
margin-left: auto; flex: none; display: grid; place-items: center;
|
|
745
776
|
width: 36px; height: 36px; border: 0; border-radius: var(--radius-sm);
|
|
@@ -901,6 +932,70 @@ a:hover { color: var(--link-hover); }
|
|
|
901
932
|
.sidebar__item svg { width: 18px; height: 18px; color: var(--text-muted); flex: none; }
|
|
902
933
|
.sidebar__item.is-active svg { color: var(--sidebar-active-icon); }
|
|
903
934
|
|
|
935
|
+
/* Grupos con rótulo dentro del sidebar */
|
|
936
|
+
.sidebar__group { display: flex; flex-direction: column; gap: 2px; }
|
|
937
|
+
.sidebar__group + .sidebar__group { margin-top: var(--space-4); }
|
|
938
|
+
.sidebar__group-label {
|
|
939
|
+
padding: 0 12px var(--space-1);
|
|
940
|
+
font-size: var(--text-xs); font-weight: 500; text-transform: uppercase;
|
|
941
|
+
letter-spacing: var(--tracking-overline); color: var(--text-muted); white-space: nowrap;
|
|
942
|
+
}
|
|
943
|
+
.sidebar__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
944
|
+
|
|
945
|
+
/* Sidebar contraíble: expandido 260 px, riel de iconos de 64 px.
|
|
946
|
+
El ancho cambia sin transición; solo los rótulos se funden con opacity y transform. */
|
|
947
|
+
.sidebar--collapsible .sidebar__label {
|
|
948
|
+
transition: opacity var(--duration-fast) var(--ease-out),
|
|
949
|
+
transform var(--duration-fast) var(--ease-out);
|
|
950
|
+
}
|
|
951
|
+
.sidebar__toggle {
|
|
952
|
+
margin-top: auto; width: 100%; border: 0; background: transparent;
|
|
953
|
+
font-family: var(--font-body); text-align: left; cursor: pointer;
|
|
954
|
+
}
|
|
955
|
+
.sidebar.is-collapsed { width: 64px; }
|
|
956
|
+
.sidebar.is-collapsed .sidebar__item { position: relative; justify-content: center; padding-inline: 0; }
|
|
957
|
+
.sidebar.is-collapsed .sidebar__toggle svg { transform: rotate(180deg); }
|
|
958
|
+
.sidebar.is-collapsed .sidebar__group + .sidebar__group { margin-top: var(--space-2); }
|
|
959
|
+
/* En el riel, el rótulo de grupo es un separador de 1 px; su texto sigue en el árbol de accesibilidad */
|
|
960
|
+
.sidebar.is-collapsed .sidebar__group-label {
|
|
961
|
+
height: 1px; padding: 0; margin: 0 var(--space-2) var(--space-2);
|
|
962
|
+
overflow: hidden; color: transparent; background: var(--border);
|
|
963
|
+
}
|
|
964
|
+
/* Oculto a la vista pero accesible (patrón de .visually-hidden) */
|
|
965
|
+
.sidebar.is-collapsed .sidebar__label,
|
|
966
|
+
.sidebar.is-collapsed .sidebar__group:first-child > .sidebar__group-label {
|
|
967
|
+
position: absolute; width: 1px; height: 1px; margin: -1px;
|
|
968
|
+
clip-path: inset(50%); overflow: hidden; white-space: nowrap;
|
|
969
|
+
}
|
|
970
|
+
.sidebar.is-collapsed .sidebar__label {
|
|
971
|
+
background: var(--tooltip-bg); border: 1px solid var(--tooltip-border); border-radius: var(--radius-sm);
|
|
972
|
+
color: var(--tooltip-text); font-size: var(--text-xs); line-height: 16px;
|
|
973
|
+
pointer-events: none; z-index: 70;
|
|
974
|
+
opacity: 0; transform: translateX(-4px);
|
|
975
|
+
}
|
|
976
|
+
/* Tooltip del riel: el propio rótulo aparece a la derecha del ítem, también con teclado */
|
|
977
|
+
.sidebar.is-collapsed .sidebar__item:focus-visible .sidebar__label {
|
|
978
|
+
left: calc(100% + 8px); top: calc(50% - 15px); width: auto; height: auto;
|
|
979
|
+
margin: 0; padding: 6px 10px; clip-path: none; overflow: visible;
|
|
980
|
+
opacity: 1; transform: none; transition-delay: 400ms;
|
|
981
|
+
}
|
|
982
|
+
/* Por hover solo con puntero real — en touch quedaría pegado tras el tap */
|
|
983
|
+
@media (hover: hover) and (pointer: fine) {
|
|
984
|
+
.sidebar.is-collapsed .sidebar__item:hover .sidebar__label {
|
|
985
|
+
left: calc(100% + 8px); top: calc(50% - 15px); width: auto; height: auto;
|
|
986
|
+
margin: 0; padding: 6px 10px; clip-path: none; overflow: visible;
|
|
987
|
+
opacity: 1; transform: none; transition-delay: 400ms;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
/* Por debajo de 768 px la navegación vive en un Drawer --left, que abre el botón ☰
|
|
991
|
+
.sidebar-trigger (combinado con .icon-btn); desde 768 px el sidebar es fijo y el botón sobra */
|
|
992
|
+
@media (max-width: 767px) {
|
|
993
|
+
.sidebar--collapsible { display: none; }
|
|
994
|
+
}
|
|
995
|
+
@media (min-width: 768px) {
|
|
996
|
+
.sidebar-trigger { display: none; }
|
|
997
|
+
}
|
|
998
|
+
|
|
904
999
|
.tabs { position: relative; display: flex; gap: var(--space-2); border-bottom: 1px solid var(--border-strong); }
|
|
905
1000
|
.tabs__btn {
|
|
906
1001
|
padding: 10px 14px; border: 0; background: transparent; cursor: pointer;
|
|
@@ -1208,7 +1303,7 @@ a:hover { color: var(--link-hover); }
|
|
|
1208
1303
|
.calendar__day--today { border-color: var(--accent-400); color: var(--accent-300); }
|
|
1209
1304
|
/* Seleccionado: fondo de acento + texto oscuro + peso — no depende solo del color */
|
|
1210
1305
|
.calendar__day--selected { background: var(--accent-400); border-color: var(--accent-400); color: var(--text-on-accent); font-weight: 600; }
|
|
1211
|
-
.calendar__day--outside { color: var(--text-
|
|
1306
|
+
.calendar__day--outside { color: var(--text-muted); }
|
|
1212
1307
|
.calendar__day[disabled] { color: var(--text-disabled); opacity: .5; cursor: not-allowed; }
|
|
1213
1308
|
|
|
1214
1309
|
/* ============================================================
|
|
@@ -1227,7 +1322,7 @@ a:hover { color: var(--link-hover); }
|
|
|
1227
1322
|
}
|
|
1228
1323
|
.file-upload__label > svg { width: 28px; height: 28px; color: var(--text-muted); }
|
|
1229
1324
|
.file-upload__button { color: var(--link); font-weight: 600; }
|
|
1230
|
-
.file-upload__hint { font-size: var(--text-xs); color: var(--text-
|
|
1325
|
+
.file-upload__hint { font-size: var(--text-xs); color: var(--text-muted); }
|
|
1231
1326
|
@media (hover: hover) and (pointer: fine) {
|
|
1232
1327
|
.file-upload__label:hover { border-color: var(--border-hover); background: var(--surface-2); color: var(--text-secondary); }
|
|
1233
1328
|
}
|