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
dev 27012a5c68
feat(motion): structural stagger index + Cascade orchestrator on the verified model
4 months ago
..
accordion palabras: panel de imagen rico (componentes del framework) + fit/filtros en el motor + cssLength en sium 4 months ago
alert-dialog perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
announce feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
aspect-ratio fix(uix audit): apply Kim audit remediations across morfo/soma/eidos/recipe layers 5 months ago
auto-grid fix(uix audit): apply Kim audit remediations across morfo/soma/eidos/recipe layers 5 months ago
avatar perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
badge feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
banner feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
box fix(uix audit): apply Kim audit remediations across morfo/soma/eidos/recipe layers 5 months ago
breadcrumb perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
button feat(eidos): IconButton primitive + Lote 0 iconOnly->IconButton cascade 4 months ago
calendar perf(eidos): code-split 16 picker-family recipes (Phase 3, batch 4) 4 months ago
card feat(eidos): shape `shape` prop on Badge / Card / Button (per-component family) 4 months ago
carousel feat(eidos): IconButton primitive + Lote 0 iconOnly->IconButton cascade 4 months ago
cascade feat(motion): structural stagger index + Cascade orchestrator on the verified model 4 months ago
checkbox feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
clipboard perf(eidos): code-split final 10 recipes — index.css is foundation-only (Phase 3 batch 5 + Phase 4) 4 months ago
code feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
code-block feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
collapsible perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
color-field feat(eidos): color-field leading value swatch + full-width coherence 4 months ago
color-picker feat(eidos): field-family font-scaling coherence (input 1:1 + label step-down) 4 months ago
combobox feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish 4 months ago
command feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish 4 months ago
container docs(layout): READMEs for 8 layout primitives (audit PASS) 5 months ago
context-menu feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
cropper feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
css-field refactor(eidos): share number-field + css-field visual via spin-field 4 months ago
date-field feat(eidos): shared field control-trigger + segment state/width layers; uix- id prefix 4 months ago
date-picker feat(eidos): shared field control-trigger + segment state/width layers; uix- id prefix 4 months ago
date-range-field perf(eidos): code-split 16 picker-family recipes (Phase 3, batch 4) 4 months ago
date-range-picker fix(eidos): resolve segment-min-width orphan; color 3ch / hex 6ch segment width 4 months ago
dialog feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish 4 months ago
display perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
drag-drop feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
drawer feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
dropdown-menu refactor(motion): retire parallel orchestration motor (Plan A) + verified model + Fase 1 prototype 4 months ago
editable perf(eidos): code-split 15 form-control recipes (Phase 3, batch 3) 4 months ago
feed feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
field feat(eidos): color-field leading value swatch + full-width coherence 4 months ago
file-upload perf(eidos): code-split 15 form-control recipes (Phase 3, batch 3) 4 months ago
flex fix(uix audit): apply Kim audit remediations across morfo/soma/eidos/recipe layers 5 months ago
float perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
float-panel feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
form perf(eidos): code-split 15 form-control recipes (Phase 3, batch 3) 4 months ago
format-date fix(eidos): use ActiveEidos.require() instead of getActiveUix() in 15 components 5 months ago
format-number fix(eidos): use ActiveEidos.require() instead of getActiveUix() in 15 components 5 months ago
grid fix(uix audit): apply Kim audit remediations across morfo/soma/eidos/recipe layers 5 months ago
grid-list feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
group docs(layout): READMEs for 8 layout primitives (audit PASS) 5 months ago
heading perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
highlight perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
icon feat(eidos): tie icon scale to type scale + button icons on tokens 4 months ago
icon-button feat(eidos): IconButton primitive + Lote 0 iconOnly->IconButton cascade 4 months ago
image feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
image-adjustments feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
image-picker feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
kbd feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
link feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
link-preview feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
listbox feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
mark perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
menubar feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
meter perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
month-grid perf(eidos): code-split 16 picker-family recipes (Phase 3, batch 4) 4 months ago
navigation-menu feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
number-field refactor(eidos): share number-field + css-field visual via spin-field 4 months ago
pagination perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
palabras refactor(eidos): share number-field + css-field visual via spin-field 4 months ago
password-field feat(eidos): shared field control-trigger + segment state/width layers; uix- id prefix 4 months ago
picker-shell perf(eidos): code-split 16 picker-family recipes (Phase 3, batch 4) 4 months ago
pin-input perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
popover feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish 4 months ago
progress perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
qr-code fix(eidos): QrCode size tokens use literal var names (recipe contract) 4 months ago
radio-cards feat(uix): RadioCards orientation as ResponsiveProp + first responsive-prop example 4 months ago
radio-group perf(eidos): code-split 15 form-control recipes (Phase 3, batch 3) 4 months ago
range-calendar perf(eidos): code-split 16 picker-family recipes (Phase 3, batch 4) 4 months ago
rating-group perf(eidos): code-split 15 form-control recipes (Phase 3, batch 3) 4 months ago
relative-time feat(timer): optional key (auto-keyed timers) + close raw-timer/DOM violations 4 months ago
s-text feat(timer): optional key (auto-keyed timers) + close raw-timer/DOM violations 4 months ago
s-text-virtual-list feat(timer): optional key (auto-keyed timers) + close raw-timer/DOM violations 4 months ago
scroll-area feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish 4 months ago
scroll-frames feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
search-field feat(eidos): IconButton primitive + Lote 0 iconOnly->IconButton cascade 4 months ago
section docs(layout): READMEs for 8 layout primitives (audit PASS) 5 months ago
select feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish 4 months ago
separator perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
skeleton feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
slider feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer 4 months ago
spin-field refactor(eidos): share number-field + css-field visual via spin-field 4 months ago
spinner fix(eidos): use ActiveEidos.require() instead of getActiveUix() in 15 components 5 months ago
splitter perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
stack fix(uix audit): apply Kim audit remediations across morfo/soma/eidos/recipe layers 5 months ago
stepper perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
svg feat(eidos): IconButton primitive + Lote 0 iconOnly->IconButton cascade 4 months ago
switch perf(eidos): code-split 15 form-control recipes (Phase 3, batch 3) 4 months ago
table feat(eidos): IconButton primitive + Lote 0 iconOnly->IconButton cascade 4 months ago
tabs perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
tag-group perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
tags-input perf(eidos): code-split 15 form-control recipes (Phase 3, batch 3) 4 months ago
text perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
textarea feat(eidos): field-family font-scaling coherence (input 1:1 + label step-down) 4 months ago
time-field fix(eidos): resolve segment-min-width orphan; color 3ch / hex 6ch segment width 4 months ago
time-picker feat(eidos): field-family font-scaling coherence (input 1:1 + label step-down) 4 months ago
time-range-field perf(eidos): code-split 16 picker-family recipes (Phase 3, batch 4) 4 months ago
time-range-picker feat(eidos): field-family font-scaling coherence (input 1:1 + label step-down) 4 months ago
toast feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
toggle perf(eidos): code-split 15 form-control recipes (Phase 3, batch 3) 4 months ago
toggle-group feat(uix): heading levels H1-H6 + ToggleGroup deselectable 4 months ago
toolbar perf(eidos): code-split 35 component recipes (Phase 3, batch 1) 4 months ago
tooltip feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
trans fix(eidos): use ActiveEidos.require() instead of getActiveUix() in 15 components 5 months ago
tree-grid feat(eidos): IconButton primitive + Lote 0 iconOnly->IconButton cascade 4 months ago
tree-view feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
virtual-grid feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
virtual-list feat(eidos): canonize theming scales + tokenize magic numbers 4 months ago
words palabras: panel de imagen rico (componentes del framework) + fit/filtros en el motor + cssLength en sium 4 months ago
wrap fix(uix audit): apply Kim audit remediations across morfo/soma/eidos/recipe layers 5 months ago
year-grid perf(eidos): code-split 16 picker-family recipes (Phase 3, batch 4) 4 months ago
README.md feat(uix): elevation archetype (A1) + coherence audit + popover/dialog/option-list polish 4 months ago

