orbital-ds 0.8.0 → 0.9.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/AGENTS.md +145 -80
- package/components.manifest.json +1326 -1267
- package/contract.json +226 -168
- package/dist/hooks/use-ds-messages.d.ts +36 -0
- package/dist/hooks/use-ds-messages.js +12 -0
- package/dist/hooks/use-ds-motion.d.ts +17 -0
- package/dist/hooks/use-ds-motion.js +50 -0
- package/dist/lib/i18n.d.ts +46 -0
- package/dist/lib/i18n.js +109 -0
- package/dist/lib/motion.d.ts +74 -0
- package/dist/lib/motion.js +97 -0
- package/dist/styles.css +876 -528
- package/dist/themes/friendly.css +362 -318
- package/dist/themes/friendly.meta.json +75 -14
- package/dist/themes/friendly.scoped.css +362 -318
- package/dist/themes/qratia.meta.json +42 -12
- package/dist/themes/tactile.css +575 -430
- package/dist/themes/tactile.fonts.css +3 -2
- package/dist/themes/tactile.guide.md +82 -49
- package/dist/themes/tactile.meta.json +175 -82
- package/dist/themes/tactile.scoped.css +575 -430
- package/dist/themes/twitter.css +354 -298
- package/dist/themes/twitter.meta.json +83 -61
- package/dist/themes/twitter.scoped.css +354 -298
- package/dist/themes/zyro.css +443 -389
- package/dist/themes/zyro.meta.json +67 -14
- package/dist/themes/zyro.scoped.css +443 -389
- package/dist/ui/accordion.js +5 -5
- package/dist/ui/alert-dialog.js +3 -3
- package/dist/ui/alert.d.ts +1 -1
- package/dist/ui/alert.js +9 -3
- package/dist/ui/attachment.d.ts +1 -1
- package/dist/ui/attachment.js +4 -4
- package/dist/ui/avatar.js +6 -6
- package/dist/ui/ayuda.js +3 -2
- package/dist/ui/badge.d.ts +1 -1
- package/dist/ui/badge.js +9 -3
- package/dist/ui/banner.d.ts +14 -0
- package/dist/ui/banner.js +88 -0
- package/dist/ui/breadcrumb.js +7 -3
- package/dist/ui/bubble.js +6 -6
- package/dist/ui/button-group.js +4 -4
- package/dist/ui/button.d.ts +1 -1
- package/dist/ui/button.js +5 -5
- package/dist/ui/calendar.js +33 -6
- package/dist/ui/card.js +4 -4
- package/dist/ui/carousel.js +15 -8
- package/dist/ui/chart.js +7 -3
- package/dist/ui/checkbox.js +1 -1
- package/dist/ui/combobox.js +7 -7
- package/dist/ui/command.js +13 -11
- package/dist/ui/context-menu.js +8 -8
- package/dist/ui/dialog.js +10 -6
- package/dist/ui/drawer.js +9 -9
- package/dist/ui/dropdown-menu.js +8 -8
- package/dist/ui/empty.js +3 -3
- package/dist/ui/field.js +2 -2
- package/dist/ui/hover-card.js +1 -1
- package/dist/ui/i18n-provider.d.ts +11 -0
- package/dist/ui/i18n-provider.js +18 -0
- package/dist/ui/icon-tile.d.ts +1 -1
- package/dist/ui/input-group.d.ts +1 -1
- package/dist/ui/input-group.js +11 -9
- package/dist/ui/input-otp.js +3 -3
- package/dist/ui/input.js +1 -1
- package/dist/ui/item.d.ts +1 -1
- package/dist/ui/item.js +3 -3
- package/dist/ui/kbd.js +1 -1
- package/dist/ui/label.js +1 -1
- package/dist/ui/marker.d.ts +1 -1
- package/dist/ui/marker.js +2 -2
- package/dist/ui/menubar.js +9 -9
- package/dist/ui/message-scroller.js +5 -2
- package/dist/ui/message.js +2 -2
- package/dist/ui/motion-provider.d.ts +11 -0
- package/dist/ui/motion-provider.js +16 -0
- package/dist/ui/native-select.js +4 -4
- package/dist/ui/navigation-menu.js +7 -6
- package/dist/ui/pagination.js +19 -11
- package/dist/ui/popover.js +2 -2
- package/dist/ui/progress.js +2 -2
- package/dist/ui/questionnaire.js +20 -15
- package/dist/ui/radio-group.js +2 -2
- package/dist/ui/resizable.js +2 -2
- package/dist/ui/scroll-area.js +3 -3
- package/dist/ui/select.js +6 -6
- package/dist/ui/separator.js +1 -1
- package/dist/ui/sheet.js +6 -4
- package/dist/ui/sidebar.js +42 -30
- package/dist/ui/skeleton.js +1 -1
- package/dist/ui/slider.js +6 -6
- package/dist/ui/sonner.js +25 -8
- package/dist/ui/spinner.js +4 -1
- package/dist/ui/switch.js +2 -2
- package/dist/ui/table.js +3 -3
- package/dist/ui/tabs.js +5 -5
- package/dist/ui/textarea.js +1 -1
- package/dist/ui/toast.js +15 -12
- package/dist/ui/toggle-group.js +2 -2
- package/dist/ui/toggle.js +6 -4
- package/dist/ui/tooltip.js +2 -2
- package/docs/CONTRACT.md +183 -153
- package/package.json +160 -142
- package/scripts/brand-core.mjs +153 -0
- package/scripts/doctor.mjs +128 -7
- package/scripts/i18n.mjs +111 -0
- package/scripts/init.mjs +326 -351
- package/scripts/setup.mjs +99 -0
package/AGENTS.md
CHANGED
|
@@ -1,80 +1,145 @@
|
|
|
1
|
-
# orbital-ds — guía para agentes de IA
|
|
2
|
-
|
|
3
|
-
Sistema de diseño: **estructura común + temas + componentes** sobre Base UI y Tailwind v4.
|
|
4
|
-
Todo está pensado para que un agente pueda usarlo, ampliarlo y comprobar su trabajo sin
|
|
5
|
-
adivinar. Lee antes: `README.md`, `docs/CONTRACT.md`, `contract.json` y
|
|
6
|
-
`components.manifest.json` (qué componentes hay, cómo se importan y qué `data-slot` exponen).
|
|
7
|
-
|
|
8
|
-
## Reglas (obligatorias)
|
|
9
|
-
|
|
10
|
-
1. **Solo variables.** Un componente no escribe colores, medidas, sombras, z-index ni
|
|
11
|
-
duraciones: pide un hueco a una variable (`--ds-*` o las de color de shadcn). Si falta el
|
|
12
|
-
hueco, **añade el token** con un valor que deje el aspecto como estaba y úsalo.
|
|
13
|
-
2. **Añadir, nunca sustituir.** Un componente nuevo es un fichero nuevo en `src/ui/`. No se
|
|
14
|
-
reescribe ni se renombra uno existente para otro fin. No uses `--overwrite` de la CLI de
|
|
15
|
-
shadcn sin aprobación expresa. Quitar o renombrar algo público = versión **mayor**.
|
|
16
|
-
3. **Nombres en inglés.** Tokens, props y valores de variantes. Variables propias con el
|
|
17
|
-
prefijo `--ds-`; las que Tailwind convierte en utilidades (`--color-*`, `--radius-*`,
|
|
18
|
-
`--shadow-*`) siguen su convención. Las privadas de un tema llevan el prefijo del tema
|
|
19
|
-
(`--zy-*`).
|
|
20
|
-
4. **Un solo motor: Base UI.** Nada de Radix. `asChild` no existe: se usa `render`.
|
|
21
|
-
5. **«Sin sombra» es `0 0 #0000`**, nunca `none` (invalida todo el `box-shadow`, anillo de
|
|
22
|
-
foco incluido).
|
|
23
|
-
6. **Un tema cumple el contrato completo** (`contract.json`) y define en `:root` todo
|
|
24
|
-
`--ds-*` que defina el tema base.
|
|
25
|
-
7. **La marca de una app vive en SU `brand.css`**, no en el tema del paquete. Para
|
|
26
|
-
personalizar una app (color, radio, fuentes) edita ese fichero; nunca `node_modules` ni
|
|
27
|
-
los temas. `pnpm exec orbital-ds-init` lo crea sin sobrescribir.
|
|
28
|
-
|
|
29
|
-
## Cómo cambiar el aspecto
|
|
30
|
-
|
|
31
|
-
- **Valores** (color, forma, fuentes, escala, sombras, espaciado, materiales, sidebar):
|
|
32
|
-
variables del tema en `src/themes/<tema>.css`.
|
|
33
|
-
- **Personalidad de un elemento** (por ejemplo, botones en píldora con degradado): reglas en
|
|
34
|
-
la sección «PERSONALIDAD» del tema, dirigidas a los ganchos del DOM —`data-slot`,
|
|
35
|
-
`data-variant`, `data-size`, `data-active`, `data-state`— y escritas solo con variables. Van
|
|
36
|
-
**sin capa** para ganar a las utilidades; define también hover y active.
|
|
37
|
-
- **Estructura** distinta de un componente (otro layout): variante del componente, no tema.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
##
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
1
|
+
# orbital-ds — guía para agentes de IA
|
|
2
|
+
|
|
3
|
+
Sistema de diseño: **estructura común + temas + componentes** sobre Base UI y Tailwind v4.
|
|
4
|
+
Todo está pensado para que un agente pueda usarlo, ampliarlo y comprobar su trabajo sin
|
|
5
|
+
adivinar. Lee antes: `README.md`, `docs/CONTRACT.md`, `contract.json` y
|
|
6
|
+
`components.manifest.json` (qué componentes hay, cómo se importan y qué `data-slot` exponen).
|
|
7
|
+
|
|
8
|
+
## Reglas (obligatorias)
|
|
9
|
+
|
|
10
|
+
1. **Solo variables.** Un componente no escribe colores, medidas, sombras, z-index ni
|
|
11
|
+
duraciones: pide un hueco a una variable (`--ds-*` o las de color de shadcn). Si falta el
|
|
12
|
+
hueco, **añade el token** con un valor que deje el aspecto como estaba y úsalo.
|
|
13
|
+
2. **Añadir, nunca sustituir.** Un componente nuevo es un fichero nuevo en `src/ui/`. No se
|
|
14
|
+
reescribe ni se renombra uno existente para otro fin. No uses `--overwrite` de la CLI de
|
|
15
|
+
shadcn sin aprobación expresa. Quitar o renombrar algo público = versión **mayor**.
|
|
16
|
+
3. **Nombres en inglés.** Tokens, props y valores de variantes. Variables propias con el
|
|
17
|
+
prefijo `--ds-`; las que Tailwind convierte en utilidades (`--color-*`, `--radius-*`,
|
|
18
|
+
`--shadow-*`) siguen su convención. Las privadas de un tema llevan el prefijo del tema
|
|
19
|
+
(`--zy-*`).
|
|
20
|
+
4. **Un solo motor: Base UI.** Nada de Radix. `asChild` no existe: se usa `render`.
|
|
21
|
+
5. **«Sin sombra» es `0 0 #0000`**, nunca `none` (invalida todo el `box-shadow`, anillo de
|
|
22
|
+
foco incluido).
|
|
23
|
+
6. **Un tema cumple el contrato completo** (`contract.json`) y define en `:root` todo
|
|
24
|
+
`--ds-*` que defina el tema base.
|
|
25
|
+
7. **La marca de una app vive en SU `brand.css`**, no en el tema del paquete. Para
|
|
26
|
+
personalizar una app (color, radio, fuentes) edita ese fichero; nunca `node_modules` ni
|
|
27
|
+
los temas. `pnpm exec orbital-ds-init` lo crea sin sobrescribir.
|
|
28
|
+
|
|
29
|
+
## Cómo cambiar el aspecto
|
|
30
|
+
|
|
31
|
+
- **Valores** (color, forma, fuentes, escala, sombras, espaciado, materiales, sidebar):
|
|
32
|
+
variables del tema en `src/themes/<tema>.css`.
|
|
33
|
+
- **Personalidad de un elemento** (por ejemplo, botones en píldora con degradado): reglas en
|
|
34
|
+
la sección «PERSONALIDAD» del tema, dirigidas a los ganchos del DOM —`data-slot`,
|
|
35
|
+
`data-variant`, `data-size`, `data-active`, `data-state`— y escritas solo con variables. Van
|
|
36
|
+
**sin capa** para ganar a las utilidades; define también hover y active.
|
|
37
|
+
- **Estructura** distinta de un componente (otro layout): variante del componente, no tema.
|
|
38
|
+
- **Un solo sitio.** Todos los temas son `conformance: strict`: sus reglas de personalidad solo
|
|
39
|
+
leen variables, y estilizan familias completas de piezas. Para adaptar el aspecto de una app
|
|
40
|
+
basta con su `brand.css`; para cambiar un tema, su bloque de VARIABLES. Si una regla necesita
|
|
41
|
+
un valor que no existe, añade la variable a la estructura (`src/styles.css`) con un valor
|
|
42
|
+
neutro y dásela en el tema.
|
|
43
|
+
|
|
44
|
+
## Movimiento (animación)
|
|
45
|
+
|
|
46
|
+
- **El tema gobierna el «cómo»**: ritmo (`--ds-motion-*`), curvas (`--ds-ease-*`) y coreografía
|
|
47
|
+
(`--ds-motion-distance`, `-scale`, `-blur`, `--ds-spring-bounce`, `--ds-motion-stagger`,
|
|
48
|
+
`--ds-motion-press-scale`). Cada tema trae el suyo de fábrica; la app lo ajusta en `brand.css`.
|
|
49
|
+
- **La app decide el «qué» y el «cuándo»** con **Motion** (librería oficial, opcional): una vez
|
|
50
|
+
`<DsMotionProvider>` en la raíz y los presets del tema con `useDsMotion()` (`fadeUp`,
|
|
51
|
+
`scaleIn`, `list`, `press`, `spring`). Para animar un componente existente:
|
|
52
|
+
`render={<motion.div {...m.fadeUp} />}`.
|
|
53
|
+
- Los componentes del paquete animan con CSS y nunca dependen de Motion. Ninguna duración, curva
|
|
54
|
+
ni capa se escribe a mano (el doctor lo comprueba también dentro de valores arbitrarios).
|
|
55
|
+
- Otras librerías: `readDsTransition()` (`orbital-ds/lib/motion`) da duración y curva CSS.
|
|
56
|
+
|
|
57
|
+
## Idioma (i18n)
|
|
58
|
+
|
|
59
|
+
- Los textos PROPIOS de los componentes (cerrar, cargando, paginación, carrusel, lateral,
|
|
60
|
+
cuestionario…) salen de `useDsMessages()` y sus catálogos (`orbital-ds/lib/i18n`: en, es,
|
|
61
|
+
pt-BR). **Ningún componente escribe un texto visible o accesible a mano**: el doctor lo rechaza
|
|
62
|
+
(`aria-label="…"`, `sr-only`, `?? "…"`, `title="…"`).
|
|
63
|
+
- Un texto nuevo = una clave nueva en los TRES catálogos (el tipo obliga a tenerla en todos).
|
|
64
|
+
- La app pasa su idioma una vez: `<DsI18nProvider locale={i18n.language}>` (con i18next,
|
|
65
|
+
`useTranslation().i18n.language`). Un texto concreto se sobrescribe con `messages`. Sin
|
|
66
|
+
provider, inglés. Otro idioma cae a su base (`es-CO` → `es`) o al inglés.
|
|
67
|
+
- El texto de la APP (títulos, botones con contenido) lo pone la app con su i18n, no orbital-ds.
|
|
68
|
+
- **En una app con i18next** (la plantilla TanStack es la referencia): los textos de orbital-ds viven
|
|
69
|
+
en el espacio **«orbital»** de sus JSON (`src/i18n/locales/<idioma>.json`), añadido a `ns`, y se
|
|
70
|
+
pasan con `messages={i18n.getResourceBundle(i18n.language, "orbital")}`. `pnpm exec orbital-ds-i18n`
|
|
71
|
+
rellena lo que falte sin pisar lo personalizado; `--check` va en el verify.
|
|
72
|
+
- **Añadir un idioma** (p. ej. `ru`): crea `ru.json` con las mismas claves que `es.json`, añádelo a
|
|
73
|
+
los idiomas de la app y ejecuta `pnpm exec orbital-ds-i18n`. Si orbital-ds no trae ese idioma, deja
|
|
74
|
+
el inglés y LISTA las claves pendientes: tradúcelas en el JSON hasta que `--check` pase.
|
|
75
|
+
- **RTL**: `dsDirection(idioma)` (`orbital-ds/lib/i18n`) da `ltr`/`rtl`; la app lo pone en `<html dir>` y
|
|
76
|
+
`DsI18nProvider` lo pasa a Base UI. La conversión de los componentes a clases lógicas
|
|
77
|
+
(`ms-`/`ps-`/`start-`/`text-start` en lugar de `ml-`/`pl-`/`left-`/`text-left`) es una fase pendiente.
|
|
78
|
+
|
|
79
|
+
## Instalar en una app
|
|
80
|
+
|
|
81
|
+
`pnpm exec orbital-ds-init`: elige tema y color, comprueba el contraste y, si lo aceptas,
|
|
82
|
+
escribe `brand.css`, conecta el CSS de la app (bloque de imports en orden), anota DESIGN.md,
|
|
83
|
+
instala Motion y pasa el doctor. Sin preguntas: `--primary <color> --theme <tema> --connect --motion`.
|
|
84
|
+
|
|
85
|
+
## Cómo comprobar tu trabajo
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pnpm check:ds # contrato, repetidos, sombras, fugas, valores fijos, personalidad, familias, contraste y demos
|
|
89
|
+
pnpm verify # doctor + typecheck + build
|
|
90
|
+
pnpm manifest # regenera components.manifest.json tras añadir componentes
|
|
91
|
+
pnpm showcase # escaparate: todos los componentes × todos los temas × claro/oscuro × marca
|
|
92
|
+
pnpm showcase:typecheck # tipos de las demos
|
|
93
|
+
pnpm showcase:build # compila el escaparate (comprobación sin servidor)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Escaparate.** Descubre solo los temas (`src/themes`), las demos (`showcase/src/demos/<componente>.tsx`,
|
|
97
|
+
una por componente: el doctor falla si falta) y las variantes (`showcase/variants/*.css`).
|
|
98
|
+
|
|
99
|
+
**Variante de un tema** (p. ej. «dame variantes de tactile»): un fichero en `showcase/variants/<nombre>.css`
|
|
100
|
+
con `@base-theme <tema>` y `@about <frase>` en su comentario de cabecera y **solo variables** (como un
|
|
101
|
+
`brand.css`). Valídala con `pnpm check:ds -- --css showcase/variants/<nombre>.css`: comprueba que las
|
|
102
|
+
variables existan y el **contraste AA** de la variante sobre su tema. Para llevarla a una app, su contenido
|
|
103
|
+
va al `brand.css`.
|
|
104
|
+
|
|
105
|
+
Si `pnpm check:ds` falla, **corrige la causa**; no lo desactives ni lo rodees.
|
|
106
|
+
|
|
107
|
+
## Añadir un componente
|
|
108
|
+
|
|
109
|
+
1. Fichero en `src/ui/<nombre>.tsx` (un componente = un fichero; sin barril).
|
|
110
|
+
2. `data-slot="<nombre>"` en su raíz y en las partes relevantes.
|
|
111
|
+
3. Solo tokens. Importa `cn` de `../lib/utils`; los demás componentes, de `./<otro>`.
|
|
112
|
+
4. Dependencias de terceros de un COMPONENTE: opcionales (`peerDependenciesMeta`), nunca
|
|
113
|
+
obligatorias. Las herramientas de terminal del paquete (`orbital-ds-init`) sí pueden
|
|
114
|
+
tener las suyas (`@clack/prompts`), porque no llegan al bundle de la app.
|
|
115
|
+
5. Su demo en `showcase/src/demos/<nombre>.tsx` (variantes, tamaños y estados).
|
|
116
|
+
6. `pnpm manifest`, `pnpm verify`; versión **menor** (añadido compatible).
|
|
117
|
+
|
|
118
|
+
## Crear un tema
|
|
119
|
+
|
|
120
|
+
Copia `themes/qratia.css` (o `tactile.css`, el ejemplo estricto), cambia valores, no quites
|
|
121
|
+
ninguno, añade `<tema>.fonts.css` con el `@import url()` de sus fuentes, marca la ficha con
|
|
122
|
+
`"conformance": "strict"` y ejecuta `pnpm check:ds`. En modo estricto el doctor exige que la
|
|
123
|
+
personalidad use **solo variables** (ni medidas, ni colores, ni pesos) y que estilice
|
|
124
|
+
**familias completas** de piezas (`slotFamilies` en `contract.json`: si das tinta al
|
|
125
|
+
`checkbox`, también al `radio-group-item`).
|
|
126
|
+
|
|
127
|
+
## Estándar de integración (cómo debe ajustarse una app)
|
|
128
|
+
|
|
129
|
+
Para que una app (o un agente que la edite) use orbital-ds sin romper nada:
|
|
130
|
+
|
|
131
|
+
1. **La marca vive solo en `brand.css`.** Ninguna regla de color propia fuera de ese fichero; jamás se edita el paquete.
|
|
132
|
+
2. **Solo variables públicas.** Nada de variables privadas de un tema: pueden renombrarse. Usa `--ds-*` y los tokens del contrato (`--zw-*` de `tactile` están obsoletas: usa `--ds-key-outline`, `--ds-key-edge` y `--ds-marker`).
|
|
133
|
+
3. **Sin `!important` ni selectores propios** para vencer a la librería (lateral, raíl, botones). Si algo no se puede ajustar con un token, es un hueco de la librería: abre un issue.
|
|
134
|
+
4. **El tema se elige con un solo `@import`** (o `pnpm theme`). Los temas con ámbito (`*.scoped.css`) son solo para un banco de pruebas; la marca (`brand.css`) gana siempre, también sobre ellos.
|
|
135
|
+
5. **Patrón de página.** La cabecera lleva solo botón de menú, título y, siempre visibles, idioma y tema (y avisos si la app los tiene). El subtítulo y las acciones de la vista van al inicio del contenido, centrado en un ancho máximo; solo un lienzo (mapa, kanban, editor) ocupa todo el ancho.
|
|
136
|
+
6. **Cada tema se documenta y se elige informado.** Lee `orbital-ds/themes/<tema>.guide.md` y su ficha `<tema>.meta.json` (esencia, gesto firma, paleta, accesibilidad, qué valores son aproximados) antes de usar o ajustar un tema.
|
|
137
|
+
7. **Comprueba antes de dar algo por bueno:** `pnpm exec orbital-ds-doctor --css src/brand.css` y el `verify` de la app.
|
|
138
|
+
|
|
139
|
+
### Accesibilidad (WCAG 2.2)
|
|
140
|
+
|
|
141
|
+
- **Mínimo exigible: AA.** Texto 4,5:1; texto grande y controles 3:1. `pnpm check:ds` falla si un par principal de texto sobre su superficie (texto/fondo, tarjeta, popover, primario, secundario, lateral, destructivo) o un texto de estado (info, danger, warning, success, neutral) baja de 4,5:1.
|
|
142
|
+
- **Objetivo: AAA en el texto del cuerpo** (7:1 para texto/fondo y texto/tarjeta). Se mide con `pnpm check:ds -- --aaa` y avisa, no falla: WCAG no aconseja exigir AAA a todo el sitio. Hoy todos los temas lo cumplen.
|
|
143
|
+
- **Excepciones declaradas, nunca silenciosas.** Si un tema no puede cumplir un par (el azul de marca de Twitter con texto blanco da 3:1), lo declara en su ficha, en `a11y.exceptions`, con su porqué; el doctor lo muestra como aviso.
|
|
144
|
+
- **Foco siempre visible** (anillo de 3:1 como mínimo) y sin que otro elemento lo tape.
|
|
145
|
+
- **Una marca nueva debe volver a pasar el doctor.** Cambiar `--primary` obliga a revisar `--primary-foreground`, `--link` y `--ring`.
|