feat(direction): las demos dejan de clavar ltr, y la prosa inglesa se declara inglesa

Cierra el eje de direccion abierto en 013ceac57.

1) LAS 49 DEMOS. Todas hacian `let dir = $state('ltr')` y se lo pasaban
explicito al componente, asi que el interruptor del topbar no movia
ninguna: la prop gana a prefs, que es el contrato. Pasan al patron de tres
estados `auto | ltr | rtl` con `auto` por defecto, que es lo que recibe un
consumidor real — sin prop, resolviendo por prefs. Elegir ltr/rtl sigue
demostrando que la prop pisa. El arnes compartido SystemAxes va al mismo
patron, y las 22 demos que lo bindean pasan a bindear el modo.

El tooltip ya tenia tres estados con '' como "inherit" — mismo concepto,
otro nombre; normalizado a `auto`.

2) LA PROSA. `<main data-uix-canvas dir="ltr" lang="en">`. La copia de la
documentacion esta escrita en ingles, asi que se DECLARA inglesa. Sin eso,
al voltear la shell a RTL el algoritmo bidi mueve la puntuacion neutra
final al extremo visual izquierdo y "What is morfo?" se lee "?What is
morfo" — bidi correcto sobre una declaracion falsa. `dir="ltr"` lo arregla
y ademas AISLA la tirada en un solo atributo: la hoja de estilos de agente
del WHATWG da `[dir=ltr i] { unicode-bidi: isolate }`, verificado en la
fuente. NO `dir="auto"`: la spec llama a su heuristica de primer-caracter
"very crude" y aqui ya sabemos la respuesta.

LO QUE ESTO NO ARREGLA, y es correcto que no lo haga: el texto ingles
DENTRO de un componente que declara rtl sigue mostrando el artefacto. El
componente hace bien en estar en rtl —es lo que pidio el usuario— y el
contenedor de arriba no entra en el. Eso solo se cierra traduciendo el
contenido de la demo o marcandolo; es la parte que las cinco librerias de
referencia dejan explicitamente al consumidor.

Verificado: check 74 = linea base exacta. smoke: las 49 rutas tocadas
PASAN; los 3 fallos de la pasada (/demos/motion, /temas,
/uix/components/card) son inestables — ninguno esta en el diff, y la
pasada sin mis cambios fallo en dos rutas DISTINTAS (/demos/cristal,
/demos/heroscrolling). En Chrome: elegir arabe voltea calendar, tabs y
slider por derivacion mientras la prosa se queda legible en ltr.

NOTA DE METODO: un `prettier --write` sobre todas las demos genero 165
ficheros y +31k lineas de ruido, y rompio dos atributos normalizando
`data-perm-mode='type="X"'` a comillas dobles. Revertido y reaplicado solo
el cambio de direccion: 49 ficheros, +357/-161.

QUEDA: `html lang` no sigue al idioma (el dir si); perm:check sin ejecutar
—el topbar ya lleva data-perm-step, lo que hace que toda ruta tenga paso.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-dir-prefs
dev 2 months ago
parent 42d58c3940
commit 5469e05df9

@ -1162,7 +1162,19 @@
{/if}
</aside>
<main data-uix-canvas>
<!-- The docs copy is written in English, so it is DECLARED as English.
Without this, flipping the shell to RTL leaves the prose in an RTL
paragraph and the Unicode bidi algorithm moves trailing neutral
punctuation to the visual left — "What is morfo?" renders as
"?What is morfo". That is correct bidi over a wrong declaration.
`dir="ltr"` fixes it and isolates the run in one attribute: the
WHATWG UA stylesheet gives `[dir=ltr i]` `unicode-bidi: isolate`.
NOT `dir="auto"` — the spec calls its first-strong-character
heuristic "very crude" and we already know the answer.
Components inside still follow prefs: they stamp their own `dir`
only when someone asserted one, so an RTL preference reaches them
through the prop/prefs chain, not through this container. -->
<main data-uix-canvas dir="ltr" lang="en">
{@render children?.()}
</main>
</div>