README.md

eidos/components/

Recipes + wrappers visuales por componente. Cada componente vive en su subdirectorio con la forma canónica documentada abajo. Los .css sueltos al nivel raíz son legacy de la fase "eidos = solo CSS" y se migran progresivamente.

⚠️ Canon de arquetipos (2026-06-19) — leer antes de escribir un recipe/wrapper. La fuente de verdad de qué tokens/arquetipos consume un componente visual es el Contrato de construcción (§13) de ../ARCHETYPE_COHERENCE_AUDIT_2026-06-19.md: elevación vía data-depth (bundle completo, NO surface-raised a dedo) · estado vía --state-* (NO color-mix ad-hoc) · radio vía factor global + [data-shape-nest] (NO calc(--radius-md − space)) · label de campo canónico · bundle --size-* (NO re-derivar size→fuente). Cada fila del §13 marca su estado (LIVE / pendiente Etapa A): no referencies un token aún pendiente. Lo LIVE ya es obligatorio: color por token (cero hex), densidad --space-*/--control-height-*, RTL lógico, i18n langs.ts, portal-safe.


Forma canónica (disciplined option C — vigente desde 2026-05-10)

Convención unificada para TODOS los componentes multi-part. Sigue la estructura de air y la ergonomía moderna de bits-ui / shadcn-svelte: una sola entidad mental — <Drawer> — con hijos accesibles como propiedades — <Drawer.Trigger>, <Drawer.Content>, etc.

