orbital-ds 0.0.0-stage → 0.1.1

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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -2
  3. package/dist/hooks/use-mobile.d.ts +3 -0
  4. package/dist/hooks/use-mobile.js +20 -0
  5. package/dist/lib/utils.d.ts +5 -0
  6. package/dist/lib/utils.js +8 -0
  7. package/dist/styles.css +347 -0
  8. package/dist/themes/qratia.css +247 -0
  9. package/dist/ui/avatar.d.ts +13 -0
  10. package/dist/ui/avatar.js +103 -0
  11. package/dist/ui/ayuda.d.ts +22 -0
  12. package/dist/ui/ayuda.js +24 -0
  13. package/dist/ui/badge.d.ts +11 -0
  14. package/dist/ui/badge.js +44 -0
  15. package/dist/ui/breadcrumb.d.ts +12 -0
  16. package/dist/ui/breadcrumb.js +119 -0
  17. package/dist/ui/button.d.ts +12 -0
  18. package/dist/ui/button.js +56 -0
  19. package/dist/ui/card.d.ts +13 -0
  20. package/dist/ui/card.js +98 -0
  21. package/dist/ui/chart.d.ts +46 -0
  22. package/dist/ui/chart.js +250 -0
  23. package/dist/ui/checkbox.d.ts +6 -0
  24. package/dist/ui/checkbox.js +29 -0
  25. package/dist/ui/collapsible.d.ts +8 -0
  26. package/dist/ui/collapsible.js +16 -0
  27. package/dist/ui/dialog.d.ts +19 -0
  28. package/dist/ui/dialog.js +145 -0
  29. package/dist/ui/dropdown-menu.d.ts +31 -0
  30. package/dist/ui/dropdown-menu.js +258 -0
  31. package/dist/ui/input.d.ts +5 -0
  32. package/dist/ui/input.js +19 -0
  33. package/dist/ui/label.d.ts +5 -0
  34. package/dist/ui/label.js +19 -0
  35. package/dist/ui/popover.d.ts +12 -0
  36. package/dist/ui/popover.js +83 -0
  37. package/dist/ui/progress.d.ts +6 -0
  38. package/dist/ui/progress.js +30 -0
  39. package/dist/ui/scroll-area.d.ts +7 -0
  40. package/dist/ui/scroll-area.js +59 -0
  41. package/dist/ui/select.d.ts +19 -0
  42. package/dist/ui/select.js +191 -0
  43. package/dist/ui/separator.d.ts +6 -0
  44. package/dist/ui/separator.js +25 -0
  45. package/dist/ui/sheet.d.ts +16 -0
  46. package/dist/ui/sheet.js +128 -0
  47. package/dist/ui/sidebar.d.ts +68 -0
  48. package/dist/ui/sidebar.js +646 -0
  49. package/dist/ui/skeleton.d.ts +5 -0
  50. package/dist/ui/skeleton.js +15 -0
  51. package/dist/ui/sonner.d.ts +6 -0
  52. package/dist/ui/sonner.js +43 -0
  53. package/dist/ui/table.d.ts +12 -0
  54. package/dist/ui/table.js +114 -0
  55. package/dist/ui/tabs.d.ts +14 -0
  56. package/dist/ui/tabs.js +85 -0
  57. package/dist/ui/textarea.d.ts +11 -0
  58. package/dist/ui/textarea.js +18 -0
  59. package/dist/ui/tooltip.d.ts +9 -0
  60. package/dist/ui/tooltip.js +63 -0
  61. package/package.json +93 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fractalia Systems
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,50 @@
1
- # Temporary Holding Version
1
+ # orbital-ds
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Sistema de diseño genérico. **Estructura común + temas intercambiables + primitivos**
4
+ sobre [Base UI](https://base-ui.com) y Tailwind v4.
5
+
6
+ - `styles.css` — la ESTRUCTURA: mapea variables de tema a utilidades de Tailwind y define los
7
+ tokens semánticos (radios, alturas de control, sombras, capas, movimiento, foco) y la capa base.
8
+ No lleva colores ni fuentes.
9
+ - `themes/<tema>.css` — un TEMA: paleta (claro/oscuro), radio base y tipografía.
10
+ Hoy: `qratia`. Añadir otro = añadir otro fichero.
11
+ - `ui/*` — primitivos (`orbital-ds/ui/button`, …), uno por fichero: quien no usa gráficos no
12
+ necesita `recharts`, quien no usa toasts no necesita `sonner`.
13
+
14
+ ## Uso en una app (Tailwind v4)
15
+
16
+ ```css
17
+ @import 'tailwindcss';
18
+ @plugin '@tailwindcss/typography';
19
+ @import 'tw-animate-css';
20
+ @import 'orbital-ds/themes/qratia.css'; /* un tema */
21
+ @import 'orbital-ds/styles.css'; /* la estructura */
22
+ @source '../node_modules/orbital-ds/dist'; /* que Tailwind vea las clases del paquete */
23
+ ```
24
+
25
+ ```tsx
26
+ import { Button } from 'orbital-ds/ui/button';
27
+ ```
28
+
29
+ ## Regla
30
+
31
+ Ningún componente lleva valores propios (colores, medidas, sombras, z-index, duraciones):
32
+ pide un hueco a una variable. Para cambiar el aspecto se cambia el tema, no el componente.
33
+ Si un componente necesita un hueco nuevo, se AÑADE el token con un valor que deje el aspecto
34
+ como estaba.
35
+
36
+ ## Desarrollo
37
+
38
+ ```bash
39
+ pnpm install
40
+ pnpm verify # typecheck + build
41
+ pnpm dev # recompila al guardar (para consumirlo con `link:` desde una app)
42
+ ```
43
+
44
+ Peers obligatorios: `react`, `react-dom`, `@base-ui/react`, `tailwindcss`, `tailwind-merge`,
45
+ `class-variance-authority`, `clsx`, `lucide-react`, `tw-animate-css`.
46
+ Opcionales (solo si usas ese primitivo): `recharts` (chart), `sonner` (sonner), `next-themes` (sonner).
47
+
48
+ ## Licencia
49
+
50
+ MIT © Fractalia Systems
@@ -0,0 +1,3 @@
1
+ declare function useIsMobile(): boolean;
2
+
3
+ export { useIsMobile };
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ const MOBILE_BREAKPOINT = 768;
3
+ function useIsMobile() {
4
+ const [isMobile, setIsMobile] = React.useState(
5
+ void 0
6
+ );
7
+ React.useEffect(() => {
8
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
9
+ const onChange = () => {
10
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
11
+ };
12
+ mql.addEventListener("change", onChange);
13
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
14
+ return () => mql.removeEventListener("change", onChange);
15
+ }, []);
16
+ return !!isMobile;
17
+ }
18
+ export {
19
+ useIsMobile
20
+ };
@@ -0,0 +1,5 @@
1
+ import { ClassValue } from 'clsx';
2
+
3
+ declare function cn(...inputs: ClassValue[]): string;
4
+
5
+ export { cn };
@@ -0,0 +1,8 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
6
+ export {
7
+ cn
8
+ };
@@ -0,0 +1,347 @@
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
+ --control-h-sm: 2rem;
64
+ --control-h-md: 2.25rem;
65
+ --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
+ --z-raised: 10;
84
+ --z-sticky: 20;
85
+ --z-overlay: 50;
86
+
87
+ /* MOVIMIENTO — dos duraciones; `reduced-motion` ya se respeta en la capa base. */
88
+ --motion-fast: 100ms;
89
+ --motion-base: 200ms;
90
+
91
+ /* FOCO — grosor del anillo de foco/error de todos los controles. */
92
+ --ring-width: 3px;
93
+
94
+ /* Velo tras diálogos y paneles. Un tema puede cambiar su tinte u opacidad. */
95
+ --color-scrim: oklch(0 0 0 / 0.8);
96
+
97
+ /*
98
+ * Dos sombras y no más: reposo y realce al señalar. Una tarjeta que no se
99
+ * puede pulsar no necesita la segunda.
100
+ *
101
+ * EN CAPAS y TINTADAS CON LA TINTA del tema (no negro): una capa tensa
102
+ * pegada a la pieza + una suave más lejana — es lo que hace que la tarjeta
103
+ * se despegue del lienzo de forma natural y armónica (patrón validado:
104
+ * joshwcomeau.com/css/designing-shadows).
105
+ */
106
+ --shadow-card:
107
+ 0 1px 2px oklch(0.186 0.048 269 / 0.08),
108
+ 0 3px 10px oklch(0.186 0.048 269 / 0.07);
109
+ --shadow-card-hover:
110
+ 0 3px 6px oklch(0.186 0.048 269 / 0.1),
111
+ 0 14px 32px oklch(0.186 0.048 269 / 0.14);
112
+
113
+ --color-background: var(--background);
114
+ --color-foreground: var(--foreground);
115
+ --color-card: var(--card);
116
+ --color-card-foreground: var(--card-foreground);
117
+ --color-inset: var(--inset);
118
+ --color-inset-foreground: var(--inset-foreground);
119
+ --color-popover: var(--popover);
120
+ --color-popover-foreground: var(--popover-foreground);
121
+ --color-primary: var(--primary);
122
+ --color-primary-foreground: var(--primary-foreground);
123
+ /* Texto acentuado: SIEMPRE `text-link`, nunca el cian de marca — no alcanza
124
+ * contraste de texto sobre las superficies claras. */
125
+ --color-link: var(--link);
126
+ --color-link-hover: var(--link-hover);
127
+ --color-brand: var(--brand);
128
+ --color-brand-bright: var(--brand-bright);
129
+ --color-secondary: var(--secondary);
130
+ --color-secondary-foreground: var(--secondary-foreground);
131
+ --color-muted: var(--muted);
132
+ --color-muted-foreground: var(--muted-foreground);
133
+ --color-accent: var(--accent);
134
+ --color-accent-foreground: var(--accent-foreground);
135
+ --color-destructive: var(--destructive);
136
+ --color-destructive-foreground: var(--destructive-foreground);
137
+ --color-border: var(--border);
138
+ --color-input: var(--input);
139
+ --color-ring: var(--ring);
140
+
141
+ --color-chart-1: var(--chart-1);
142
+ --color-chart-2: var(--chart-2);
143
+ --color-chart-3: var(--chart-3);
144
+ --color-chart-4: var(--chart-4);
145
+ --color-chart-5: var(--chart-5);
146
+
147
+ /*
148
+ * Estado. Se exponen como colores del tema para que una etiqueta de severidad
149
+ * se escriba `bg-danger-soft text-danger` y nadie tenga que
150
+ * volver a elegir un rojo a mano.
151
+ */
152
+ --color-danger: var(--danger);
153
+ --color-danger-soft: var(--danger-soft);
154
+ --color-warning: var(--warning);
155
+ --color-warning-soft: var(--warning-soft);
156
+ --color-success: var(--success);
157
+ --color-success-soft: var(--success-soft);
158
+ --color-neutral: var(--neutral);
159
+ --color-neutral-soft: var(--neutral-soft);
160
+ --color-info: var(--info);
161
+ --color-info-soft: var(--info-soft);
162
+
163
+ --color-sidebar: var(--sidebar);
164
+ --color-sidebar-foreground: var(--sidebar-foreground);
165
+ --color-sidebar-primary: var(--sidebar-primary);
166
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
167
+ --color-sidebar-accent: var(--sidebar-accent);
168
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
169
+ --color-sidebar-border: var(--sidebar-border);
170
+ --color-sidebar-ring: var(--sidebar-ring);
171
+ }
172
+
173
+ @layer base {
174
+ * {
175
+ @apply border-border outline-ring/50;
176
+ }
177
+
178
+ html,
179
+ body,
180
+ #app {
181
+ min-height: 100%;
182
+ }
183
+
184
+ body {
185
+ @apply bg-background text-foreground font-sans m-0 antialiased;
186
+ }
187
+
188
+ a {
189
+ @apply text-link hover:text-link-hover;
190
+ }
191
+
192
+ /*
193
+ * EXCEPCIÓN: los enlaces del CHROME no son enlaces de texto — son destinos de
194
+ * navegación. Deben heredar el color de su superficie (blanco en la barra de
195
+ * marca, tinta en un menú), no el azul de enlace, que rompería el diseño.
196
+ */
197
+ [data-slot='sidebar'] a,
198
+ [data-sidebar='menu-button'],
199
+ [role='menuitem'],
200
+ [role='menuitemradio'],
201
+ [role='option'] {
202
+ color: inherit;
203
+ }
204
+ [data-slot='sidebar'] a:hover,
205
+ [data-sidebar='menu-button']:hover {
206
+ color: inherit;
207
+ }
208
+
209
+ ::selection {
210
+ background: color-mix(in oklch, var(--brand) 25%, transparent);
211
+ }
212
+
213
+ /*
214
+ * TARJETA según el design system: radio `xl`, borde de 1px y sombra de reposo.
215
+ * `components/ui/card.tsx` ya lleva estos valores; esta regla los GARANTIZA
216
+ * aunque alguien regenere el primitivo con la CLI de shadcn (que los pisa).
217
+ */
218
+ [data-slot='card'] {
219
+ @apply rounded-xl shadow-card ring-0;
220
+ border: 1px solid var(--border);
221
+ }
222
+
223
+ /*
224
+ * Estados de interacción, resueltos UNA vez para toda la aplicación
225
+ * (spec `sistema-de-componentes`). Ninguna feature debería volver a declarar
226
+ * cursor ni anillo de foco.
227
+ *
228
+ * La lista de roles cubre lo que Base UI renderiza sin <button>: opciones de
229
+ * menú y select, pestañas, switches, checkboxes y radios — con hover pero sin
230
+ * pointer quedarían «muertos» al señalar.
231
+ */
232
+ button:not(:disabled),
233
+ select:not(:disabled),
234
+ [role='button']:not([aria-disabled='true'], [data-disabled]),
235
+ [role='menuitem']:not([aria-disabled='true'], [data-disabled]),
236
+ [role='menuitemcheckbox']:not([aria-disabled='true'], [data-disabled]),
237
+ [role='menuitemradio']:not([aria-disabled='true'], [data-disabled]),
238
+ [role='option']:not([aria-disabled='true'], [data-disabled]),
239
+ [role='tab']:not([aria-disabled='true'], [data-disabled]),
240
+ [role='switch']:not([aria-disabled='true'], [data-disabled]),
241
+ [role='checkbox']:not([aria-disabled='true'], [data-disabled]),
242
+ [role='radio']:not([aria-disabled='true'], [data-disabled]),
243
+ [role='combobox']:not([aria-disabled='true'], [data-disabled]),
244
+ [role='slider']:not([aria-disabled='true'], [data-disabled]),
245
+ input[type='checkbox']:not(:disabled),
246
+ input[type='radio']:not(:disabled),
247
+ input[type='range']:not(:disabled),
248
+ a[href],
249
+ label[for],
250
+ summary {
251
+ @apply cursor-pointer;
252
+ }
253
+
254
+ button:disabled,
255
+ select:disabled,
256
+ input:disabled,
257
+ [aria-disabled='true'],
258
+ [data-disabled] {
259
+ @apply cursor-not-allowed;
260
+ }
261
+
262
+ /*
263
+ * Los primitivos de menú/select traen `cursor-default` como UTILIDAD, y las
264
+ * utilidades ganan a la capa base por orden de cascada — por eso una opción
265
+ * de idioma quedaba sin pointer aunque la regla de arriba la cubriera. Como
266
+ * los primitivos no se editan a mano, aquí el pointer se IMPONE para los
267
+ * roles interactivos de esos popups.
268
+ */
269
+ [role='menuitem']:not([aria-disabled='true'], [data-disabled]),
270
+ [role='menuitemcheckbox']:not([aria-disabled='true'], [data-disabled]),
271
+ [role='menuitemradio']:not([aria-disabled='true'], [data-disabled]),
272
+ [role='option']:not([aria-disabled='true'], [data-disabled]) {
273
+ cursor: pointer !important;
274
+ }
275
+
276
+ [aria-busy='true'] {
277
+ @apply cursor-progress;
278
+ }
279
+
280
+ /* Foco visible solo en navegación por teclado, nunca al hacer clic. */
281
+ :focus-visible {
282
+ @apply outline-ring outline-2 outline-offset-2;
283
+ }
284
+
285
+ /*
286
+ * SIDEBAR FLOTANTE según el DS (Identity Hub): la barra va despegada del
287
+ * borde, con el radio de vista (2xl ≈ 20px) y la sombra de realce — el efecto
288
+ * flotante es lo que le da el aire moderno. El primitivo trae `rounded-lg` y
289
+ * `shadow-sm` en su variante floating; se corrige AQUÍ (capa base), no en el
290
+ * componente, igual que la tarjeta.
291
+ */
292
+ [data-variant='floating'] [data-slot='sidebar-inner'] {
293
+ border-radius: var(--radius-2xl);
294
+ box-shadow: var(--shadow-card-hover);
295
+ }
296
+
297
+ /*
298
+ * TAMAÑO Y ALINEACIÓN DE LA NAVEGACIÓN — ajuste del DS sobre el primitivo
299
+ * (capa base, no el componente; los `!important` baten a las utilidades que
300
+ * el primitivo trae escritas).
301
+ *
302
+ * EXPANDIDO: filas de 40px con icono de 18px — las medidas del diseño
303
+ * («Revisión de llamada»), más presentes que las del shadcn por defecto.
304
+ *
305
+ * COLAPSADO: el primitivo amplió el raíl a 64px (`--sidebar-width-icon: 4rem`)
306
+ * pero conservó botones de 32px y `p-2` (8px) pensados para el raíl de 48px
307
+ * del shadcn original: quedaban pegados a la izquierda con 24px muertos.
308
+ * Botón a 40px y padding lateral a (64−40)/2 = 12px → centrado.
309
+ */
310
+ [data-sidebar='menu-button'] {
311
+ min-height: 2.5rem;
312
+ }
313
+ [data-sidebar='menu-button'] svg {
314
+ width: 1.125rem !important;
315
+ height: 1.125rem !important;
316
+ }
317
+
318
+ [data-collapsible='icon'] [data-slot='sidebar-header'],
319
+ [data-collapsible='icon'] [data-slot='sidebar-footer'],
320
+ [data-collapsible='icon'] [data-slot='sidebar-group'] {
321
+ padding-inline: 0.75rem;
322
+ }
323
+ [data-collapsible='icon'] [data-sidebar='menu-button'] {
324
+ width: 2.5rem !important;
325
+ height: 2.5rem !important;
326
+ padding: 0.6875rem !important; /* (40−18)/2: icono centrado */
327
+ }
328
+ /* Botones lg (marca, cuenta): dentro va una ficha de 32px → 4px de aire. */
329
+ [data-collapsible='icon'] [data-sidebar='menu-button'][data-size='lg'] {
330
+ padding: 0.25rem !important;
331
+ }
332
+
333
+ /*
334
+ * `prefers-reduced-motion` no es un detalle de cortesía: en un chrome que se
335
+ * pliega a diario, la animación de anchura es justo el movimiento que molesta
336
+ * a quien pide menos movimiento.
337
+ */
338
+ @media (prefers-reduced-motion: reduce) {
339
+ *,
340
+ *::before,
341
+ *::after {
342
+ transition-duration: 0.01ms !important;
343
+ animation-duration: 0.01ms !important;
344
+ animation-iteration-count: 1 !important;
345
+ }
346
+ }
347
+ }