@ -23,7 +23,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
let topOffset = $state(8);
@ -208,7 +212,7 @@
(<code>border-inline-start</code>, <code>padding-inline-start</code>), so RTL flips it to
the inline-end side and mirrors the indent.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -158,7 +158,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const eidosSnippet = $derived(
@ -447,7 +451,7 @@
<p data-uix-section-desc>
Foundation knobs applied to the stage. Dark mode is the key check for the orb tints.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -101,7 +101,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -406,7 +410,7 @@
what does <em>not</em> change: the symbol's ink and quiet zone hold their tone in dark mode by
design — density, scaling and mode reach the DownloadTrigger and the frame, never the bars' contrast.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -70,7 +70,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -337,7 +341,7 @@
These retint / rescale every component identically — change one and watch the preview.
Radius / shape (component props) live in the Live tab.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
<p data-uix-section-desc style="margin-top: var(--uix-space-4);">
<strong>Theme vs prop.</strong> Here you change the <em>system</em> (mode, density, zoom,
border). To change this component's own color you use its <code>color</code> prop (Live).

@ -65,7 +65,11 @@
let maxDays = $state(4);
let weekStartsOn = $state<WeekStartControl>('1');
let locale = $state('en-US');
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let size = $state<CalendarSize>('md');
let variant = $state<CalendarVariant>('surface');
let color = $state<CalendarColor>('primary');
@ -269,7 +273,7 @@
selectionType === 'multiple' && ` maxDays={${maxDays}}`,
weekStartsOn !== 'locale' && ` weekStartsOn={${weekStartsOn}}`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
' weekdayFormat="short"',
' isDateUnavailable={(date) => date.month === 5 && (date.day === 17 || date.day === 24)}',
' isDateHoliday={(date) => date.month === 5 && date.day === 1}',
@ -358,7 +362,7 @@
selectionType === 'multiple' && ` maxDays={${maxDays}}`,
weekStartsOn !== 'locale' && ` weekStartsOn={${weekStartsOn}}`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
' weekdayFormat="short"',
' isDateUnavailable={(date) => date.month === 5 && (date.day === 17 || date.day === 24)}',
' isDateHoliday={(date) => date.month === 5 && date.day === 1}',
@ -723,11 +727,11 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as value}
{#each ['auto', 'ltr', 'rtl'] as value}
<button
data-uix-chip
data-active={dir === value}
onclick={() => (dir = value as Direction)}>{value}</button
data-active={dirMode === value}
onclick={() => (dirMode = value as typeof dirMode)}>{value}</button
>
{/each}
</span>

@ -24,7 +24,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
// ── Live state ───────────────────────────────────────────────────────
@ -263,7 +267,7 @@
with zero recipe overrides; RTL flips the accent edge (logical
<code>border-inline-start</code>).
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -58,7 +58,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -371,7 +375,7 @@
Foundation knobs applied live. RTL mirrors the whole surface (context accent bar,
attach/send sides) — every axis in the recipe is logical.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -58,7 +58,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
let stageRef = $state<HTMLElement | null>(null);
@ -205,7 +209,7 @@
Foundation knobs applied live. RTL mirrors the whole row (avatar side,
meta side) — all axes are logical.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -108,7 +108,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -396,7 +400,7 @@
Foundation knobs applied live. RTL mirrors the whole conversation (bubble sides,
accent bars, the floating actions) — all axes are logical.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -102,7 +102,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -473,7 +477,7 @@
the bubble stack side and the accent bars are LOGICAL — flip to RTL and the layout
mirrors correctly.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -38,7 +38,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -253,7 +257,7 @@
Foundation knobs applied live. The row is a logical flex line — RTL mirrors dots and
label order for free.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -45,7 +45,11 @@
let disabled = $state(false);
let readonly = $state(false);
let invalid = $state(false);
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
// Soma · Input
let name = $state('brand');
let ariaLabel = $state('Brand color');
@ -155,7 +159,7 @@
invalid && ' invalid',
!enableAlpha && ' enableAlpha={false}',
allowedFormats.length !== 3 && ` allowedFormats={${JSON.stringify(allowedFormats)}}`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
readonlySegments.length > 0 &&
` readonlySegments={${JSON.stringify(readonlySegments)}}`,
'>',
@ -198,7 +202,7 @@
invalid && ' invalid',
!enableAlpha && ' enableAlpha={false}',
allowedFormats.length !== 3 && ` allowedFormats={${JSON.stringify(allowedFormats)}}`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
readonlySegments.length > 0 &&
` readonlySegments={${JSON.stringify(readonlySegments)}}`,
'>',
@ -461,8 +465,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as opt (opt)}
<button data-uix-chip data-active={dir === opt} onclick={() => (dir = opt as Direction)}>{opt}</button>
{#each ['auto', 'ltr', 'rtl'] as opt (opt)}
<button data-uix-chip data-active={dirMode === opt} onclick={() => (dirMode = opt as typeof dirMode)}>{opt}</button>
{/each}
</span>
</label>

@ -12,7 +12,11 @@
// ── Live state ────────────────────────────────────────────────────────
// Soma · Provider
let open = $state(false);
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
// Soma · Content (floating layer)
let side = $state<'top' | 'right' | 'bottom' | 'left'>('bottom');
let sideOffset = $state<number | undefined>(undefined);
@ -94,7 +98,7 @@
'',
'<ContextMenu.Provider',
' bind:open',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
'>',
` <ContextMenu.Trigger>${triggerText}</ContextMenu.Trigger>`,
' <ContextMenu.Content',
@ -150,7 +154,7 @@
'',
'<ContextMenu',
' bind:open',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
'>',
` <ContextMenu.Trigger>${triggerText}</ContextMenu.Trigger>`,
' <ContextMenu.Content',
@ -356,8 +360,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d (d)}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as 'ltr' | 'rtl')}>{d}</button>
{#each ['auto', 'ltr', 'rtl'] as d (d)}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}>{d}</button>
{/each}
</span>
</label>

@ -34,7 +34,11 @@
let readonly = $state(false);
let required = $state(false);
let invalid = $state(false);
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let size = $state<CssFieldSize>('md');
let variant = $state<CssFieldVariant>('surface');
let color = $state<CssFieldColor>('primary');
@ -393,8 +397,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as Direction)}
{#each ['auto', 'ltr', 'rtl'] as d}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}
>{d}</button
>
{/each}

@ -51,7 +51,11 @@
let bounds = $state(true);
let readonlyYear = $state(false);
let locale = $state('en-US');
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let hourCycle = $state<HourCycle>(24);
let hideTimeZone = $state(false);
let size = $state<DateFieldSize>('md');
@ -211,7 +215,7 @@
bounds && ' maxValue={new CalendarDate(2026, 12, 31)}',
granularity !== 'day' && ` granularity="${granularity}"`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
showsTimeSegments && hourCycle !== 24 && ` hourCycle={${hourCycle}}`,
readonlyYear && " readonlySegments={['year']}",
valueProfile === 'zoned' && hideTimeZone && ' hideTimeZone',
@ -253,7 +257,7 @@
bounds && ' maxValue={new CalendarDate(2026, 12, 31)}',
granularity !== 'day' && ` granularity="${granularity}"`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
showsTimeSegments && hourCycle !== 24 && ` hourCycle={${hourCycle}}`,
readonlyYear && " readonlySegments={['year']}",
valueProfile === 'zoned' && hideTimeZone && ' hideTimeZone',
@ -436,11 +440,11 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as value}
{#each ['auto', 'ltr', 'rtl'] as value}
<button
data-uix-chip
data-active={dir === value}
onclick={() => (dir = value as Direction)}>{value}</button
data-active={dirMode === value}
onclick={() => (dirMode = value as typeof dirMode)}>{value}</button
>
{/each}
</span>

@ -114,7 +114,11 @@
// (en-US → 12h AM/PM, es-ES / fr-FR → 24h) — for both the input and the
// composed <DatePicker.Time />. No manual hourCycle plumbing needed.
let locale = $state('en-US');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let size = $state<DatePickerSize>('md');
let variant = $state<DatePickerVariant>('surface');
let color = $state<DatePickerColor>('primary');
@ -324,7 +328,7 @@
showWeekNumbers && ' showWeekNumbers',
weekStartsOn !== 'locale' && ` weekStartsOn={${weekStartsOn}}`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
blockWeekends && ' isDateDisabled={(date) => date.toDate("UTC").getUTCDay() % 6 === 0}',
markBookedUnavailable && ' isDateUnavailable={(date) => date.day === 22}',
' weekdayFormat="short"',
@ -408,7 +412,7 @@
showWeekNumbers && ' showWeekNumbers',
weekStartsOn !== 'locale' && ` weekStartsOn={${weekStartsOn}}`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
blockWeekends && ' isDateDisabled={(date) => date.toDate("UTC").getUTCDay() % 6 === 0}',
markBookedUnavailable && ' isDateUnavailable={(date) => date.day === 22}',
' weekdayFormat="short"',
@ -674,8 +678,8 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as value}
<button data-uix-chip data-active={dir === value} onclick={() => (dir = value as 'ltr' | 'rtl')}>{value}</button>
{#each ['auto', 'ltr', 'rtl'] as value}
<button data-uix-chip data-active={dirMode === value} onclick={() => (dirMode = value as typeof dirMode)}>{value}</button>
{/each}
</span>
</label>

@ -62,7 +62,11 @@
let readonlyEndMonth = $state(false);
let readonlyEndDay = $state(false);
let locale = $state('en-US');
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let hourCycle = $state<HourCycle>(24);
let hideTimeZone = $state(false);
let size = $state<DateRangeFieldSize>('md');
@ -245,7 +249,7 @@
' bind:value',
kind !== 'day' && ` kind="${kind}"`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
hourCycle !== 24 && ` hourCycle={${hourCycle}}`,
hideTimeZone && ' hideTimeZone',
disabled && ' disabled',
@ -297,7 +301,7 @@
size !== 'md' && ` size="${size}"`,
kind !== 'day' && ` kind="${kind}"`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
hourCycle !== 24 && ` hourCycle={${hourCycle}}`,
hideTimeZone && ' hideTimeZone',
disabled && ' disabled',
@ -536,7 +540,7 @@
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each directions as opt}
<button data-uix-chip data-active={dir === opt} onclick={() => (dir = opt)}>{opt}</button>
<button data-uix-chip data-active={dirMode === opt} onclick={() => (dirMode = opt as typeof dirMode)}>{opt}</button>
{/each}
</span>
</label>