Reglas duras

  1. Un solo punto de entrada por componente: la default export es el componente root visual. Se llama igual que el componente (<Drawer>, <Tabs>, <Checkbox>, …) — no <Drawer.Provider>, no <Drawer.Root>.
  2. El root vive en {name}.svelte — NO en {name}-provider.svelte. El nombre del fichero coincide con el nombre del componente.
  3. No exportar Provider públicamente. La separación "Provider compound vs flat" es una invención previa que se ha retirado: hay UNA forma compound — el root + sus hijos atados.
  4. Los hijos siguen el naming de air / headless: Trigger, Content, Overlay, Title, Description, Close, Portal, Header, Footer, Item, Indicator, HiddenInput, Group, Label, etc. No inventar nombres nuevos.
  5. Portal se incluye donde air lo tenía (Dialog, Drawer, Popover, Tooltip — overlays portaled). Importado de $soma/components/internal.
  6. No flat con snippet slots como API principal. La invención <Drawer trigger={...} title={...} actions={...}> está retirada — esconde decisiones de composición que deberían ser explícitas en componentes con portal/overlay/content/close.
  7. Los hijos se atan al root con asignación explícita, no Object.assign (que en Svelte 5 puede causar issues sutiles de hidratación):
    const Drawer = DrawerComponent as DrawerNamespace;
    Drawer.Trigger = Trigger;
    Drawer.Content = Content;
    // ...
    

Estructura de directorio

