|
|
|
|
@ -0,0 +1,404 @@
|
|
|
|
|
# air
|
|
|
|
|
|
|
|
|
|
Arquitectura y diseño de la capa visual para `src/uix/air`.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 1. Propósito y relación con terra
|
|
|
|
|
|
|
|
|
|
`air` no es `terra` con CSS encima. Es la capa visual del sistema: define tokens, aplica estilos y expone una API declarativa para el consumidor. `terra` resuelve comportamiento y accesibilidad. `air` resuelve presentación.
|
|
|
|
|
|
|
|
|
|
La relación entre ambas capas es asimétrica y deliberada:
|
|
|
|
|
|
|
|
|
|
- `air` conoce `terra`: consume su anatomía, sus `data-*` attrs y sus estados.
|
|
|
|
|
- `terra` no conoce `air`: no importa nada de `air`, no depende de ningún token visual.
|
|
|
|
|
|
|
|
|
|
> **Regla central.** Si algo en `air` necesita que `terra` cambie su comportamiento, hay un error de diseño. La dependencia solo fluye en una dirección.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 2. Sistema de tokens en tres capas
|
|
|
|
|
|
|
|
|
|
El error más común en design systems es tener un solo nivel de tokens donde todo es una variable CSS directa. `air` usa tres capas con responsabilidades distintas.
|
|
|
|
|
|
|
|
|
|
### 2.1 Primitive tokens
|
|
|
|
|
|
|
|
|
|
Valores absolutos. Son la escala base del sistema. Ningún componente los usa directamente.
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
/* tokens/primitive.css */
|
|
|
|
|
:root {
|
|
|
|
|
--blue-1: #eff6ff;
|
|
|
|
|
--blue-9: #6e56cf;
|
|
|
|
|
--blue-12: #1d1930;
|
|
|
|
|
|
|
|
|
|
--gray-1: #fcfcfd;
|
|
|
|
|
--gray-6: #d9dce3;
|
|
|
|
|
--gray-7: #c9ced6;
|
|
|
|
|
--gray-12: #111827;
|
|
|
|
|
|
|
|
|
|
--space-1: 4px;
|
|
|
|
|
--space-2: 8px;
|
|
|
|
|
--space-4: 16px;
|
|
|
|
|
|
|
|
|
|
--radius-1: 4px;
|
|
|
|
|
--radius-2: 6px;
|
|
|
|
|
--radius-3: 8px;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2.2 Semantic tokens
|
|
|
|
|
|
|
|
|
|
Apuntan a primitive tokens y cambian según el tema activo. Son la única capa que conoce el concepto de tema. Los componentes pueden usarlos, pero se prefiere que usen component tokens.
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
/* tokens/semantic.css */
|
|
|
|
|
:root,
|
|
|
|
|
[data-theme='light'] {
|
|
|
|
|
--color-accent: var(--blue-9);
|
|
|
|
|
--color-accent-muted: var(--blue-3);
|
|
|
|
|
--color-surface: var(--gray-1);
|
|
|
|
|
--color-text-primary: var(--gray-12);
|
|
|
|
|
--color-border: var(--gray-6);
|
|
|
|
|
--radius-md: var(--radius-2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme='dark'] {
|
|
|
|
|
--color-accent: var(--blue-10);
|
|
|
|
|
--color-surface: var(--gray-2);
|
|
|
|
|
--color-text-primary: var(--gray-1);
|
|
|
|
|
--color-border: var(--gray-7);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2.3 Component tokens
|
|
|
|
|
|
|
|
|
|
Scoped a cada componente. Son el único nivel que el CSS de un componente debe escribir. Permiten override quirúrgico sin tocar la escala global.
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
/* tokens/component/button.css */
|
|
|
|
|
.btn {
|
|
|
|
|
height: var(--btn-height);
|
|
|
|
|
padding-inline: var(--btn-px);
|
|
|
|
|
font-size: var(--btn-font-size);
|
|
|
|
|
background: var(--btn-bg);
|
|
|
|
|
color: var(--btn-fg);
|
|
|
|
|
border: 1px solid var(--btn-border);
|
|
|
|
|
border-radius: var(--btn-radius, var(--radius-md));
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> **Regla de componentes.** El CSS de un componente de `air` solo escribe `var(--btn-*)`, `var(--input-*)`, etc. Nunca toca `--blue-9` ni tokens primitivos directamente.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 3. El contrato con terra: data-* como API pública
|
|
|
|
|
|
|
|
|
|
`terra` genera `data-*` attrs en todos sus estados. Esa es exactamente la superficie que `air` necesita consumir. El CSS de `air` no necesita nada más para responder a estados del componente.
|
|
|
|
|
|
|
|
|
|
### 3.1 data-* attrs de contrato previstos en terra
|
|
|
|
|
|
|
|
|
|
| Atributo | Cuándo aparece | Uso típico en air |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| `data-disabled` | Componente deshabilitado | `opacity: .5; pointer-events: none` |
|
|
|
|
|
| `data-invalid` | Valor no válido | `color: var(--color-error)` |
|
|
|
|
|
| `data-open` / `data-closed` | Overlay visible o no | `display: block / none` |
|
|
|
|
|
| `data-highlighted` | Item bajo cursor o foco | `background: var(--color-accent-muted)` |
|
|
|
|
|
| `data-selected` | Item seleccionado | `font-weight: 500` |
|
|
|
|
|
| `data-placement` | `top \| bottom \| left \| right` | `transform` para animar entrada |
|
|
|
|
|
| `data-orientation` | `horizontal \| vertical` | `flex-direction` del contenedor |
|
|
|
|
|
|
|
|
|
|
### 3.2 Cómo air consume esos attrs
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
/* air responde a estados sin saber cómo terra los produce */
|
|
|
|
|
|
|
|
|
|
[data-disabled] { opacity: .5; pointer-events: none; }
|
|
|
|
|
[data-invalid] { --input-border: var(--color-error); }
|
|
|
|
|
[data-open] { display: block; }
|
|
|
|
|
[data-highlighted] { background: var(--color-accent-muted); }
|
|
|
|
|
[data-selected] { font-weight: 500; }
|
|
|
|
|
|
|
|
|
|
/* posicionamiento de overlays */
|
|
|
|
|
[data-placement='top'] { transform-origin: bottom center; }
|
|
|
|
|
[data-placement='bottom'] { transform-origin: top center; }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> **Advertencia crítica.** Los `data-*` de `terra` son API pública con el mismo peso que los tipos TypeScript, pero deben formalizarse componente a componente. Si `terra` renombra `data-open` a `data-expanded`, el CSS de `air` rompe silenciosamente sin ningún error de compilación. Tratar ese contrato con el mismo rigor que las props tipadas.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 4. Anatomía pública por componente
|
|
|
|
|
|
|
|
|
|
`air` no solo depende de `data-*` attrs: también depende de la anatomía de `terra`. Qué partes existen, cómo se llaman, cuáles son raíz y cuáles hijas.
|
|
|
|
|
|
|
|
|
|
Para cada componente que `air` estiliza, debe estar documentado:
|
|
|
|
|
|
|
|
|
|
- qué partes existen (`Root`, `Trigger`, `Content`, `Item`, etc.)
|
|
|
|
|
- qué `data-*` garantiza cada parte
|
|
|
|
|
- qué parte es el root visual (la que recibe los component tokens)
|
|
|
|
|
- qué partes pueden recibir snippets custom del consumidor
|
|
|
|
|
|
|
|
|
|
### 4.1 Ejemplo: anatomía de Input / Field
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Field.Root ← crea contexto, recibe props globales
|
|
|
|
|
Field.Label ← [data-invalid], [data-disabled], [data-required]
|
|
|
|
|
Field.Input ← [data-invalid], [data-disabled], [data-readonly]
|
|
|
|
|
Field.Description ← ayuda contextual
|
|
|
|
|
Field.Error ← visible cuando [data-invalid] en el Root
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Esta anatomía es contrato estable solo si `terra` la declara como pública. `air` asume que esas partes existen con esos nombres cuando están documentadas como soportadas. Si `terra` renombra `Field.Error` a `Field.Message`, `air` necesita actualizarse.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 5. Variantes: desacoplando dimensiones
|
|
|
|
|
|
|
|
|
|
El problema de las variantes no es cuántas hay, sino cómo se combinan. Con múltiples dimensiones ortogonales (`variant × size × color × state`), el número de combinaciones explota si el CSS las trata como casos individuales.
|
|
|
|
|
|
|
|
|
|
La solución es diseñar los component tokens de forma que cada dimensión sea independiente:
|
|
|
|
|
|
|
|
|
|
- `size` define tokens de dimensión: `height`, `padding`, `font-size`
|
|
|
|
|
- `variant` define tokens de apariencia: `background`, `color`, `border`
|
|
|
|
|
- `state` aplica overrides transversales: `disabled`, `invalid`, `loading`
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
/* size solo toca dimensiones */
|
|
|
|
|
[data-size='1'] { --btn-height: 28px; --btn-px: 10px; --btn-font-size: 12px; }
|
|
|
|
|
[data-size='2'] { --btn-height: 36px; --btn-px: 14px; --btn-font-size: 14px; }
|
|
|
|
|
[data-size='3'] { --btn-height: 44px; --btn-px: 18px; --btn-font-size: 16px; }
|
|
|
|
|
|
|
|
|
|
/* variant solo toca apariencia */
|
|
|
|
|
[data-variant='solid'] { --btn-bg: var(--color-accent); --btn-fg: white; --btn-border: transparent; }
|
|
|
|
|
[data-variant='outline'] { --btn-bg: transparent; --btn-fg: var(--color-accent); --btn-border: var(--color-accent); }
|
|
|
|
|
[data-variant='soft'] { --btn-bg: var(--color-accent-muted); --btn-fg: var(--color-accent-text); --btn-border: transparent; }
|
|
|
|
|
|
|
|
|
|
/* state no necesita conocer variant ni size */
|
|
|
|
|
[data-disabled] { opacity: .5; pointer-events: none; }
|
|
|
|
|
[data-loading] { --btn-fg: transparent; }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Esto escala a N variantes × M tamaños × K estados sin escribir combinaciones. Cada dimensión toca solo su propio conjunto de tokens.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 6. Theming: el tema es un atributo DOM, no estado JS
|
|
|
|
|
|
|
|
|
|
El store de Svelte es solo el mecanismo para cambiar `data-theme` en el elemento raíz. El theming real vive en CSS. El store no contiene colores ni tokens: solo el nombre del tema activo.
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
// theme/index.ts
|
|
|
|
|
import { writable } from 'svelte/store';
|
|
|
|
|
|
|
|
|
|
export const theme = writable<'light' | 'dark'>('light');
|
|
|
|
|
|
|
|
|
|
export function setTheme(next: 'light' | 'dark') {
|
|
|
|
|
theme.set(next);
|
|
|
|
|
document.documentElement.setAttribute('data-theme', next);
|
|
|
|
|
localStorage.setItem('theme', next);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
La ventaja de este enfoque es que el tema puede ser local a cualquier contenedor, no solo global. Poner `data-theme='dark'` en cualquier `div` crea un subtema sin JS adicional.
|
|
|
|
|
|
|
|
|
|
### 6.1 accentColor dinámico
|
|
|
|
|
|
|
|
|
|
Para paletas de acento personalizadas, el sistema puede derivar la escala desde un color base usando `color-mix()`:
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
[data-accent='custom'] {
|
|
|
|
|
--accent-base: #e54d2e;
|
|
|
|
|
--color-accent: var(--accent-base);
|
|
|
|
|
--color-accent-muted: color-mix(in oklch, var(--accent-base) 15%, white);
|
|
|
|
|
--color-accent-text: color-mix(in oklch, var(--accent-base) 80%, black);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> **Nota de compatibilidad.** `color-mix()` con `oklch` tiene soporte amplio en navegadores modernos (Chrome 111+, Firefox 113+, Safari 16.2+). Para proyectos que requieran soporte más amplio, evaluar generación de escalas en build time como alternativa o complemento. No tomar este enfoque como dogma: es la dirección correcta para sistemas nuevos, pero necesita validación de requisitos antes de adoptarlo como base única.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 7. CSS scoped de Svelte: la trampa más común
|
|
|
|
|
|
|
|
|
|
Los `<style>` scoped de Svelte no atraviesan el boundary de componente. Si `air` usa `<style scoped>`, los selectores basados en `data-*` no llegan a los elementos que renderiza `terra`.
|
|
|
|
|
|
|
|
|
|
La regla es clara:
|
|
|
|
|
|
|
|
|
|
- tokens (primitive, semantic, component): CSS global
|
|
|
|
|
- reglas basadas en `data-*`: CSS global
|
|
|
|
|
- estilos verdaderamente locales al nodo del wrapper de `air`: scoped
|
|
|
|
|
|
|
|
|
|
```svelte
|
|
|
|
|
<!-- components/input/input.svelte -->
|
|
|
|
|
<script>
|
|
|
|
|
import './input.css'; // global: contiene data-* rules y component tokens
|
|
|
|
|
let { variant = 'outline', size = '2', ...rest } = $props();
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<!-- terra.Field.Input ya aplica los data-* attrs desde su State -->
|
|
|
|
|
<Field.Input data-variant={variant} data-size={size} {...rest} />
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
/* solo estilos que no dependen de data-* ni de terra */
|
|
|
|
|
</style>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 7.1 Contrato formal entre terra y air
|
|
|
|
|
|
|
|
|
|
Antes de estilizar un componente en `air`, debe quedar explícito qué parte del componente es pública para consumo visual y qué parte es incidental.
|
|
|
|
|
|
|
|
|
|
### Pública
|
|
|
|
|
|
|
|
|
|
- anatomía documentada (`Root`, `Trigger`, `Content`, `Item`, etc.)
|
|
|
|
|
- `data-*` attrs documentados para cada parte
|
|
|
|
|
- atributos de variante y tamaño que `air` puede añadir (`data-variant`, `data-size`, etc.)
|
|
|
|
|
|
|
|
|
|
### No pública
|
|
|
|
|
|
|
|
|
|
- clases generadas por Svelte
|
|
|
|
|
- orden incidental del DOM que no esté documentado
|
|
|
|
|
- wrappers internos añadidos por comodidad de implementación
|
|
|
|
|
- nombres de variables locales o detalles del State de `terra`
|
|
|
|
|
|
|
|
|
|
> **Regla operativa.** Si un selector de `air` necesita una clase interna o un wrapper no documentado de `terra`, el contrato está mal definido.
|
|
|
|
|
|
|
|
|
|
## 7.2 Qué no debe hacer air
|
|
|
|
|
|
|
|
|
|
Para mantener la separación entre capas, `air` no debe:
|
|
|
|
|
|
|
|
|
|
- cambiar comportamiento que pertenece a `terra`
|
|
|
|
|
- depender de clases internas o estructura incidental del DOM
|
|
|
|
|
- leer estado interno de clases `State`
|
|
|
|
|
- definir lógica de accesibilidad, foco o keyboard navigation
|
|
|
|
|
- introducir tokens visuales dentro de `terra`
|
|
|
|
|
|
|
|
|
|
`air` sí puede:
|
|
|
|
|
|
|
|
|
|
- consumir `data-*` attrs públicos
|
|
|
|
|
- añadir `data-variant`, `data-size` o attrs equivalentes de presentación
|
|
|
|
|
- mapear tokens semánticos a component tokens
|
|
|
|
|
- ofrecer wrappers finos y declarativos sobre componentes de `terra`
|
|
|
|
|
|
|
|
|
|
## 8. Estructura de carpetas
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
src/uix/air/
|
|
|
|
|
tokens/
|
|
|
|
|
primitive.css ← escala absoluta de colores, espacio, radio
|
|
|
|
|
semantic.css ← surface, accent, text, border por tema
|
|
|
|
|
component/
|
|
|
|
|
button.css ← define --btn-* tokens
|
|
|
|
|
input.css ← define --input-* tokens
|
|
|
|
|
field.css ← define --field-* tokens
|
|
|
|
|
select.css ← define --select-* tokens
|
|
|
|
|
index.css ← barrel: importa todos los tokens
|
|
|
|
|
theme/
|
|
|
|
|
index.ts ← store + setTheme() + setAccent()
|
|
|
|
|
components/
|
|
|
|
|
button/
|
|
|
|
|
button.svelte ← wrapper fino sobre terra.Button
|
|
|
|
|
button.css ← aplica tokens a la anatomía real de terra
|
|
|
|
|
input/
|
|
|
|
|
input.svelte
|
|
|
|
|
input.css ← aplica --input-* sobre data-* attrs públicos
|
|
|
|
|
field/
|
|
|
|
|
field.svelte ← wrapper de Field.Root
|
|
|
|
|
field.css ← aplica --field-* sobre data-* attrs públicos
|
|
|
|
|
exports.ts
|
|
|
|
|
index.ts
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> **Diferencia importante.** `tokens/component/*.css` define qué tokens existen por componente. `components/*/*.css` aplica esos tokens a la anatomía concreta de `terra`. No deben mezclarse ambas responsabilidades en un mismo archivo por comodidad.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 9. Estrategia de implementación incremental
|
|
|
|
|
|
|
|
|
|
No tokenizar todos los componentes desde el principio. Los component tokens son trabajo de mantenimiento real. Crear solo los que tienen evidencia de que el consumidor va a necesitar override.
|
|
|
|
|
|
|
|
|
|
### Fase 1: piloto con Input / Field
|
|
|
|
|
|
|
|
|
|
- definir `primitive.css` y `semantic.css` completos
|
|
|
|
|
- component tokens solo para `Field` y `Input`
|
|
|
|
|
- validar que el contrato con `terra` funciona via `data-*`
|
|
|
|
|
- validar theming anidado con `data-theme` en contenedor
|
|
|
|
|
|
|
|
|
|
### Fase 2: expandir con evidencia
|
|
|
|
|
|
|
|
|
|
- añadir component tokens por componente según necesidad real
|
|
|
|
|
- no adelantar tokens que nadie ha pedido sobrescribir
|
|
|
|
|
- documentar qué tokens son API pública en cada componente
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 10. Diferencias respecto a Radix Themes
|
|
|
|
|
|
|
|
|
|
| Aspecto | Radix Themes | air |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| Capas de tokens | 2 niveles (primitives + semánticos) | 3 niveles + component tokens scoped |
|
|
|
|
|
| Customización de componente | Sobrescribir clases internas | Override de `--component-*` tokens |
|
|
|
|
|
| Temas anidados | `className='dark'` en contenedor | `data-theme` en cualquier contenedor |
|
|
|
|
|
| Dependencia de JS | Context + estado para theming | Solo para cambiar atributo DOM |
|
|
|
|
|
| AccentColor personalizado | Redefinir escala entera (9+ vars) | Una variable base, escala derivada |
|
|
|
|
|
| Contrato con headless | Implícito en clases internas | Explícito via `data-*` como API pública |
|
|
|
|
|
| CSS scoped | N/A (React) | Global para tokens y `data-*` rules |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 11. Checklist: componente Input / Field
|
|
|
|
|
|
|
|
|
|
Antes de considerar terminado cualquier componente de `air`, verificar cada punto.
|
|
|
|
|
|
|
|
|
|
### 11.1 Tokens
|
|
|
|
|
|
|
|
|
|
- [ ] `primitive.css` tiene la escala completa de colores, espacio y radio
|
|
|
|
|
- [ ] `semantic.css` mapea correctamente para light y dark
|
|
|
|
|
- [ ] `component/field.css` define `--field-gap`, `--field-label-size`, `--field-error-color`
|
|
|
|
|
- [ ] `component/input.css` define `--input-height`, `--input-px`, `--input-border`, `--input-bg`, `--input-radius`
|
|
|
|
|
- [ ] Ningún selector en `field.css` o `input.css` usa `--blue-9` ni tokens primitivos directamente
|
|
|
|
|
|
|
|
|
|
### 11.2 Contrato con terra
|
|
|
|
|
|
|
|
|
|
- [ ] El CSS de `air` responde a `[data-disabled]` en `Field.Root` y en `Field.Input`
|
|
|
|
|
- [ ] El CSS de `air` responde a `[data-invalid]` en `Field.Root` (cambia border e icono de error)
|
|
|
|
|
- [ ] `Field.Error` puede estilizarse o mostrarse a partir de `[data-invalid]` del root, sin asumir clases internas
|
|
|
|
|
- [ ] `Field.Label` refleja `[data-required]` si `terra` lo expone
|
|
|
|
|
- [ ] No hay ningún selector que dependa de clases generadas por Svelte scoped
|
|
|
|
|
|
|
|
|
|
### 11.3 Variantes y tamaños
|
|
|
|
|
|
|
|
|
|
- [ ] Los tokens de `size` solo tocan dimensiones: `height`, `padding`, `font-size`
|
|
|
|
|
- [ ] Los tokens de `variant` solo tocan apariencia: `bg`, `color`, `border`
|
|
|
|
|
- [ ] El estado `[data-disabled]` funciona igual en todas las variantes sin CSS específico por combinación
|
|
|
|
|
- [ ] El estado `[data-invalid]` funciona igual en todos los tamaños
|
|
|
|
|
|
|
|
|
|
### 11.4 Theming
|
|
|
|
|
|
|
|
|
|
- [ ] El componente cambia correctamente con `data-theme='dark'` en un contenedor padre
|
|
|
|
|
- [ ] No hay colores hardcodeados en el CSS del componente
|
|
|
|
|
- [ ] El tema anidado (dark dentro de light) funciona sin JS adicional
|
|
|
|
|
|
|
|
|
|
### 11.5 CSS global vs scoped
|
|
|
|
|
|
|
|
|
|
- [ ] Los archivos `.css` de tokens y variantes son importados como módulos globales
|
|
|
|
|
- [ ] Los selectores `data-*` no están dentro de `<style scoped>` en ningún `.svelte`
|
|
|
|
|
- [ ] Si hay estilos scoped, son solo para propiedades que no dependen de `data-*` ni de `terra`
|
|
|
|
|
|
|
|
|
|
### 11.6 API pública
|
|
|
|
|
|
|
|
|
|
- [ ] `exports.ts` expone el componente con su nombre correcto
|
|
|
|
|
- [ ] Los component tokens de `Input` y `Field` están documentados (qué tokens son override permitido)
|
|
|
|
|
- [ ] Existe una demo en `src/routes/test/field/` con: disabled, invalid, readonly, dark theme, tamaños
|
|
|
|
|
|
|
|
|
|
### 11.7 Limitaciones conocidas
|
|
|
|
|
|
|
|
|
|
- [ ] Si se usa `color-mix()`, está documentado el requisito de navegador mínimo
|
|
|
|
|
- [ ] Si se generan escalas en build time, el pipeline está documentado
|
|
|
|
|
- [ ] El comportamiento cuando `terra` renombra un `data-*` está contemplado en la demo
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
Si actualizamos la arquitectura de `air`, este documento también debe actualizarse. El contrato con `terra` es tan estable como queramos que sea.
|