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.
621 lines
23 KiB
621 lines
23 KiB
<script lang="ts">
|
|
import {
|
|
Separator,
|
|
type SeparatorOrientation,
|
|
type SeparatorProps
|
|
} from '$uix/eidos/components/separator';
|
|
import { compileMorfo } from '$uix/morfo';
|
|
import { separatorMorfo } from '@/uix/morfo/components/separator';
|
|
|
|
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
|
|
type TraceEntry = { event: string; family: string; intent?: string; at: number };
|
|
let tab = $state<Tab>('live');
|
|
let trace = $state<TraceEntry[]>([]);
|
|
let stageRef = $state<HTMLElement | null>(null);
|
|
|
|
// ── Live state ───────────────────────────────────────────────────────
|
|
const orientations: SeparatorOrientation[] = ['horizontal', 'vertical'];
|
|
let orientation = $state<SeparatorOrientation>('horizontal');
|
|
let decorative = $state<boolean>(false);
|
|
type Demo = 'stacked' | 'toolbar' | 'menu';
|
|
const demoOptions = ['stacked', 'toolbar', 'menu'] as const;
|
|
let demo = $state<Demo>('stacked');
|
|
let thickness = $state<number>(1);
|
|
type ColorOption = 'default' | 'subtle' | 'strong';
|
|
const colorOptions = ['default', 'subtle', 'strong'] as const;
|
|
let color = $state<ColorOption>('default');
|
|
|
|
const separatorProps = $derived<Partial<SeparatorProps>>({
|
|
orientation,
|
|
decorative
|
|
});
|
|
|
|
const colorVar = $derived(
|
|
color === 'default'
|
|
? 'var(--color-border-default)'
|
|
: color === 'subtle'
|
|
? 'var(--color-border-subtle)'
|
|
: 'var(--color-border-strong)'
|
|
);
|
|
|
|
// ── Compiled morfo ───────────────────────────────────────────────────
|
|
const compiled = compileMorfo(separatorMorfo);
|
|
const partsList = $derived([...compiled.parts.byKebab.values()]);
|
|
const events = $derived([...compiled.actions.byName.values()]);
|
|
|
|
$effect(() => {
|
|
const el = stageRef;
|
|
if (!el) return;
|
|
const obs = new MutationObserver((mutations) => {
|
|
for (const m of mutations) {
|
|
if (m.attributeName !== 'data-event') continue;
|
|
const target = m.target as Element;
|
|
const ev = target.getAttribute('data-event');
|
|
if (!ev) continue;
|
|
trace = [
|
|
{
|
|
event: ev,
|
|
family: target.getAttribute('data-event-family') ?? '—',
|
|
intent: target.getAttribute('data-event-intent') ?? undefined,
|
|
at: Date.now()
|
|
},
|
|
...trace
|
|
].slice(0, 6);
|
|
}
|
|
});
|
|
obs.observe(el, { attributes: true, subtree: true, attributeFilter: ['data-event'] });
|
|
return () => obs.disconnect();
|
|
});
|
|
|
|
// ── Code snippet ──────────────────────────────────────────────────────
|
|
const eidosSnippet = $derived(
|
|
[
|
|
"<script lang='ts'>",
|
|
" import { Separator } from '$uix/eidos/components/separator';",
|
|
'</' + 'script>',
|
|
'',
|
|
'<Separator',
|
|
orientation !== 'horizontal' && ` orientation="${orientation}"`,
|
|
decorative && ' decorative',
|
|
'/>'
|
|
]
|
|
.filter(Boolean)
|
|
.join('\n')
|
|
);
|
|
|
|
const styleOverride = $derived(
|
|
[
|
|
`--separator-thickness: ${thickness}px;`,
|
|
`--separator-color: ${colorVar};`
|
|
].join(' ')
|
|
);
|
|
</script>
|
|
|
|
<div data-uix-canvas-inner>
|
|
<header>
|
|
<div data-uix-eyebrow>Layout · Separator</div>
|
|
<h1 data-uix-page-title>Separator</h1>
|
|
<p data-uix-page-lede>
|
|
Visual divider between sections — a thin rule painted in the muted border color. The
|
|
<code>orientation</code> prop drives the geometry; <code>decorative</code> toggles between a
|
|
semantic
|
|
<code>role="separator"</code> + <code>aria-orientation</code> and a purely visual
|
|
<code>aria-hidden</code>. Eidos-native primitive: no soma backing, no semantic events. Logical
|
|
sizing (<code>inline-size</code> / <code>block-size</code>) so the rule mirrors automatically
|
|
in RTL.
|
|
</p>
|
|
<div data-uix-page-meta>
|
|
<span data-uix-meta-pill>
|
|
<span data-uix-meta-key>parts</span>{compiled.parts.order.length}
|
|
</span>
|
|
<span data-uix-meta-pill>
|
|
<span data-uix-meta-key>events</span>0
|
|
</span>
|
|
<span data-uix-meta-pill>
|
|
<span data-uix-meta-key>scope</span>eidos
|
|
</span>
|
|
<span data-uix-meta-pill>
|
|
<span data-uix-meta-key>role</span>{decorative ? '— (decorative)' : 'separator'}
|
|
</span>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Live preview always rendered -->
|
|
<div data-uix-stage>
|
|
<div data-uix-stage-area bind:this={stageRef}>
|
|
{#if demo === 'stacked'}
|
|
<div
|
|
style="display: flex; flex-direction: column; gap: var(--space-3); inline-size: 100%; max-inline-size: 32rem; padding: var(--space-4); border-radius: var(--radius-md); border: 1px dashed var(--color-border-default);"
|
|
>
|
|
<div style="color: var(--color-content-primary); font-weight: 600;">Account</div>
|
|
<div style="color: var(--color-content-secondary); font-size: var(--font-size-sm);">
|
|
Personal information and password.
|
|
</div>
|
|
<Separator {...separatorProps} style={styleOverride} />
|
|
<div style="color: var(--color-content-primary); font-weight: 600;">Notifications</div>
|
|
<div style="color: var(--color-content-secondary); font-size: var(--font-size-sm);">
|
|
Email, SMS, and in-app delivery preferences.
|
|
</div>
|
|
<Separator {...separatorProps} style={styleOverride} />
|
|
<div style="color: var(--color-content-primary); font-weight: 600;">Danger zone</div>
|
|
<div style="color: var(--color-content-secondary); font-size: var(--font-size-sm);">
|
|
Export data, delete account.
|
|
</div>
|
|
</div>
|
|
{:else if demo === 'toolbar'}
|
|
<div
|
|
style="display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); border: 1px dashed var(--color-border-default); background: var(--color-surface-default);"
|
|
>
|
|
<button style="padding: 0 var(--space-2); background: transparent; border: 0; cursor: pointer;">Bold</button>
|
|
<button style="padding: 0 var(--space-2); background: transparent; border: 0; cursor: pointer;">Italic</button>
|
|
<Separator orientation="vertical" {decorative} style={styleOverride} />
|
|
<button style="padding: 0 var(--space-2); background: transparent; border: 0; cursor: pointer;">Left</button>
|
|
<button style="padding: 0 var(--space-2); background: transparent; border: 0; cursor: pointer;">Center</button>
|
|
<button style="padding: 0 var(--space-2); background: transparent; border: 0; cursor: pointer;">Right</button>
|
|
<Separator orientation="vertical" {decorative} style={styleOverride} />
|
|
<button style="padding: 0 var(--space-2); background: transparent; border: 0; cursor: pointer;">Link</button>
|
|
</div>
|
|
{:else}
|
|
<div
|
|
style="inline-size: 100%; max-inline-size: 16rem; padding: var(--space-2); border-radius: var(--radius-md); border: 1px dashed var(--color-border-default); background: var(--color-surface-overlay);"
|
|
>
|
|
<button style="display: block; inline-size: 100%; text-align: start; padding: var(--space-2) var(--space-3); background: transparent; border: 0; cursor: pointer;">New file</button>
|
|
<button style="display: block; inline-size: 100%; text-align: start; padding: var(--space-2) var(--space-3); background: transparent; border: 0; cursor: pointer;">Open</button>
|
|
<button style="display: block; inline-size: 100%; text-align: start; padding: var(--space-2) var(--space-3); background: transparent; border: 0; cursor: pointer;">Open recent</button>
|
|
<Separator {...separatorProps} style={styleOverride} />
|
|
<button style="display: block; inline-size: 100%; text-align: start; padding: var(--space-2) var(--space-3); background: transparent; border: 0; cursor: pointer;">Save</button>
|
|
<button style="display: block; inline-size: 100%; text-align: start; padding: var(--space-2) var(--space-3); background: transparent; border: 0; cursor: pointer;">Save as…</button>
|
|
<Separator {...separatorProps} style={styleOverride} />
|
|
<button style="display: block; inline-size: 100%; text-align: start; padding: var(--space-2) var(--space-3); background: transparent; border: 0; cursor: pointer; color: var(--color-loss-text);">Quit</button>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
<div data-uix-stage-trace>
|
|
<span data-uix-stage-trace-key>trace</span>
|
|
<span>{trace.length === 0 ? 'no semantic events' : `${trace.length} event(s)`}</span>
|
|
<span style="color: var(--uix-text-faint)">·</span>
|
|
<span data-uix-stage-trace-key>orientation</span>
|
|
<span>{orientation}</span>
|
|
<span style="margin-inline-start: auto;">
|
|
<span data-uix-stage-trace-key>decorative</span> {String(decorative)} ·
|
|
<span data-uix-stage-trace-key>thickness</span> {thickness}px
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div data-uix-tabs role="tablist">
|
|
<button data-uix-tab data-active={tab === 'live'} onclick={() => (tab = 'live')}>Live</button>
|
|
<button data-uix-tab data-active={tab === 'api'} onclick={() => (tab = 'api')}>
|
|
API <span data-uix-tab-count>2</span>
|
|
</button>
|
|
<button data-uix-tab data-active={tab === 'morfo'} onclick={() => (tab = 'morfo')}>
|
|
<span data-uix-layer-badge="morfo">morfo</span>
|
|
<span data-uix-tab-count>{partsList.length}p · 0e</span>
|
|
</button>
|
|
<button data-uix-tab data-active={tab === 'sema'} onclick={() => (tab = 'sema')}>
|
|
<span data-uix-layer-badge="sema">sema</span>
|
|
<span data-uix-tab-count>{events.length}</span>
|
|
</button>
|
|
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
|
|
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
|
|
</div>
|
|
|
|
{#if tab === 'live'}
|
|
<section data-uix-section>
|
|
<h2 data-uix-section-title>Controls</h2>
|
|
<p data-uix-section-desc>
|
|
Separator is eidos-native — no <span data-uix-layer-badge="soma">soma</span> split. Two
|
|
public props drive geometry + accessibility; tokens override thickness and color via the
|
|
<code>style</code> escape.
|
|
</p>
|
|
|
|
<div data-uix-subsection-head>
|
|
<span data-uix-layer-badge="eidos">eidos</span> props · visual + a11y treatment
|
|
</div>
|
|
<div data-uix-controls>
|
|
<label data-uix-control>
|
|
<span data-uix-control-label>orientation</span>
|
|
<span data-uix-chips role="radiogroup">
|
|
{#each orientations as opt}
|
|
<button
|
|
data-uix-chip
|
|
data-active={orientation === opt}
|
|
onclick={() => (orientation = opt)}>{opt}</button
|
|
>
|
|
{/each}
|
|
</span>
|
|
</label>
|
|
<label data-uix-control>
|
|
<span data-uix-control-label>decorative</span>
|
|
<span data-uix-switch>
|
|
<input type="checkbox" bind:checked={decorative} />
|
|
<span data-uix-switch-label>{decorative ? 'on' : 'off'}</span>
|
|
</span>
|
|
</label>
|
|
<label data-uix-control>
|
|
<span data-uix-control-label
|
|
>thickness <span data-uix-control-hint
|
|
>pixels → <code>--separator-thickness</code></span
|
|
></span
|
|
>
|
|
<input type="range" min="1" max="6" step="1" bind:value={thickness} />
|
|
<span data-uix-control-value>{thickness}px</span>
|
|
</label>
|
|
<label data-uix-control>
|
|
<span data-uix-control-label
|
|
>color <span data-uix-control-hint
|
|
>token chip → <code>--separator-color</code></span
|
|
></span
|
|
>
|
|
<span data-uix-chips role="radiogroup">
|
|
{#each colorOptions as opt}
|
|
<button data-uix-chip data-active={color === opt} onclick={() => (color = opt)}
|
|
>{opt}</button
|
|
>
|
|
{/each}
|
|
</span>
|
|
</label>
|
|
<label data-uix-control>
|
|
<span data-uix-control-label>demo content</span>
|
|
<span data-uix-chips role="radiogroup">
|
|
{#each demoOptions as opt}
|
|
<button data-uix-chip data-active={demo === opt} onclick={() => (demo = opt)}
|
|
>{opt}</button
|
|
>
|
|
{/each}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- ── Code snippet (eidos-native — no soma layer) ──────────────── -->
|
|
<div data-uix-code>
|
|
<div data-uix-code-head>
|
|
<span data-uix-layer-badge="eidos">eidos</span>
|
|
<span>visual · orientation drives geometry; decorative drops the role</span>
|
|
<span data-uix-code-lang>svelte</span>
|
|
</div>
|
|
<pre><code>{eidosSnippet}</code></pre>
|
|
</div>
|
|
</section>
|
|
{/if}
|
|
|
|
{#if tab === 'api'}
|
|
<section data-uix-section>
|
|
<h2 data-uix-section-title>API reference</h2>
|
|
<p data-uix-section-desc>
|
|
Two public props on the root. Visual tuning (thickness, color) is exposed via CSS custom
|
|
properties (<code>--separator-thickness</code> / <code>--separator-color</code>) rather
|
|
than typed props — there is no consumer-driven reason to manufacture a typed surface
|
|
around a single CSS value.
|
|
</p>
|
|
|
|
<div data-uix-subsection-head>Separator props</div>
|
|
<div data-uix-table-wrap>
|
|
<table data-uix-table>
|
|
<thead><tr><th>Prop</th><th>Type</th><th>Default</th><th>Notes</th></tr></thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="name">orientation</td>
|
|
<td class="type">{orientations.join(' | ')}</td>
|
|
<td class="default">'horizontal'</td>
|
|
<td>
|
|
Layout axis. Maps to <code>inline-size</code> / <code>block-size</code>. Responsive
|
|
via <code>ResponsiveProp<SeparatorOrientation></code>.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name">decorative</td>
|
|
<td class="type">boolean</td>
|
|
<td class="default">false</td>
|
|
<td>
|
|
When true, drops <code>role="separator"</code> + <code>aria-orientation</code>
|
|
and adds <code>aria-hidden="true"</code>.
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div data-uix-subsection-head>CSS tokens</div>
|
|
<div data-uix-table-wrap>
|
|
<table data-uix-table>
|
|
<thead><tr><th>Token</th><th>Default</th><th>Notes</th></tr></thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="name">--separator-thickness</td>
|
|
<td class="type">var(--border-width)</td>
|
|
<td>Block-axis size on horizontal, inline-axis size on vertical.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name">--separator-color</td>
|
|
<td class="type">var(--color-border-default)</td>
|
|
<td>Background of the rule. Use one of the canonical border colors.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name">--separator-min-length</td>
|
|
<td class="type">var(--space-4)</td>
|
|
<td>Vertical separator's minimum block-axis size when stretched.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div data-uix-subsection-head>Reference comparison</div>
|
|
<div data-uix-table-wrap>
|
|
<table data-uix-table>
|
|
<thead>
|
|
<tr><th>Library</th><th>Closest equivalent</th><th>Difference</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="name">radix-primitives</td>
|
|
<td><code><Separator></code></td>
|
|
<td>
|
|
Identical contract — <code>orientation</code> + <code>decorative</code>. UIX adds
|
|
responsive orientation + logical CSS axes.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name">mantine</td>
|
|
<td><code><Divider></code></td>
|
|
<td>
|
|
Mantine adds <code>label</code> and <code>variant</code> (dashed / dotted); UIX
|
|
defers those to consumer composition + style overrides.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name">chakra-ui</td>
|
|
<td><code><Divider></code></td>
|
|
<td>
|
|
Chakra mixes the prop with the style-prop system; UIX keeps it pure layout — no
|
|
theme-coloured variants on the divider itself.
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
{/if}
|
|
|
|
{#if tab === 'morfo'}
|
|
<section data-uix-section>
|
|
<h2 data-uix-section-title>Morfo contract</h2>
|
|
<p data-uix-section-desc>
|
|
Source: <code>src/uix/morfo/components/separator.ts</code>. Eidos-native (scope:
|
|
<code>['eidos']</code>). The Provider part declares conditional ARIA so the same DOM
|
|
element flips between semantic and decorative modes based on the
|
|
<code>decorative</code> prop.
|
|
</p>
|
|
|
|
<div data-uix-table-wrap>
|
|
<table data-uix-table>
|
|
<thead><tr><th>Field</th><th>Value</th></tr></thead>
|
|
<tbody>
|
|
<tr><td class="name">name</td><td>{separatorMorfo.name}</td></tr>
|
|
<tr><td class="name">kebab</td><td><code>{separatorMorfo.kebab}</code></td></tr>
|
|
<tr><td class="name">scope</td><td>{separatorMorfo.scope.join(', ')}</td></tr>
|
|
<tr><td class="name">parts</td><td>{partsList.length}</td></tr>
|
|
<tr><td class="name">events</td><td>0</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div data-uix-subsection-head>Parts</div>
|
|
<div data-uix-table-wrap>
|
|
<table data-uix-table>
|
|
<thead>
|
|
<tr>
|
|
<th>kebab</th>
|
|
<th>marker</th>
|
|
<th>element</th>
|
|
<th>archetype</th>
|
|
<th>optional</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{#each partsList as part}
|
|
<tr>
|
|
<td class="name">{part.kebab}</td>
|
|
<td><code data-uix-part-marker>[{part.marker}]</code></td>
|
|
<td class="type"><{part.defaultElement}></td>
|
|
<td class="type">{part.archetype}</td>
|
|
<td class="default">{part.optional ? 'yes' : 'no'}</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{#each separatorMorfo.parts as rawPart}
|
|
{@const partAny = rawPart as unknown as {
|
|
kebab: string;
|
|
aria?: ReadonlyArray<{
|
|
attr: string;
|
|
value: { kind: string };
|
|
condition?: { when: string; prop?: string; part?: string };
|
|
severity?: string;
|
|
}>;
|
|
}}
|
|
{@const dataAttrs = compiled.contracts.dataAttrsByPart.get(partAny.kebab) ?? []}
|
|
{@const ariaAttrs = partAny.aria ?? []}
|
|
{#if dataAttrs.length || ariaAttrs.length}
|
|
<div data-uix-subsection-head>{partAny.kebab}</div>
|
|
{#if dataAttrs.length}
|
|
<div data-uix-table-wrap>
|
|
<table data-uix-table>
|
|
<thead><tr><th>data-attr</th><th>Values</th><th>Source kind</th></tr></thead>
|
|
<tbody>
|
|
{#each dataAttrs as attr}
|
|
<tr>
|
|
<td class="name">{attr.attr}</td>
|
|
<td class="type">{attr.values ? attr.values.join(' | ') : '—'}</td>
|
|
<td class="default"
|
|
>{'value' in attr && attr.value
|
|
? (attr as { value: { kind: string } }).value.kind
|
|
: '—'}</td
|
|
>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{/if}
|
|
{#if ariaAttrs.length}
|
|
<div data-uix-table-wrap style="margin-top: var(--uix-space-2);">
|
|
<table data-uix-table>
|
|
<thead>
|
|
<tr><th>aria-attr</th><th>Source kind</th><th>Condition</th><th>Severity</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{#each ariaAttrs as a}
|
|
<tr>
|
|
<td class="name">{a.attr}</td>
|
|
<td class="type">{a.value.kind}</td>
|
|
<td class="default"
|
|
>{a.condition
|
|
? `when ${a.condition.when}${a.condition.prop ? ` (${a.condition.prop})` : ''}${a.condition.part ? ` (${a.condition.part})` : ''}`
|
|
: 'always'}</td
|
|
>
|
|
<td class="default">{a.severity ?? 'required'}</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
{/each}
|
|
</section>
|
|
{/if}
|
|
|
|
{#if tab === 'sema'}
|
|
<section data-uix-section>
|
|
<h2 data-uix-section-title>
|
|
<span data-uix-layer-badge="sema">sema</span> · events
|
|
</h2>
|
|
<p data-uix-section-desc>
|
|
Separator declares no semantic events. As a passive layout primitive it paints a rule and
|
|
nothing else — no commit, no emerge, no contact. Components that animate sections (e.g.
|
|
Collapsible toggling visibility around a separator) own the relevant sema verbs in their
|
|
own morfo. The morfo is declared with <code>scope: ['eidos']</code> and the README's
|
|
Passive justification section makes the rationale explicit.
|
|
</p>
|
|
</section>
|
|
{/if}
|
|
|
|
{#if tab === 'recipe'}
|
|
<section data-uix-section>
|
|
<h2 data-uix-section-title>Eidos recipe</h2>
|
|
<p data-uix-section-desc>
|
|
Recipe lives in <code>src/uix/eidos/components/separator/separator.css</code>. The
|
|
<code>data-orientation</code> attribute switches between horizontal and vertical sizing;
|
|
logical CSS axes (<code>inline-size</code> / <code>block-size</code>) make the rule
|
|
mirror in RTL.
|
|
</p>
|
|
<div data-uix-table-wrap>
|
|
<table data-uix-table>
|
|
<thead><tr><th>Selector</th><th>Owner</th><th>Purpose</th></tr></thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="name"><code>[data-separator]</code></td>
|
|
<td><span data-uix-tag data-kind="morfo">morfo</span></td>
|
|
<td>Provider marker. Emitted by the wrapper on the single element.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name"
|
|
><code>{`[data-separator] { background: var(--separator-color, var(--color-border-default)); flex-shrink: 0; border: 0; }`}</code></td
|
|
>
|
|
<td><span data-uix-tag data-kind="eidos">eidos</span></td>
|
|
<td>Base painting + shrink protection inside flex parents.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name"
|
|
><code>[data-separator][data-orientation='horizontal']</code></td
|
|
>
|
|
<td><span data-uix-tag data-kind="eidos">eidos</span></td>
|
|
<td>
|
|
<code>inline-size: 100%</code> + <code>block-size:
|
|
var(--separator-thickness)</code>.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name"
|
|
><code>[data-separator][data-orientation='vertical']</code></td
|
|
>
|
|
<td><span data-uix-tag data-kind="eidos">eidos</span></td>
|
|
<td>
|
|
<code>inline-size: var(--separator-thickness)</code> +
|
|
<code>block-size: 100%</code> + <code>min-block-size</code> safeguard.
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
{/if}
|
|
|
|
{#if tab === 'a11y'}
|
|
<section data-uix-section>
|
|
<h2 data-uix-section-title>Accessibility</h2>
|
|
<p data-uix-section-desc>
|
|
The conditional ARIA contract honours the WAI-ARIA <code>separator</code> role when
|
|
<code>decorative</code> is false (default). When true the element drops the role and is
|
|
hidden from assistive tech, matching Radix Primitives' contract.
|
|
</p>
|
|
|
|
<div data-uix-subsection-head>Roles</div>
|
|
<div data-uix-table-wrap>
|
|
<table data-uix-table>
|
|
<thead><tr><th>Mode</th><th>Role</th><th>aria-orientation</th><th>aria-hidden</th></tr></thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="name">semantic (default)</td>
|
|
<td class="type">'separator'</td>
|
|
<td class="type">orientation</td>
|
|
<td class="default empty">—</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name">decorative</td>
|
|
<td class="default empty">—</td>
|
|
<td class="default empty">—</td>
|
|
<td class="type">'true'</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div data-uix-subsection-head>Other concerns</div>
|
|
<div data-uix-table-wrap>
|
|
<table data-uix-table>
|
|
<thead><tr><th>Concern</th><th>Contract</th></tr></thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="name">Keyboard</td>
|
|
<td>Not focusable. Tab order follows surrounding content.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name">Focus visible</td>
|
|
<td>No focus ring; the element never receives focus.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name">Color contrast</td>
|
|
<td>
|
|
Default color is <code>--color-border-default</code>, which meets 3:1 against
|
|
the canonical surface tokens in both light and dark themes.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="name">RTL</td>
|
|
<td>
|
|
Logical CSS axes mean the divider doesn't need to know about direction — the
|
|
horizontal rule fills the inline axis, which flips under <code>dir="rtl"</code>
|
|
automatically.
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
{/if}
|
|
</div>
|