drawer/
  drawer.svelte                ← root (lo que era air's Provider)
  drawer-trigger.svelte
  drawer-overlay.svelte
  drawer-content.svelte
  drawer-handle.svelte
  drawer-title.svelte
  drawer-description.svelte
  drawer-close.svelte
  drawer-header.svelte         ← eidos-only layout shell (cuando aplique)
  drawer-footer.svelte         ← idem
  drawer.css                   ← recipe
  types.ts                     ← extiende las props públicas de Soma
  index.ts                     ← compone Drawer + hijos

{name}.svelte — el root

Wrapper directo sobre el provider público de Soma. Setea el contexto headless, acepta los bindables del estado (open, value, pressed, etc.), añade los data-attrs visuales propios de eidos (data-size, data-variant, data-color, …), y renderiza {@render children?.()} para que los hijos se compongan dentro.

<script lang="ts">
	/**
	 * Eidos `<Drawer>` — root component. Wraps the Soma
	 * to set up the component context.
	 */
	import * as Drawer from '$soma/components/drawer';
	import type { DrawerProps } from './types';

	let {
		open = $bindable(false),
		activeSnapPoint = $bindable(null),
		isDragging = $bindable(false),
		children,
		...rest
	}: DrawerProps = $props();
</script>

<Drawer.Provider {...rest} bind:open bind:activeSnapPoint bind:isDragging>
	{@render children?.()}
</Drawer.Provider>

Patrón de import interno: import * as Drawer from '$soma/components/drawer'. Eidos no crea una fachada headless propia por componente; envuelve las partes públicas de Soma directamente como <Drawer.Provider>, <Drawer.Trigger>, etc.

Prohibiciones de naming interno

El namespace interno que apunta a Soma se llama igual que el componente. No se prefija ni se renombra:

<!-- Correcto -->
import * as Collapsible from '$soma/components/collapsible';

<Collapsible.Provider>
	<Collapsible.Trigger />
</Collapsible.Provider>

Formas prohibidas:

  • import * as Parts from '$soma/components/collapsible'
  • import * as CollapsibleBase from '$soma/components/collapsible'
  • import { Provider as SomaCollapsibleProvider } from '$soma/components/collapsible'
  • etiquetas sueltas <Provider>, <Trigger>, <Content> dentro de Eidos
  • tipos o aliases internos SomaXxxProvider, XxxBase, XxxRoot

Motivo: Eidos ya declara la capa en el path. El import interno debe expresar la entidad headless concreta de Soma, no una abstraccion inventada. Si un wrapper Eidos necesita el provider de Soma, escribe <Collapsible.Provider>; si necesita una parte, escribe <Collapsible.Trigger>, <Collapsible.Content>, etc.

Guardia de componentes compuestos de fecha

Incidencia 2026-05-20: DateField, DatePicker y DateRangePicker no pueden cerrarse con wrappers Eidos si Morfo/Soma y la demo no estan cerrados primero. Para componentes compuestos de fecha:

  • Comparar antes con morfo-runtime (air / terra) y referencias React Aria, Ark UI, Bits UI y shadcn-svelte.
  • Completar Morfo con partes, data-*, ARIA, estados, keyboard y eventos observables de la superficie compuesta.
  • La demo no puede inventar data-calendar-* o data-range-calendar-*; si la receta los necesita, pertenecen a Morfo/Soma.
  • Cada control visible (granularity, hourCycle, segmentos, min/max, pagedNavigation, modal/no-modal, clear) debe cambiar algo visible en el stage y estar reflejado en los snippets.
  • Verificar visualmente popup, uno/dos meses, seleccion, deseleccion parcial, limites y accion de borrado antes de documentarlo como listo.

{name}-{part}.svelte — hijos passthrough

Cada hijo es un wrapper delgado sobre la part del Soma. Si añade visual-only data-attrs, los stamp aquí (ej. data-size en Content). Si es passthrough puro (Trigger, Close), trivial.

<!-- drawer-trigger.svelte -->
<script lang="ts">
	import * as Drawer from '$soma/components/drawer';
	import type { DrawerTriggerProps } from './types';

	let { children, ...rest }: DrawerTriggerProps = $props();
</script>

<Drawer.Trigger {...rest}>
	{@render children?.()}
</Drawer.Trigger>

index.ts — compone el namespace

Asignación explícita per-property sobre el root component. NO usar Object.assign(DrawerComponent, { Trigger, ... }) — Svelte 5 puede manejar mal la mutación bulk del component constructor durante hidratación, causando que los hijos aparezcan y desaparezcan después de mount.

import DrawerComponent from './drawer.svelte';
import Trigger from './drawer-trigger.svelte';
import Overlay from './drawer-overlay.svelte';
import Content from './drawer-content.svelte';
import Title from './drawer-title.svelte';
import Description from './drawer-description.svelte';
import Close from './drawer-close.svelte';
import Header from './drawer-header.svelte';
import Footer from './drawer-footer.svelte';
import Handle from './drawer-handle.svelte';
import { Portal } from '$soma/components/internal';

type DrawerNamespace = typeof DrawerComponent & {
	Trigger: typeof Trigger;
	Portal: typeof Portal;
	Overlay: typeof Overlay;
	Content: typeof Content;
	Handle: typeof Handle;
	Title: typeof Title;
	Description: typeof Description;
	Close: typeof Close;
	Header: typeof Header;
	Footer: typeof Footer;
};

const Drawer = DrawerComponent as DrawerNamespace;
Drawer.Trigger = Trigger;
Drawer.Portal = Portal;
Drawer.Overlay = Overlay;
Drawer.Content = Content;
Drawer.Handle = Handle;
Drawer.Title = Title;
Drawer.Description = Description;
Drawer.Close = Close;
Drawer.Header = Header;
Drawer.Footer = Footer;

export { Drawer };
export default Drawer;

export type {
	DrawerProps,
	DrawerTriggerProps as TriggerProps,
	DrawerOverlayProps as OverlayProps,
	DrawerContentProps as ContentProps
	// … etc
} from './types';

types.ts — passthrough + adiciones eidos

import type {
	ProviderProps,
	TriggerProps,
	ContentProps
	// …
} from '$soma/components/drawer';

export type DrawerProps = ProviderProps;
export type DrawerTriggerProps = TriggerProps;

// Eidos añade props visuales que el recipe consume vía data-attrs
export type DrawerContentProps = ContentProps & {
	size?: ResponsiveProp<DrawerSize>;
};

Sin XxxFlatProps, sin XxxProviderProps as ProviderProps aliases. Hay un solo XxxProps (el del root) y los XxxPartProps per hijo.

Cuando el tipo root de Soma se importa como ProviderProps, se usa solo como tipo local dentro de types.ts. No se exporta al consumidor con nombre ProviderProps desde Eidos ni se crea un alias SomaXxxProviderProps.

Tipos visuales canonicos

No se redeclaran variantes, colores ni tamanos a mano en cada componente. La unica fuente para las props visuales compartidas es src/uix/eidos/lib/types.ts:

  • Size y ResponsiveProp<T> para escalas responsivas.
  • ControlVariant, SelectionVariant, ChipVariant, MarkerVariant, SurfaceVariant, etc. para variantes canonicas por familia visual.
  • ColorRole para la paleta de intents UIX (primary, secondary, neutral, affirm, fulfill, risk, threat, loss).

Cuando un componente necesita restringir una familia compartida, usa Extract<> sobre el tipo canonico. No se crean unions locales con literales duplicados:

import type { ColorRole, ControlVariant, Size } from '$uix/eidos/lib/types';

export type DateFieldSize = Extract<Size, 'sm' | 'md' | 'lg'>;
export type DateFieldVariant = ControlVariant;
export type DateFieldColor = ColorRole;

Partes Eidos-only

Algunas partes existen solo para componer la superficie visual: Header, Footer, Status, Main, Handle, filas/labels planas, indicadores decorativos o primitives svg. No necesitan morfo propio mientras cumplan las cuatro reglas:

  1. No crean comportamiento ni estado.
  2. No son target de eventos perceptivos.
  3. No poseen ARIA obligatoria ni relaciones accesibles propias.
  4. Solo emiten estructura, clase/estilo passthrough o data-* visuales que el recipe consume.

Si cualquiera de esas reglas deja de cumplirse, la parte deja de ser Eidos-only y debe subir al contrato correspondiente: primero morfo, luego Soma si necesita runtime.

El agregador scripts/eidos-lint-all.ts mantiene una allowlist explícita de estos attrs/parts visuales. El lint base sigue siendo estricto con valores inválidos de enums morfo; la allowlist solo evita que el reporte de drift se llene de partes visuales intencionales.

src/uix/eidos/component-api-contract.test.ts protege la forma pública del barrel: sin Object.assign, sin Provider público, root en {component}.svelte y miembros del XxxNamespace sincronizados con sus asignaciones explícitas (Drawer.Trigger = Trigger, etc.).

La guardia src/uix/eidos/component-visual-attrs.test.ts fija el cableado mínimo entre wrapper y recipe: si un wrapper visual declara una prop que se consume como data-* (size, variant, color, position, columns, etc.), el fichero debe seguir estampando ese atributo. Esto no añade comportamiento a Eidos; solo evita que la capa visual trague props en silencio.

Uso del consumidor

<script lang="ts">
	import { Drawer } from '$uix/eidos/components/drawer';
	let open = $state(false);
</script>

<Drawer bind:open variant="overlay" direction="right">
	<Drawer.Trigger>Open</Drawer.Trigger>
	<Drawer.Portal>
		<Drawer.Overlay />
		<Drawer.Content>
			<Drawer.Header>
				<Drawer.Title>Title</Drawer.Title>
				<Drawer.Description>Subtitle</Drawer.Description>
			</Drawer.Header>
			<p>body</p>
			<Drawer.Footer>
				<Drawer.Close>Close</Drawer.Close>
			</Drawer.Footer>
		</Drawer.Content>
	</Drawer.Portal>
</Drawer>

Componentes single-part (toggle, icon)

Sin hijos compound. El default export ES el componente entero. Sin namespace, sin asignación de propiedades, sin Provider alias.

// toggle/index.ts
export { default } from './toggle.svelte';
export type { ToggleProps, ToggleVariant, ToggleSize } from './types';
import Toggle from '$uix/eidos/components/toggle';
<Toggle bind:pressed variant="solid">Bold</Toggle>

Partes visuales por defecto

Algunos componentes multi-part necesitan una parte visual minima para no renderizar una superficie rota. Switch es el caso canonico: el root sigue exponiendo Switch.Thumb, pero si el consumidor no aporta children, <Switch /> monta un thumb visual por defecto. Esto no crea una API flat ni esconde el contrato de Soma; solo evita que el uso minimo renderice un track sin indicador.


Toast — caso especial (dos roots independientes)

Toast tiene dos roots públicos que NO se anidan:

  • <Toast> — manual compound (Provider+Viewport+Item iteración del consumer). Children attached: Viewport, Item, Status, Main, Title, Description, Action, Close.
  • <Toaster /> — imperative auto-mount. Renderiza Provider+Viewport+ Item-loop con un template default. Pasas toaster de createToaster(). Export separado, no Toast.Toaster porque es un root competidor, no un hijo.
import { Toast, Toaster, createToaster } from '$uix/eidos/components/toast';

const t = createToaster();

// Imperative
<Toaster toaster={t} />

// Manual
<Toast toaster={t}>
  <Toast.Viewport>
    {#each t.toasts as toast}
      <Toast.Item {toast}>
        <Toast.Title>{toast.title}</Toast.Title>
      </Toast.Item>
    {/each}
  </Toast.Viewport>
</Toast>

Convenciones de naming de tokens CSS

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) --_tabs-trigger-height, --_toggle-bg

Los [data-{component}] y [data-{component}-{part}] selectors son la única vía pública para que el recipe se acople al runtime — toda información cross-layer pasa por data-attrs declarados en el morfo. Cada --{component}-* consumido por CSS debe salir de EidosConfig.recipes; si un alias publico queda sin consumidor real, el test src/uix/eidos/recipe-css-contract.test.ts falla.


Comparativa obligatoria por componente

Ningun componente Eidos se declara cerrado solo por envolver Soma. Antes de implementar o revisar un componente:

  1. Leer Air en la rama anterior (morfo-runtime:src/uix/air/components/{name}) cuando exista. Air es la primera baseline visual.
  2. Leer Soma/Morfo actuales para separar comportamiento, ARIA, estado, traducciones y data-attrs de la superficie visual Eidos.
  3. Auditar Morfo/Sema. El morfo no se considera correcto solo porque compile:
    • Clasificar el componente como pasivo, interactivo o mixto.
    • Justificar cualquier 0 events de forma explicita.
    • Para cada accion real de usuario revisar family, verb, sequence, intent, target, prewrite y commit.
    • Verificar que Soma dispara esas ocurrencias con runtime.trigger(...).
    • Evitar eventos de alta frecuencia para cambios continuos; normalmente se modelan inicio/drag confirmado/commit, no cada frame.
  4. Comparar contra referentes externos relevantes: Radix/Radix Themes, Ark UI, Bits UI, shadcn-svelte y React Aria cuando aplique.
  5. Crear/actualizar components/{name}/README.md con tabla de funcionalidades, tabla Morfo/Sema, gaps y decisiones. Cada ⚠️ / ❌ debe acabar en una decision explicita: implementar ahora, diferir a Morfo/Soma, diferir a v2 o descartar por no pertenecer a Eidos.
  6. Si hay demo en web/routes/uix/components/{name}, sus snippets forman parte de la arquitectura del componente: deben reproducir el mismo contrato visible que el preview. En componentes schema-driven (form, auto-fields, date/time con formatos, etc.) no se permite un schema reducido que omita campos visibles, validators o defaults reales.
  7. Solo despues tocar wrapper, recipe o tokens.

El objetivo no es copiar APIs, sino que Eidos no quede por debajo de Air ni de los referentes en funcionalidades reales. Si una capacidad pertenece a Soma, la tabla debe decirlo; si es visual, Eidos debe cubrirla o justificar el gap.

Reglas especificas para demos de formularios

  • La validacion en tiempo real se modela como validationBehaviour: 'onChange'.
  • Si una demo arranca en onChange, los defaults iniciales deben ser validos salvo que el README y la UI digan explicitamente que se esta mostrando un formulario inicialmente invalido.
  • El snippet de Soma y el snippet de Eidos deben declarar los mismos campos visibles que el preview: imports SIUM, schema, defaults, Form + Field y Form.AutoFields. No se admiten snippets que omiten age, role, campos de fecha, arrays u otros datos que el preview valida.
  • Form.AutoFields es renderer reflectivo de SIUM. Si necesita comportamiento nuevo, se cambia Soma / $libs/forms / Morfo antes que Eidos.

Forma legacy (CSS-only, retired)

Hubo una fase anterior cuando eidos sólo emitía CSS (accordion.css, dialog.css, etc., al nivel raíz de components/). Todos los componentes han sido migrados a la forma canónica del subdirectorio. Si encuentras un .css suelto, es un descuido — debe vivir dentro de su subdirectorio.


Estado actual de la migración (2026-05-20)

La tanda 2026-05-17 reanuda la migración Soma -> Eidos por orden explícita. Los wrappers nuevos siguen el mismo criterio: envolver partes públicas de Soma, añadir sólo props visuales (size en esta tanda) y dejar comportamiento, estado, ARIA, traducciones y escritura headless en Soma/Morfo.

Componente Forma canónica Notas
toggle ✅ single-part piloto
switch ✅ multi-part Thumb
collapsible ✅ multi-part Trigger, Content
dialog ✅ multi-part Trigger, Portal, Overlay, Content, Title, Description, Close, Header, Footer
drawer ✅ multi-part + Handle
field ✅ multi-part Label, RequiredIndicator, Control, Input, HelperText, ErrorText, Prefix, Suffix
form ✅ multi-part Submit, Reset, ErrorSummary, AutoFields
popover ✅ multi-part Arrow, Anchor, Title, Description
toast ✅ multi-part + Toaster separate
accordion ✅ multi-part Item, Header, Trigger, Content
avatar ✅ multi-part Image, Fallback (eidos-native)
breadcrumb ✅ multi-part List, Item, Link, Separator, Ellipsis
calendar ✅ multi-part Header, Heading, Prev/Next, Month/YearSelect, Grid, Cell, Day
date-picker ✅ multi-part DateField + Popover + Calendar composition
icon ✅ single-part + 1697 lucide glyphs
meter ✅ multi-part Indicator
number-field ✅ multi-part Input, IncrementTrigger, DecrementTrigger, Scrubber
pagination ✅ multi-part FirstTrigger, PrevTrigger, NextTrigger, LastTrigger, Item, Ellipsis
progress ✅ multi-part Label, ValueText, Indicator
rating-group ✅ multi-part Item
search-field ✅ multi-part Input, ClearTrigger
select ✅ multi-part Trigger, Value, Indicator, Portal, Content, Viewport, Item, ItemIndicator, Group
combobox ✅ multi-part Control, Input, Trigger, Indicator, Portal, Content, Viewport, Item, ItemIndicator
slider ✅ multi-part Range, Thumb, Tick
tooltip ✅ multi-part + Group
tabs ✅ multi-part List, Trigger, Content, Indicator
checkbox ✅ multi-part Indicator, HiddenInput, Group, GroupLabel
radio-group ✅ multi-part Item, Indicator, HiddenInput, Label
toolbar ✅ multi-part Button, Link, Group, GroupItem, Separator
tag-group ✅ multi-part Label, Item, Link, RemoveButton
tags-input ✅ multi-part Control, Input, Item, ItemText, ItemDeleteTrigger, ClearTrigger
file-upload ✅ multi-part Label, Dropzone, Trigger, HiddenInput, FileList, Item, preview/progress/actions
editable ✅ multi-part Area, Control, Preview, Input, EditTrigger, SubmitTrigger, CancelTrigger
stepper ✅ multi-part List, Item, Trigger, Indicator, Separator, Content, CompletedContent, Prev/Next

Orden recomendado para continuar

  1. Componentes grandes sólo con tabla previa de migración: date-range-picker, time-field, time-picker.

Powered by TurnKey Linux.