@ -106,7 +106,11 @@
let numberOfMonths = $state(2);
let weekStartsOn = $state<WeekStartControl>('1');
let locale = $state('en-US');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let required = $state(true);
let disabled = $state(false);
let readonly = $state(false);
@ -730,8 +734,8 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as value}
<button data-uix-chip data-active={dir === value} onclick={() => (dir = value as 'ltr' | 'rtl')}>{value}</button>
{#each ['auto', 'ltr', 'rtl'] as value}
<button data-uix-chip data-active={dirMode === value} onclick={() => (dirMode = value as typeof dirMode)}>{value}</button>
{/each}
</span>
</label>

@ -12,7 +12,11 @@
// ── Live state ────────────────────────────────────────────────────────
// Soma · Provider
let open = $state(false);
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let accessibleWhenDisabled = $state(true);
// Soma · Content
let side = $state<'top' | 'right' | 'bottom' | 'left'>('bottom');
@ -107,7 +111,7 @@
'',
'<DropdownMenu.Provider',
' bind:open',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
!accessibleWhenDisabled && ' accessibleWhenDisabled={false}',
'>',
` <DropdownMenu.Trigger>${triggerLabel}</DropdownMenu.Trigger>`,
@ -173,7 +177,7 @@
'',
'<DropdownMenu',
' bind:open',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
'>',
` <DropdownMenu.Trigger${triggerVariant !== 'surface' ? ` variant="${triggerVariant}"` : ''}${triggerSize !== 'md' ? ` size="${triggerSize}"` : ''}${triggerIntent !== 'neutral' ? ` intent="${triggerIntent}"` : ''}>${triggerLabel}</DropdownMenu.Trigger>`,
` <DropdownMenu.Content${menuSize !== 'md' ? ` size="${menuSize}"` : ''}${side !== 'bottom' ? ` side="${side}"` : ''}${align !== 'start' ? ` align="${align}"` : ''}>`,
@ -393,8 +397,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d (d)}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as 'ltr' | 'rtl')}>{d}</button>
{#each ['auto', 'ltr', 'rtl'] as d (d)}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}>{d}</button>
{/each}
</span>
</label>

@ -41,7 +41,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
let stageRef = $state<HTMLElement | null>(null);
@ -192,7 +196,7 @@
Foundation knobs applied live. RTL mirrors the grid navigation (arrow
Left/Right invert) — all axes are logical.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -30,7 +30,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
// ── Live state ───────────────────────────────────────────────────────
@ -312,7 +316,7 @@
EmptyState's typography rides the <code>--size-*</code> bundle, so density and scaling re-derive
it coherently.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -62,7 +62,11 @@
// ── i18n ─────────────────────────────────────────────────────────────
let locale = $state('en-US');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
const compiled = compileMorfo(feedMorfo);
const partsList = $derived([...compiled.parts.byKebab.values()]);
@ -113,7 +117,7 @@
'<Feed',
' {busy}',
' onLoadMore={loadMore}',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
totalItems !== undefined && ` totalItems={${totalItems}}`,
size !== 'md' && ` size="${size}"`,
variant !== 'surface' && ` variant="${variant}"`,
@ -271,8 +275,8 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as v (v)}
<button data-uix-chip data-active={dir === v} onclick={() => (dir = v as 'ltr' | 'rtl')}>{v}</button>
{#each ['auto', 'ltr', 'rtl'] as v (v)}
<button data-uix-chip data-active={dirMode === v} onclick={() => (dirMode = v as typeof dirMode)}>{v}</button>
{/each}
</span>
</label>

@ -102,7 +102,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -300,7 +304,7 @@
density / mode / scaling / border and watch the editor card respond. The gradient itself is
the user's data — it doesn't retint with the theme.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -106,7 +106,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let stageMode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -309,7 +313,7 @@
Foundation knobs applied to the stage. Open the picker and switch mode to preview the
popover in dark.
</p>
<SystemAxes bind:density bind:scaling bind:mode={stageMode} bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode={stageMode} bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -80,7 +80,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -399,7 +403,7 @@
the control bar is a <code>surface-overlay</code> glass that keeps the controls legible in both
themes (the amber accent is theme-stable).
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -18,7 +18,11 @@
// ── Live state ────────────────────────────────────────────────────────
let activeMenu = $state(''); // which menu is open
let size = $state<MenubarSize>('md');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let loop = $state(true);
// Bound state from inner items
@ -115,7 +119,7 @@
'',
'<Menubar.Provider',
' bind:value',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
!loop && ' loop={false}',
'>',
' <Menubar.Menu value="file">',
@ -142,7 +146,7 @@
'<Menubar',
' bind:value',
size !== 'md' && ` size="${size}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
!loop && ' loop={false}',
'>',
' <Menubar.Menu value="file">',
@ -451,8 +455,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d (d)}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as typeof dir)}>{d}</button>
{#each ['auto', 'ltr', 'rtl'] as d (d)}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}>{d}</button>
{/each}
</span>
</label>

@ -31,7 +31,11 @@
let readonly = $state(false);
let monthFormat = $state<'long' | 'short' | 'narrow'>('short');
let locale = $state('en-US');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let size = $state<MonthGridSize>('md');
let variant = $state<MonthGridVariant>('surface');
let color = $state<MonthGridColor>('primary');
@ -106,7 +110,7 @@
maxValue && ` maxValue={{ year: ${maxValue.year}, month: ${maxValue.month} }}`,
monthFormat !== 'short' && ` monthFormat="${monthFormat}"`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
'>',
' {#snippet children({ months })}',
' <MonthGrid.Header>',
@ -147,7 +151,7 @@
maxValue && ` maxValue={{ year: ${maxValue.year}, month: ${maxValue.month} }}`,
monthFormat !== 'short' && ` monthFormat="${monthFormat}"`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
'>',
' {#snippet children({ months })}',
' <MonthGrid.Header>',
@ -324,8 +328,8 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as v (v)}
<button data-uix-chip data-active={dir === v} onclick={() => (dir = v as 'ltr' | 'rtl')}>{v}</button>
{#each ['auto', 'ltr', 'rtl'] as v (v)}
<button data-uix-chip data-active={dirMode === v} onclick={() => (dirMode = v as typeof dirMode)}>{v}</button>
{/each}
</span>
</label>

@ -45,7 +45,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let sysMode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -368,7 +372,7 @@
<strong>dark</strong> mode — the daylight band keeps its fixed-tone sky (it depicts a
physical thing), while the panel chrome retints with the theme.
</p>
<SystemAxes bind:density bind:scaling bind:mode={sysMode} bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode={sysMode} bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -23,7 +23,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
// The real docs map (43 nodes, 3 levels) — the tree the docs-shell will
@ -483,7 +487,7 @@
mirrors as well; only the rotation is physical, so under <code>[dir='rtl']</code> the two
states swap (closed points to the inline-end, open still points down).
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -33,7 +33,11 @@
let readonly = $state(false);
let required = $state(false);
let invalid = $state(false);
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let size = $state<NumberFieldSize>('md');
let variant = $state<NumberFieldVariant>('surface');
let color = $state<NumberFieldColor>('primary');
@ -548,8 +552,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as Direction)}
{#each ['auto', 'ltr', 'rtl'] as d}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}
>{d}</button
>
{/each}

@ -132,7 +132,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const stageStyle = $derived(`--scaling: ${scaling}; --border-width: ${borderWidth}px;`);
@ -698,7 +702,7 @@
<p data-uix-section-desc>
Foundation knobs that are NOT component props, applied live to the stage above.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -25,7 +25,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
const sizes: ProseSize[] = ['sm', 'md', 'lg'];
@ -248,7 +252,7 @@ for (const el of document) style(el);</code></pre>
the whole document with zero overrides (no tailwind-style <code>prose-invert</code> parallel stylesheet).
Logical properties throughout → RTL flips list indents and blockquote accents.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -32,7 +32,11 @@
let orientation = $state<Orientation>('vertical');
let columns = $state<number | undefined>(undefined);
let loop = $state(false);
let dir = $state<'ltr' | 'rtl'>((params.get('perm.dir') as 'ltr' | 'rtl' | null) ?? 'ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let nameAttr = $state('plan');
let labelText = $state('Choose a plan');
let useCustomIcon = $state(false);
@ -116,7 +120,7 @@
invalid && ' invalid',
orientation !== 'vertical' && ` orientation="${orientation}"`,
loop && ' loop',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
nameAttr && ` name="${nameAttr}"`,
'>',
' <RadioGroup.Label>Choose a plan</RadioGroup.Label>',
@ -344,8 +348,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as 'ltr' | 'rtl')}>{d}</button>
{#each ['auto', 'ltr', 'rtl'] as d}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}>{d}</button>
{/each}
</span>
</label>

@ -56,7 +56,11 @@
let weekStartsOn = $state<WeekStartControl>('1');
let locale = $state('en-US');
type Direction = 'ltr' | 'rtl';
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let size = $state<RangeCalendarSize>('md');
let variant = $state<RangeCalendarVariant>('surface');
let color = $state<RangeCalendarColor>('primary');
@ -470,8 +474,8 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dir === value} onclick={() => (dir = value as Direction)}>{value}</button>
{#each ['auto', 'ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dirMode === value} onclick={() => (dirMode = value as typeof dirMode)}>{value}</button>
{/each}
</span>
</label>

@ -22,7 +22,11 @@
let required = $state(false);
let invalid = $state(false);
let orientation = $state<Orientation>('horizontal');
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let size = $state<RatingGroupSize>('md');
const compiled = compileMorfo(ratingGroupMorfo);
@ -236,8 +240,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as Direction)}
{#each ['auto', 'ltr', 'rtl'] as d}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}
>{d}</button
>
{/each}

@ -24,7 +24,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
// ── Live state ───────────────────────────────────────────────────────
@ -292,7 +296,7 @@
Foundation knobs applied to the stage. Result's typography reads fixed bundle sizes
(page-level surface), so density/scaling re-derive them coherently.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -25,7 +25,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
// The component's own props — every one of them a live control.
@ -413,7 +417,7 @@
changed a foundation knob. Density and scaling do reach the interior — row height, paddings,
gaps and the rail's own thickness all ride the spacing scale.
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -31,7 +31,11 @@
let orientation = $state<Orientation>(
params.get('perm.orientation') === 'vertical' ? 'vertical' : 'horizontal'
);
let dir = $state<Direction>(params.get('perm.dir') === 'rtl' ? 'rtl' : 'ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let linear = $state(false);
let size = $state<StepperSize>('md');
let variant = $state<StepperVariant>('soft');
@ -155,7 +159,7 @@
'<Stepper',
' bind:value',
` orientation="${orientation}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
linear && ' linear',
size !== 'md' && ` size="${size}"`,
variant !== 'soft' && ` variant="${variant}"`,
@ -343,8 +347,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as Direction)}>
{#each ['auto', 'ltr', 'rtl'] as d}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}>
{d}
</button>
{/each}

@ -24,7 +24,11 @@
let density = $state('comfortable');
let scaling = $state(1);
let mode = $state<'inherit' | 'light' | 'dark'>('inherit');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let borderWidth = $state(1);
// ── Live state ───────────────────────────────────────────────────────
@ -248,7 +252,7 @@
physical <code>top</code> / <code>bottom</code> too, so RTL is unaffected (top/bottom are the
block axis, invariant under the inline flip).
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
<SystemAxes bind:density bind:scaling bind:mode bind:dir={dirMode} bind:borderWidth />
</section>
{/if}

@ -73,7 +73,11 @@
// ── i18n controls (canonical pattern) ────────────────────────────────
let locale = $state('en-US');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
// ── Soma feature controls ────────────────────────────────────────────
// Pagination + filter + pinning are mutable via the imperative API.
@ -204,7 +208,7 @@
' {table}',
" label=\"Team members\"",
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
size !== 'md' && ` size="${size}"`,
variant !== 'surface' && ` variant="${variant}"`,
color !== 'primary' && ` color="${color}"`,
@ -488,8 +492,8 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dir === value} onclick={() => (dir = value as 'ltr' | 'rtl')}>{value}</button>
{#each ['auto', 'ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dirMode === value} onclick={() => (dirMode = value as typeof dirMode)}>{value}</button>
{/each}
</span>
</label>

@ -28,7 +28,11 @@
);
let activationMode = $state<ActivationMode>('automatic');
let loop = $state(true);
let dir = $state<'ltr' | 'rtl'>((params.get('perm.dir') as 'ltr' | 'rtl' | null) ?? 'ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let disabled = $state(params.get('perm.disabled') === 'true');
let fitted = $state(false);
let scrollable = $state(false);
@ -95,7 +99,7 @@
orientation !== 'horizontal' && ` orientation="${orientation}"`,
activationMode !== 'automatic' && ` activationMode="${activationMode}"`,
!loop && ' loop={false}',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
disabled && ' disabled',
'>',
' <Tabs.List' + (listLabel ? ` aria-label="${listLabel}"` : '') + '>',
@ -127,7 +131,7 @@
orientation !== 'horizontal' && ` orientation="${orientation}"`,
activationMode !== 'automatic' && ` activationMode="${activationMode}"`,
!loop && ' loop={false}',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
disabled && ' disabled',
fitted && ' fitted',
scrollable && ' scrollable',
@ -297,8 +301,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as 'ltr' | 'rtl')}>{d}</button>
{#each ['auto', 'ltr', 'rtl'] as d}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}>{d}</button>
{/each}
</span>
</label>

@ -55,7 +55,11 @@
let disabled = $state(false);
let readonly = $state(false);
let locale = $state('en-US');
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
// Soma · Input
let name = $state('start');
let ariaLabel = $state('Start time');
@ -170,7 +174,7 @@
hourCycle !== 24 && ` hourCycle={${hourCycle}}`,
valueProfile === 'zoned' && hideTimeZone && ' hideTimeZone',
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
readonlySegments.length > 0 &&
` readonlySegments={${JSON.stringify(readonlySegments)}}`,
'>',
@ -217,7 +221,7 @@
hourCycle !== 24 && ` hourCycle={${hourCycle}}`,
valueProfile === 'zoned' && hideTimeZone && ' hideTimeZone',
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
readonlySegments.length > 0 &&
` readonlySegments={${JSON.stringify(readonlySegments)}}`,
'>',
@ -408,8 +412,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as opt (opt)}
<button data-uix-chip data-active={dir === opt} onclick={() => (dir = opt as Direction)}>{opt}</button>
{#each ['auto', 'ltr', 'rtl'] as opt (opt)}
<button data-uix-chip data-active={dirMode === opt} onclick={() => (dirMode = opt as typeof dirMode)}>{opt}</button>
{/each}
</span>
</label>

@ -46,7 +46,11 @@
let disabled = $state(false);
let readonly = $state(false);
let locale = $state('en-US');
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
// Composition
let showLabel = $state(true);
let labelText = $state('Working hours');
@ -108,7 +112,7 @@
granularity !== 'minute' && ` granularity="${granularity}"`,
hourCycle !== 24 && ` hourCycle={${hourCycle}}`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
'>',
showLabel && ` <TimeRangeField.Label>${labelText}</TimeRangeField.Label>`,
' <TimeRangeField.Input type="start">',
@ -273,8 +277,8 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dir === value} onclick={() => (dir = value as Direction)}>{value}</button>
{#each ['auto', 'ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dirMode === value} onclick={() => (dirMode = value as typeof dirMode)}>{value}</button>
{/each}
</span>
</label>

@ -15,7 +15,11 @@
let tab = $state<Tab>('live');
let orientation = $state<Orientation>('horizontal');
let loop = $state(true);
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let size = $state<ToolbarSize>('md');
let variant = $state<ToolbarVariant>('surface');
let groupType = $state<GroupType>('multiple');
@ -70,7 +74,7 @@
` aria-label="${labelText}"`,
orientation !== 'horizontal' && ` orientation="${orientation}"`,
!loop && ' loop={false}',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
'>',
' <Toolbar.Button>Undo</Toolbar.Button>',
' <Toolbar.Button>Redo</Toolbar.Button>',
@ -99,7 +103,7 @@
variant !== 'surface' && ` variant="${variant}"`,
orientation !== 'horizontal' && ` orientation="${orientation}"`,
!loop && ' loop={false}',
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
'>',
' <Toolbar.Button>Undo</Toolbar.Button>',
' <Toolbar.Button>Redo</Toolbar.Button>',
@ -226,8 +230,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as Direction)}
{#each ['auto', 'ltr', 'rtl'] as d}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}
>{d}</button
>
{/each}

@ -40,7 +40,11 @@
let hideWhenDetached = $state(true);
let forceMount = $state(false);
let ariaLabel = $state('');
let dir = $state<'' | 'ltr' | 'rtl'>((params.get('perm.dir') as 'ltr' | 'rtl' | null) ?? '');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
// ── Arrow state ───────────────────────────────────────────────────────
let showArrow = $state(true);
@ -113,7 +117,7 @@
!hideWhenDetached && ' hideWhenDetached={false}',
forceMount && ' forceMount',
ariaLabel && ` ariaLabel="${ariaLabel}"`,
dir && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
' >',
` ${contentText}`,
showArrow &&
@ -421,11 +425,11 @@
<label data-uix-control>
<span data-uix-control-label>Content · dir</span>
<span data-uix-chips role="radiogroup">
{#each ['', 'ltr', 'rtl'] as d}
{#each ['auto', 'ltr', 'rtl'] as d}
<button
data-uix-chip
data-active={dir === d}
onclick={() => (dir = d as '' | 'ltr' | 'rtl')}>{d || 'inherit'}</button
data-active={dirMode === d}
onclick={() => (dirMode = d as typeof dirMode)}>{d}</button
>
{/each}
</span>

@ -95,7 +95,11 @@
// ── i18n controls ────────────────────────────────────────────────────
let locale = $state('en-US');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
// ── Soma feature controls ────────────────────────────────────────────
let selectionMode = $state<TreeGridSelectionMode>('single');
@ -163,7 +167,7 @@
' bind:expanded',
' bind:value',
selectionMode !== 'single' && ` selectionMode="${selectionMode}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
loop && ' loop',
!typeahead && ' typeahead={false}',
size !== 'md' && ` size="${size}"`,
@ -351,8 +355,8 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as v (v)}
<button data-uix-chip data-active={dir === v} onclick={() => (dir = v as 'ltr' | 'rtl')}>{v}</button>
{#each ['auto', 'ltr', 'rtl'] as v (v)}
<button data-uix-chip data-active={dirMode === v} onclick={() => (dirMode = v as typeof dirMode)}>{v}</button>
{/each}
</span>
</label>

@ -81,7 +81,11 @@
// ── i18n controls ────────────────────────────────────────────────────
let locale = $state('en-US');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
// ── Soma feature controls ────────────────────────────────────────────
let selectionMode = $state<TreeViewSelectionMode>('single');
@ -140,7 +144,7 @@
' bind:expandedValue',
' bind:selectedValue',
selectionMode !== 'single' && ` selectionMode="${selectionMode}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
!expandOnClick && ' expandOnClick={false}',
!typeahead && ' typeahead={false}',
size !== 'md' && ` size="${size}"`,
@ -312,8 +316,8 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dir === value} onclick={() => (dir = value as 'ltr' | 'rtl')}>{value}</button>
{#each ['auto', 'ltr', 'rtl'] as value (value)}
<button data-uix-chip data-active={dirMode === value} onclick={() => (dirMode = value as typeof dirMode)}>{value}</button>
{/each}
</span>
</label>

@ -38,7 +38,11 @@
let disabled = $state(false);
let showBuffered = $state(false);
let buffered = $state(66);
let dir = $state<Direction>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let size = $state<WaveformSize>('md');
let peaks = $state<number[]>([]);
@ -359,8 +363,8 @@
<label data-uix-control>
<span data-uix-control-label>dir</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as Direction)}
{#each ['auto', 'ltr', 'rtl'] as d}
<button data-uix-chip data-active={dirMode === d} onclick={() => (dirMode = d as typeof dirMode)}
>{d}</button
>
{/each}

@ -31,7 +31,11 @@
let readonly = $state(false);
let decadeSize = $state(12);
let locale = $state('en-US');
let dir = $state<'ltr' | 'rtl'>('ltr');
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
// `auto` passes no `dir` prop, so the component resolves the documented
// chain `prop -> soma.prefs.getDir() -> 'ltr'` and follows the shell's
// direction switch — what a real consumer gets. ltr/rtl force an override.
const dir = $derived(dirMode === 'auto' ? undefined : dirMode);
let size = $state<YearGridSize>('md');
let variant = $state<YearGridVariant>('surface');
let color = $state<YearGridColor>('primary');
@ -106,7 +110,7 @@
maxValue && ` maxValue={{ year: ${maxValue.year} }}`,
decadeSize !== 12 && ` decadeSize={${decadeSize}}`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
'>',
' {#snippet children({ years })}',
' <YearGrid.Header>',
@ -147,7 +151,7 @@
maxValue && ` maxValue={{ year: ${maxValue.year} }}`,
decadeSize !== 12 && ` decadeSize={${decadeSize}}`,
locale !== 'en-US' && ` locale="${locale}"`,
dir !== 'ltr' && ` dir="${dir}"`,
dirMode !== 'auto' && ` dir="${dir}"`,
'>',
' {#snippet children({ years })}',
' <YearGrid.Header>',
@ -321,8 +325,8 @@
<label data-uix-control>
<span data-uix-control-label>direction</span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as v (v)}
<button data-uix-chip data-active={dir === v} onclick={() => (dir = v as 'ltr' | 'rtl')}>{v}</button>
{#each ['auto', 'ltr', 'rtl'] as v (v)}
<button data-uix-chip data-active={dirMode === v} onclick={() => (dirMode = v as typeof dirMode)}>{v}</button>
{/each}
</span>
</label>

@ -13,13 +13,13 @@
density = $bindable('comfortable'),
scaling = $bindable(1),
mode = $bindable('inherit'),
dir = $bindable('ltr'),
dir = $bindable('auto'),
borderWidth = $bindable(1)
}: {
density?: string;
scaling?: number;
mode?: 'inherit' | 'light' | 'dark';
dir?: 'ltr' | 'rtl';
dir?: 'auto' | 'ltr' | 'rtl';
borderWidth?: number;
} = $props();
</script>
@ -49,9 +49,9 @@
</label>
<label data-uix-control>
<span data-uix-control-label>direction <span data-uix-control-hint>RTL flips logical props</span></span>
<span data-uix-control-label>direction <span data-uix-control-hint>auto = inherit from prefs</span></span>
<span data-uix-chips role="radiogroup">
{#each ['ltr', 'rtl'] as d}
{#each ['auto', 'ltr', 'rtl'] as d}
<button data-uix-chip data-active={dir === d} onclick={() => (dir = d as typeof dir)}>{d}</button>
{/each}
</span>

Loading…
Cancel
Save

Powered by TurnKey Linux.