You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/README.md

183 lines
6.0 KiB

# `eidos/components/`
Recipes y wrappers visuales por componente. La forma actual de un
componente es un **subdirectorio** con cuatro archivos; los `.css`
sueltos del nivel raíz son legacy de la fase "eidos = solo CSS" y se
migran progresivamente.
## Forma actual (toggle es el piloto)
```
toggle/
toggle.css → recipe CSS (selectores [data-toggle], variants, …)
toggle.svelte → wrapper sobre $soma/components/toggle
types.ts → ToggleProps (extiende SomaToggleProps)
index.ts → re-exports públicos
README.md → notas del componente (opcional, recomendado para piloto)
```
### `{name}.svelte` — el wrapper
Compone sobre el provider headless del soma. Re-bindable de
estado (`pressed = $bindable(false)`), emite los data-attrs de tokens
visuales y opcionalmente añade slots (`icon`, `checkMark`):
```svelte
<script lang="ts">
import * as Toggle from '$soma/components/toggle';
import type { ToggleProps } from './types';
let {
variant = 'solid',
size = 'md',
block = false,
iconOnly = false,
icon,
checkMark = false,
pressed = $bindable(false),
children: bodyContent,
...somaProps
}: ToggleProps = $props();
</script>
<Toggle.Provider
{...somaProps}
bind:pressed
data-variant={variant}
data-size={size}
data-block={block ? '' : undefined}
data-icon-only={iconOnly ? '' : undefined}
>
{#snippet children(snippetProps)}
{#if icon}{@render icon(snippetProps)}{/if}
<span class="eidos-toggle-body">{@render bodyContent?.(snippetProps)}</span>
{#if checkMark === true && snippetProps.pressed}
<svg class="eidos-toggle-checkmark" …/>
{/if}
{/snippet}
</Toggle.Provider>
```
Patrones notables:
- **`children` se renombra a `bodyContent`** dentro del wrapper porque
el `{#snippet children}` interno shadowearía el prop y haría
recursión.
- **`pressed = $bindable(false)`** se declara explícitamente; sin esto
un consumer que pase `bind:pressed` choca con un error de Svelte
("non-bindable property").
- **`<span class="eidos-toggle-body">`** envuelve el body del usuario
para que `[data-icon-only] .eidos-toggle-body { sr-only }` lo oculte
visualmente sin perder accesibilidad.
### `types.ts` — extensión de los tipos del soma
```ts
import type { Snippet } from 'svelte';
import type { ToggleProps as SomaToggleProps } from '$soma/components/toggle/types';
import type { Size } from '$uix/eidos/lib/types';
export type ToggleVariant = 'solid' | 'outline' | 'ghost';
export type ToggleSize = Extract<Size, 'sm' | 'md' | 'lg'>;
export type ToggleProps = SomaToggleProps & {
variant?: ToggleVariant;
size?: ToggleSize;
block?: boolean;
iconOnly?: boolean;
icon?: Snippet<[{ pressed: boolean }]>;
checkMark?: boolean | Snippet<[{ pressed: boolean }]>;
};
```
Reglas:
- **Sin prefijo `Eidos`.** El path `$uix/eidos/components/{x}` ya
identifica la capa.
- **Subset el tipo compartido.** `Size` global tiene 8 valores
(`xxs..xxl + full`); el componente reduce con `Extract` al subset
que su recipe soporta. Compile error antes que fallback silencioso.
- **El nombre del prop refleja la API doctrinal.** `intent` y `color`
pertenecen a soma — no se redeclaran en eidos; se heredan de
`SomaToggleProps`.
### `index.ts` — re-exports
```ts
export { default } from './toggle.svelte';
export { default as Provider } from './toggle.svelte';
export type {
ToggleProps,
ToggleProps as ProviderProps,
ToggleVariant,
ToggleSize
} from './types';
```
Doble export (`default` + `Provider`) por la doctrina de API:
| Forma | Cuándo |
|---|---|
| `import Toggle from '$uix/eidos/components/toggle'` → `<Toggle>` | single-part components, ergonomía Chakra-style |
| `import * as Toggle from …` → `<Toggle.Provider>` | consumers que prefieren la forma compound (Radix-style) |
### `{name}.css` — el recipe
Selectores `[data-{component}]`, variantes por `data-color`,
`data-size`, `data-variant`, etc.
Para el subset por componente, el CSS sólo define los tokens
permitidos por el anexo del libro. Si un componente no admite `loss`,
no aparece `[data-color='loss']` en su recipe.
#### Convención de naming de tokens CSS
**Regla:** los tokens CSS llevan el nombre del **componente**, no de la
**capa**. Sin prefijos de capa — ni `--eidos-`, ni `--air-`, ni
`--terra-`, ni `--soma-`.
| Forma | Uso | Ejemplo |
|---|---|---|
| `--{component}-…` | tokens públicos (sobreescribibles por el consumer) | `--dialog-content-bg`, `--toggle-radius-md` |
| `--_{component}-…` | tokens internos del recipe (no parte del API público) | `--_dialog-padding`, `--_toggle-palette-track` |
**Por qué bare-prefixed (sin `--eidos-`):**
- Los recipes son la skin oficial; el consumer interactúa con tokens
por componente, no por capa.
- Si un consumer construye un recipe alternativo a partir de soma+morfo
(doctrina §13), reutiliza los mismos tokens — no debe importar de qué
capa "salieron".
- Migrar de air → eidos no debe romper override de consumer. Mantener
`--{component}-…` hace la migración invisible (el rename `air-` → ``
es interno).
- El selector `[data-{component}]` ya identifica al componente; el
token sólo necesita coincidir con ese mismo nombre.
**Histórico:** la baseline `air/` usaba `--air-{component}-…`. Al
migrar al wrapper eidos se hace strip del prefijo `air-` (no se
reemplaza por `eidos-`).
## Forma legacy (CSS-only)
Los archivos sueltos `accordion.css`, `dialog.css`, `drawer.css`, etc.
son la fase anterior cuando eidos sólo emitía CSS. Funcionan, pero se
migran a la forma actual cuando el componente entra en revisión.
## Pendientes de migrar a wrapper
| Componente | CSS legacy | Wrapper |
|---|---|---|
| toggle | — | ✅ piloto |
| switch | — | ✅ |
| collapsible | — | ✅ (multi-part: flat + compound) |
| dialog | `dialog.css` | ⏳ |
| drawer | `drawer.css` | ⏳ |
| popover | `popover.css` | ⏳ |
| toast | `toast.css` | ⏳ |
| avatar | — | ✅ eidos-native |
| accordion | `accordion.css` | ⏳ |
| tabs | `tabs.css` | ⏳ |
| checkbox | `checkbox.css` | ⏳ |
| tooltip | `tooltip.css` | ⏳ |

Powered by TurnKey Linux.