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/web/routes/uix/components/format-number/+page.svelte

551 lines
24 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<script lang="ts">
import { FormatNumber } from '$uix/eidos/components/format-number';
import { compileMorfo } from '$uix/morfo';
import { formatNumberMorfo } from '@/uix/morfo/components/format-number';
import { getActiveUix } from '$active-uix';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
let tab = $state<Tab>('live');
const uix = getActiveUix();
const compiled = compileMorfo(formatNumberMorfo);
const partsList = $derived([...compiled.parts.byKebab.values()]);
// ── Live state ────────────────────────────────────────────────────────
// Demo owns its own `locale` state so the toolbar's global locale
// doesn't drag this demo around.
type DemoLocale = 'es-ES' | 'en-US' | 'en-GB' | 'de-DE' | 'fr-FR' | 'ja-JP' | 'ar-EG';
const DEMO_LOCALES: readonly DemoLocale[] = [
'es-ES',
'en-US',
'en-GB',
'de-DE',
'fr-FR',
'ja-JP',
'ar-EG'
];
let value = $state(1234567.89);
let demoLocale = $state<DemoLocale>('es-ES');
let formatStyle = $state<'decimal' | 'percent' | 'currency' | 'unit'>('decimal');
let currency = $state('EUR');
let unit = $state('byte');
let notation = $state<'standard' | 'compact' | 'scientific' | 'engineering'>('standard');
let compactDisplay = $state<'short' | 'long'>('short');
let signDisplay = $state<'auto' | 'never' | 'always' | 'exceptZero' | 'negative'>('auto');
let useGrouping = $state(true);
let minimumFractionDigits = $state<number | undefined>(undefined);
let maximumFractionDigits = $state<number | undefined>(undefined);
let valueAsPercent = $state(false);
const eidosSnippet = $derived(
[
"<script lang='ts'>",
" import { FormatNumber } from '$uix/eidos/components/format-number';",
'</' + 'script>',
'',
'<FormatNumber',
` value={${value}}`,
formatStyle !== 'decimal' && ` formatStyle="${formatStyle}"`,
formatStyle === 'currency' && ` currency="${currency}"`,
formatStyle === 'unit' && ` unit="${unit}"`,
notation !== 'standard' && ` notation="${notation}"`,
notation === 'compact' && compactDisplay !== 'short' && ` compactDisplay="${compactDisplay}"`,
signDisplay !== 'auto' && ` signDisplay="${signDisplay}"`,
!useGrouping && ' useGrouping={false}',
minimumFractionDigits !== undefined && ` minimumFractionDigits={${minimumFractionDigits}}`,
maximumFractionDigits !== undefined && ` maximumFractionDigits={${maximumFractionDigits}}`,
valueAsPercent && ' valueAsPercent',
'/>'
]
.filter(Boolean)
.join('\n')
);
</script>
<div data-uix-canvas-inner>
<header>
<div data-uix-eyebrow>Utilities · FormatNumber</div>
<h1 data-uix-page-title>FormatNumber</h1>
<p data-uix-page-lede>
Service component that wraps <code>getActiveUix().format.numbers</code> for locale-aware
number rendering. Same family as <a href="/uix/components/trans">&lt;Trans&gt;</a> —
grep-friendly wrapper around the active runtime API. Reactive to locale + currency
changes. Sin CSS recipe — sólo el marker <code>[data-format-number]</code> para tooling.
</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>styles</span>4
</span>
<span data-uix-meta-pill>
<span data-uix-meta-key>locale</span>
<code>{uix.langs.getLocale()}</code>
</span>
<span data-uix-meta-pill>
<span data-uix-meta-key>scope</span>eidos
</span>
</div>
</header>
<!-- Live preview -->
<div data-uix-stage>
<div data-uix-stage-area>
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--uix-space-6); inline-size: 100%;">
<div style="font-size: var(--uix-font-size-3xl); font-weight: 600;">
<FormatNumber
{value}
locale={demoLocale}
{formatStyle}
{currency}
{unit}
{notation}
{compactDisplay}
{signDisplay}
{useGrouping}
{minimumFractionDigits}
{maximumFractionDigits}
{valueAsPercent}
/>
</div>
</div>
</div>
<div data-uix-stage-trace>
<span data-uix-stage-trace-key>value</span>
<code>{value}</code>
<span style="color: var(--uix-text-faint)">·</span>
<span data-uix-stage-trace-key>style</span>
<span>{formatStyle}</span>
<span style="margin-inline-start: auto;">
<span data-uix-stage-trace-key>locale</span>
<code>{demoLocale}</code>
</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>17</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>0</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>
<div data-uix-controls>
<label data-uix-control>
<span data-uix-control-label>value</span>
<input
type="number"
bind:value
step="0.01"
style="inline-size: 16rem; font-family: var(--uix-font-mono);"
/>
</label>
<label data-uix-control>
<span data-uix-control-label>style</span>
<span data-uix-chips role="radiogroup">
{#each ['decimal', 'percent', 'currency', 'unit'] as s}
<button
data-uix-chip
data-active={formatStyle === s}
onclick={() => (formatStyle = s as typeof formatStyle)}>{s}</button
>
{/each}
</span>
</label>
{#if formatStyle === 'currency'}
<label data-uix-control>
<span data-uix-control-label>currency</span>
<span data-uix-chips role="radiogroup">
{#each ['EUR', 'USD', 'GBP', 'JPY', 'CNY', 'INR'] as c}
<button
data-uix-chip
data-active={currency === c}
onclick={() => (currency = c)}>{c}</button
>
{/each}
</span>
</label>
{/if}
{#if formatStyle === 'unit'}
<label data-uix-control>
<span data-uix-control-label>unit</span>
<span data-uix-chips role="radiogroup">
{#each ['byte', 'kilometer', 'kilometer-per-hour', 'celsius', 'percent', 'meter', 'liter'] as u}
<button
data-uix-chip
data-active={unit === u}
onclick={() => (unit = u)}>{u}</button
>
{/each}
</span>
</label>
{/if}
{#if formatStyle === 'percent'}
<label data-uix-control>
<span data-uix-control-label
>valueAsPercent
<span data-uix-control-hint>treat value as already × 100</span></span
>
<span data-uix-switch>
<input type="checkbox" bind:checked={valueAsPercent} />
<span data-uix-switch-label>{valueAsPercent ? 'on' : 'off'}</span>
</span>
</label>
{/if}
<label data-uix-control>
<span data-uix-control-label>notation</span>
<span data-uix-chips role="radiogroup">
{#each ['standard', 'compact', 'scientific', 'engineering'] as n}
<button
data-uix-chip
data-active={notation === n}
onclick={() => (notation = n as typeof notation)}>{n}</button
>
{/each}
</span>
</label>
{#if notation === 'compact'}
<label data-uix-control>
<span data-uix-control-label>compactDisplay</span>
<span data-uix-chips role="radiogroup">
{#each ['short', 'long'] as c}
<button
data-uix-chip
data-active={compactDisplay === c}
onclick={() => (compactDisplay = c as typeof compactDisplay)}>{c}</button
>
{/each}
</span>
</label>
{/if}
<label data-uix-control>
<span data-uix-control-label>signDisplay</span>
<span data-uix-chips role="radiogroup">
{#each ['auto', 'never', 'always', 'exceptZero', 'negative'] as s}
<button
data-uix-chip
data-active={signDisplay === s}
onclick={() => (signDisplay = s as typeof signDisplay)}>{s}</button
>
{/each}
</span>
</label>
<label data-uix-control>
<span data-uix-control-label>useGrouping</span>
<span data-uix-switch>
<input type="checkbox" bind:checked={useGrouping} />
<span data-uix-switch-label>{useGrouping ? 'on' : 'off'}</span>
</span>
</label>
<label data-uix-control>
<span data-uix-control-label>minimumFractionDigits</span>
<input
type="number"
bind:value={minimumFractionDigits}
min="0"
max="20"
placeholder="auto"
style="inline-size: 8rem;"
/>
</label>
<label data-uix-control>
<span data-uix-control-label>maximumFractionDigits</span>
<input
type="number"
bind:value={maximumFractionDigits}
min="0"
max="20"
placeholder="auto"
style="inline-size: 8rem;"
/>
</label>
</div>
<div data-uix-code style="margin-top: var(--uix-space-4);">
<div data-uix-code-head>
<span data-uix-layer-badge="eidos">eidos</span>
<span>composition · all options applied</span>
<span data-uix-code-lang>svelte</span>
</div>
<pre><code>{eidosSnippet}</code></pre>
</div>
<div data-uix-subsection-head>Patterns · common shapes</div>
<div data-uix-pattern-stack>
<div data-uix-pattern>
<h3 data-uix-pattern-title>Decimal · plain numbers locale-aware</h3>
<div data-uix-pattern-row>
<table data-uix-table>
<thead><tr><th>Markup</th><th>Output</th></tr></thead>
<tbody>
<tr><td><code>&lt;FormatNumber value={'{1234567}'} /&gt;</code></td><td><strong><FormatNumber value={1234567} /></strong></td></tr>
<tr><td><code>&lt;FormatNumber value={'{3.14159}'} maximumFractionDigits={'{2}'} /&gt;</code></td><td><strong><FormatNumber value={3.14159} maximumFractionDigits={2} /></strong></td></tr>
<tr><td><code>&lt;FormatNumber value={'{-42}'} signDisplay="always" /&gt;</code></td><td><strong><FormatNumber value={-42} signDisplay="always" /></strong></td></tr>
<tr><td><code>&lt;FormatNumber value={'{1234}'} useGrouping={'{false}'} /&gt;</code></td><td><strong><FormatNumber value={1234} useGrouping={false} /></strong></td></tr>
</tbody>
</table>
</div>
</div>
<div data-uix-pattern>
<h3 data-uix-pattern-title>Percent · with vs without valueAsPercent</h3>
<div data-uix-pattern-row>
<table data-uix-table>
<thead><tr><th>Markup</th><th>Output</th></tr></thead>
<tbody>
<tr><td><code>value={'{0.425}'} formatStyle="percent"</code></td><td><strong><FormatNumber value={0.425} formatStyle="percent" /></strong></td></tr>
<tr><td><code>value={'{42.5}'} formatStyle="percent" valueAsPercent</code></td><td><strong><FormatNumber value={42.5} formatStyle="percent" valueAsPercent /></strong></td></tr>
<tr><td><code>value={'{1.5}'} formatStyle="percent" minimumFractionDigits={'{1}'}</code></td><td><strong><FormatNumber value={1.5} formatStyle="percent" minimumFractionDigits={1} /></strong></td></tr>
</tbody>
</table>
</div>
</div>
<div data-uix-pattern>
<h3 data-uix-pattern-title>Currency · ISO 4217 + display variants</h3>
<div data-uix-pattern-row>
<table data-uix-table>
<thead><tr><th>Markup</th><th>Output</th></tr></thead>
<tbody>
<tr><td><code>currency="EUR" value={'{1499}'}</code></td><td><strong><FormatNumber value={1499} formatStyle="currency" currency="EUR" /></strong></td></tr>
<tr><td><code>currency="USD" value={'{1499}'} notation="compact"</code></td><td><strong><FormatNumber value={1499} formatStyle="currency" currency="USD" notation="compact" /></strong></td></tr>
<tr><td><code>currency="GBP" value={'{1234567}'} notation="compact" compactDisplay="long"</code></td><td><strong><FormatNumber value={1234567} formatStyle="currency" currency="GBP" notation="compact" compactDisplay="long" /></strong></td></tr>
<tr><td><code>currency="JPY" value={'{12345}'} currencyDisplay="name"</code></td><td><strong><FormatNumber value={12345} formatStyle="currency" currency="JPY" currencyDisplay="name" /></strong></td></tr>
<tr><td><code>currency="USD" value={'{-99.5}'} currencySign="accounting"</code></td><td><strong><FormatNumber value={-99.5} formatStyle="currency" currency="USD" currencySign="accounting" /></strong></td></tr>
</tbody>
</table>
</div>
</div>
<div data-uix-pattern>
<h3 data-uix-pattern-title>Unit · bytes, distances, temperatures</h3>
<div data-uix-pattern-row>
<table data-uix-table>
<thead><tr><th>Markup</th><th>Output</th></tr></thead>
<tbody>
<tr><td><code>unit="byte" value={'{2048}'}</code></td><td><strong><FormatNumber value={2048} formatStyle="unit" unit="byte" /></strong></td></tr>
<tr><td><code>unit="byte" value={'{1500000000}'} notation="compact"</code></td><td><strong><FormatNumber value={1500000000} formatStyle="unit" unit="byte" notation="compact" /></strong></td></tr>
<tr><td><code>unit="kilometer-per-hour" value={'{120}'}</code></td><td><strong><FormatNumber value={120} formatStyle="unit" unit="kilometer-per-hour" /></strong></td></tr>
<tr><td><code>unit="celsius" value={'{-3.5}'} unitDisplay="long"</code></td><td><strong><FormatNumber value={-3.5} formatStyle="unit" unit="celsius" unitDisplay="long" /></strong></td></tr>
</tbody>
</table>
</div>
</div>
<div data-uix-pattern>
<h3 data-uix-pattern-title>Compact notation · 1.2K · 3M · 1B</h3>
<div data-uix-pattern-row>
<table data-uix-table>
<thead><tr><th>Value</th><th>compact short</th><th>compact long</th></tr></thead>
<tbody>
<tr><td>1500</td><td><strong><FormatNumber value={1500} notation="compact" /></strong></td><td><strong><FormatNumber value={1500} notation="compact" compactDisplay="long" /></strong></td></tr>
<tr><td>1234567</td><td><strong><FormatNumber value={1234567} notation="compact" /></strong></td><td><strong><FormatNumber value={1234567} notation="compact" compactDisplay="long" /></strong></td></tr>
<tr><td>3500000000</td><td><strong><FormatNumber value={3500000000} notation="compact" /></strong></td><td><strong><FormatNumber value={3500000000} notation="compact" compactDisplay="long" /></strong></td></tr>
<tr><td>1234567890123</td><td><strong><FormatNumber value={1234567890123} notation="compact" /></strong></td><td><strong><FormatNumber value={1234567890123} notation="compact" compactDisplay="long" /></strong></td></tr>
</tbody>
</table>
</div>
</div>
<div data-uix-pattern>
<h3 data-uix-pattern-title>Locale override · same value, different formatting rules</h3>
<div data-uix-pattern-row>
<table data-uix-table>
<thead><tr><th>Locale</th><th>Decimal</th><th>Currency (EUR)</th></tr></thead>
<tbody>
<tr><td><code>es-ES</code></td><td><strong><FormatNumber value={1234567.89} locale="es-ES" /></strong></td><td><strong><FormatNumber value={1234567.89} formatStyle="currency" currency="EUR" locale="es-ES" /></strong></td></tr>
<tr><td><code>en-US</code></td><td><strong><FormatNumber value={1234567.89} locale="en-US" /></strong></td><td><strong><FormatNumber value={1234567.89} formatStyle="currency" currency="EUR" locale="en-US" /></strong></td></tr>
<tr><td><code>de-DE</code></td><td><strong><FormatNumber value={1234567.89} locale="de-DE" /></strong></td><td><strong><FormatNumber value={1234567.89} formatStyle="currency" currency="EUR" locale="de-DE" /></strong></td></tr>
<tr><td><code>en-GB</code></td><td><strong><FormatNumber value={1234567.89} locale="en-GB" /></strong></td><td><strong><FormatNumber value={1234567.89} formatStyle="currency" currency="EUR" locale="en-GB" /></strong></td></tr>
</tbody>
</table>
</div>
</div>
<div data-uix-pattern>
<h3 data-uix-pattern-title>Null / NaN · fallback prop</h3>
<div data-uix-pattern-row>
<table data-uix-table>
<thead><tr><th>Markup</th><th>Output</th></tr></thead>
<tbody>
<tr><td><code>value={'{null}'}</code></td><td><strong>"<FormatNumber value={null} />"</strong> (empty)</td></tr>
<tr><td><code>value={'{null}'} fallback="—"</code></td><td><strong><FormatNumber value={null} fallback="—" /></strong></td></tr>
<tr><td><code>value={'{NaN}'} fallback="n/a"</code></td><td><strong><FormatNumber value={NaN} fallback="n/a" /></strong></td></tr>
<tr><td><code>value={'{Infinity}'} fallback="∞"</code></td><td><strong><FormatNumber value={Infinity} fallback="∞" /></strong></td></tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
{/if}
{#if tab === 'api'}
<section data-uix-section>
<h2 data-uix-section-title>API reference</h2>
<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">value</td><td class="type">number | null</td><td class="default empty">—</td><td>Number to format. <code>null</code>/<code>undefined</code>/<code>NaN</code>/<code>Infinity</code> → renders <code>fallback</code>.</td></tr>
<tr><td class="name">formatStyle</td><td class="type">'decimal' | 'percent' | 'currency' | 'unit'</td><td class="default">'decimal'</td><td>Formatting style. <code>currency</code> requires <code>currency</code> prop; <code>unit</code> requires <code>unit</code>.</td></tr>
<tr><td class="name">currency</td><td class="type">string</td><td class="default empty">— uix.format.currency</td><td>ISO 4217 code. Falls back to the active currency from the runtime.</td></tr>
<tr><td class="name">unit</td><td class="type">string</td><td class="default">'byte'</td><td>Unit identifier (`byte`, `kilometer-per-hour`, `celsius`, …).</td></tr>
<tr><td class="name">currencyDisplay</td><td class="type">'symbol' | 'narrowSymbol' | 'code' | 'name'</td><td class="default">'symbol'</td><td>Currency display flavour.</td></tr>
<tr><td class="name">currencySign</td><td class="type">'standard' | 'accounting'</td><td class="default">'standard'</td><td>`accounting` wraps negatives in parentheses.</td></tr>
<tr><td class="name">unitDisplay</td><td class="type">'long' | 'short' | 'narrow'</td><td class="default">'short'</td><td>Unit label flavour.</td></tr>
<tr><td class="name">notation</td><td class="type">'standard' | 'compact' | 'scientific' | 'engineering'</td><td class="default">'standard'</td><td>`compact` enables "1.2K" / "1 million" displays.</td></tr>
<tr><td class="name">compactDisplay</td><td class="type">'short' | 'long'</td><td class="default">'short'</td><td>"1K" vs "1 thousand".</td></tr>
<tr><td class="name">signDisplay</td><td class="type">'auto' | 'never' | 'always' | 'exceptZero' | 'negative'</td><td class="default">'auto'</td><td>Sign-display policy.</td></tr>
<tr><td class="name">useGrouping</td><td class="type">boolean</td><td class="default">true</td><td>Show grouping separator.</td></tr>
<tr><td class="name">minimumFractionDigits, maximumFractionDigits</td><td class="type">number</td><td class="default empty">—</td><td>Fractional precision bounds.</td></tr>
<tr><td class="name">minimumIntegerDigits</td><td class="type">number</td><td class="default empty">—</td><td>Pads integer part with zeros.</td></tr>
<tr><td class="name">minimumSignificantDigits, maximumSignificantDigits</td><td class="type">number</td><td class="default empty">—</td><td>Significant-digit bounds.</td></tr>
<tr><td class="name">valueAsPercent</td><td class="type">boolean</td><td class="default">false</td><td>When <code>formatStyle="percent"</code> and the caller already scaled to 0-100, set this to skip the × 100 multiplication.</td></tr>
<tr><td class="name">locale</td><td class="type">SupportedLocale</td><td class="default empty">— uix.langs.getLocale()</td><td>Optional locale override.</td></tr>
<tr><td class="name">fallback</td><td class="type">string</td><td class="default">''</td><td>Render when value is null/undefined/NaN/Infinity.</td></tr>
<tr><td class="name">as</td><td class="type">keyof HTMLElementTagNameMap</td><td class="default">'span'</td><td>HTML tag for the rendered element.</td></tr>
</tbody>
</table>
</div>
</section>
{/if}
{#if tab === 'morfo'}
<section data-uix-section>
<h2 data-uix-section-title>
<span data-uix-layer-badge="morfo">morfo</span> · declarative contract
</h2>
<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>{formatNumberMorfo.name}</td></tr>
<tr><td class="name">kebab</td><td><code>{formatNumberMorfo.kebab}</code></td></tr>
<tr><td class="name">scope</td><td>{formatNumberMorfo.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>
</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>
FormatNumber declares <strong>no semantic events</strong>. Number formatting is a
passive transformation, not an evaluative act.
</p>
</section>
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>
<p data-uix-section-desc>
FormatNumber ships <strong>no CSS recipe</strong>. The rendered element is a
plain <code>&lt;span&gt;</code> (or the <code>as</code> tag) and inherits its
styling from the surrounding context. The <code>[data-format-number]</code>
marker exists only for tooling — l10n audits can target it without affecting
visual output.
</p>
</section>
{/if}
{#if tab === 'a11y'}
<section data-uix-section>
<h2 data-uix-section-title>Accessibility</h2>
<div data-uix-table-wrap>
<table data-uix-table>
<thead><tr><th>Concern</th><th>Contract</th></tr></thead>
<tbody>
<tr>
<td class="name">Reading direction</td>
<td>
Numbers in RTL locales (Arabic, Hebrew) are formatted by
<code>Intl.NumberFormat</code> with the appropriate digit shaping —
FormatNumber doesn't override this.
</td>
</tr>
<tr>
<td class="name">Screen-reader output</td>
<td>
Screen readers read the formatted string verbatim ("one thousand five
hundred dollars" for <code>$1,500</code>). For announcement-style updates,
wrap in <a href="/uix/components/announce">&lt;Announce&gt;</a>.
</td>
</tr>
<tr>
<td class="name">Currency code visibility</td>
<td>
Default <code>currencyDisplay="symbol"</code> shows `$`, `€`, `£`. For
unambiguous reading (especially in compact / cross-currency contexts),
consider <code>currencyDisplay="code"</code> ("USD 1,500") or
<code>currencyDisplay="name"</code> ("1,500 US dollars").
</td>
</tr>
<tr>
<td class="name">Falsy values</td>
<td>
<code>value={'{null}'}</code> + empty default fallback renders an empty
element — screen readers skip it. Set <code>fallback="—"</code> or
similar to expose a placeholder explicitly.
</td>
</tr>
</tbody>
</table>
</div>
</section>
{/if}
</div>
<style>
[data-uix-pattern-stack] {
display: flex;
flex-direction: column;
gap: var(--uix-space-5);
}
[data-uix-pattern-row] {
display: flex;
flex-direction: column;
gap: var(--uix-space-3);
padding: var(--uix-space-4);
border: 1px solid var(--uix-border);
border-radius: var(--uix-radius-md);
}
h3[data-uix-pattern-title] {
margin: 0 0 var(--uix-space-3);
font-family: var(--uix-font-ui);
font-size: var(--uix-font-size-sm);
font-weight: 600;
color: var(--uix-text-faint);
text-transform: uppercase;
letter-spacing: 0.04em;
}
</style>

Powered by TurnKey Linux.