jixoai-ui
the terminal design language
loading the style sheet…
Skip to content
docs · print medium: screen

registry:ui · Data Entry

combobox — the searchable select

Searchable select with custom-value input: real-time label filtering, allowCustom for free-text values, popover panel with roving aria-activedescendant (focus stays in input), 'No results' + 'Use xxx' affordance, blur-commit, family label/error contract. name= is intercepted off the native input and submits the committed VALUE (option value or custom string — never the display label) through the faceless form-field bridge. Floating-surface law: ::after shadow layer (semi-transparent black/white) + @starting-style entry; variant='solid' | 'acrylic' | 'auto' (acrylic falls back to solid under prefers-reduced-transparency; the terminal bezel keeps its identity through the jx-surface fill props).

install — combobox

$npx jixoai-ui add combobox

https://ui.jixoai.com/r/combobox.json

the trigger IS the input live label filter ↑/↓ + Enter / Escape / Tab allowCustom “Use “xxx”” multiple chips · showClear rtl: logical properties only

overview

Overview

The searchable sibling of select: the trigger IS an <input>, so the popup is a conversation — the filter text is input state and only an explicit commit touches value.

The commit paths are the contract: Enter commits the highlighted row (or, with nothing highlighted, resolves the raw text — exact option, else the custom value when allowCustom, else nothing); blur resolves the same way; Escape reverts to the committed display; Tab keeps the typed text through the same resolution. The “Use “xxx”” row appears only when nothing matched — while matches exist they are the answer.

The ARIA contract is the hard one and this family takes the APG shape with zero focus management: role="combobox" sits on the input, aria-expanded mirrors the panel, aria-controls and aria-owns name the top-layer listbox (a DOM sibling promoted by popover="auto"), and the roving highlight rides aria-activedescendant — focus never enters the panel, so there is no focus restitution to get wrong (measured: after ArrowDown and commit, document.activeElement is still the input). In multiple mode the listbox is aria-multiselectable, the trigger wears chips with per-chip “remove X” buttons, and the committed array crosses to FormData as repeated same-name entries through the faceless form-field bridge — the name prop is intercepted off the native input, which never submits its display text.

The paint is the terminal bezel the select family owns, and the theme axis splits it: the typed atoms (shell border and ground, chips, the active/selected row grounds) freeze at the :root pole while the raw machines (well shadow, row hover, panel ink, focus ring) flip under a scoped .dark — one listbox, two time-bases, detailed in the axis table below. Kinship: select is the composite sibling sharing the panel law; tags-input shares the chip law; cascader is the honest-path cousin — the ruled chain-of-<select>s route for dependent choices that need no typing at all.

usage

Usage

The filter text is input state, never committed state — value only changes through an explicit commit.

Combobox usage
const backends: ComboboxOption[] = [
  { value: 'node-pty', label: 'node-pty', description: 'conpty / forkpty addon' },
  { value: 'bun-terminal', label: 'Bun.Terminal', description: 'linux/macos 1.3.13+, windows 1.3.14' },
  { value: '@sigma/pty-ffi', label: '@sigma/pty-ffi', description: 'deno FFI over rust portable-pty' },
];

<!-- type to filter (label, case-insensitive); ↑/↓ + Enter commits; Escape
     reverts; Tab keeps. No match + allowCustom → the “Use “xxx”” row -->
<Combobox label="backend" bind:value={backend} options={backends} />

<!-- strict: allowCustom={false} reverts stray text on blur -->
<Combobox label="runtime" allowCustom={false} options={backends} />

combobox

The searchable select: the trigger IS the input — typing filters the panel live, ↑/↓ + Enter commits, Escape reverts, and allowCustom offers the “Use “xxx”” row.

  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
combobox-usage.svelte svelte
<Combobox
  label="backend"
  allowCustom
  options={backendOptions}
  bind:value
/>

combobox

The high-form select — the popup becomes a conversation

The trigger IS an input, typing filters the panel live (label contains, case-insensitive), ↑/↓ ride a roving aria-activedescendant highlight, Enter commits it, Escape reverts, Tab keeps — and when nothing matches, the allowCustom row offers “Use “xxx”” in the primary hue while strict fields revert stray text on blur. The panel is the same popover=auto terminal bezel as Select — light dismiss, Escape, and top layer are the browser's; focus never leaves the text field.

Focus one: the text selects itself and the panel opens on the full list with the committed row highlighted (the 2px --primary edge); typing filters live and auto-highlights the first match. Try wasi in the first field — no match, so the “Use “wasi”” row appears in primary; press Enter to commit it as a custom value. The strict field (allowCustom={false}) keeps its committed label instead.

combobox · catalogue

  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
allowCustom (default) · bound value: node-pty
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
allowCustom={false} · blur reverts stray text · value: —
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
no match → “Use “xxx”” row · value: —
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host

backend is required

error wiring: aria-invalid + dashed shell
combobox-catalog-demo.svelte svelte
<script lang="ts">
  import Combobox from '@ui/combobox.svelte';
  import type { ComboboxOption } from '@ui/combobox.svelte';

  const backendOptions: ComboboxOption[] = [
    { value: 'node-pty', label: 'node-pty', description: 'conpty / forkpty — the battle-tested addon' },
    { value: 'bun-terminal', label: 'Bun.Terminal', description: 'linux/macos since 1.3.13, windows 1.3.14' },
    { value: '@sigma/pty-ffi', label: '@sigma/pty-ffi', description: 'deno FFI over rust portable-pty' },
    { value: 'termless', label: 'termless', description: 'VT emulator — not a pty host', disabled: true },
  ];

  let backendRoute = $state<string | undefined>('node-pty');
  let backendStrict = $state<string | undefined>(undefined);
  let backendCustom = $state<string | undefined>(undefined);
</script>

<Combobox label="backend — type to filter" bind:value={backendRoute} options={backendOptions} />

<!-- strict: allowCustom={false} reverts stray text on blur -->
<Combobox
  label="strict — no custom values"
  allowCustom={false}
  bind:value={backendStrict}
  options={backendOptions.slice(0, 3)}
  placeholder="Search..."
/>

<!-- no match → the “Use “xxx”” row -->
<Combobox
  label="custom — try “wasi”"
  bind:value={backendCustom}
  options={backendOptions.slice(0, 3)}
  placeholder="Search or type..."
/>

<!-- error wiring: aria-invalid + dashed shell -->
<Combobox label="backend" error="backend is required" options={backendOptions} />

The component keeps the popover orchestration law of the family: popover="auto" panels anchored with CSS Anchor Positioning (anchor-size(width), flip fallbacks, viewport-center when the engine lacks it), focus that never enters the panel — the roving highlight rides aria-activedescendant + aria-owns off the input itself — and geometry from logical properties only, so dir="rtl" mirrors the chevron and the selected-row edge with zero branches.

Combobox usage
const backends: ComboboxOption[] = [
  { value: 'node-pty', label: 'node-pty', description: 'conpty / forkpty addon' },
  { value: 'bun-terminal', label: 'Bun.Terminal', description: 'linux/macos 1.3.13+, windows 1.3.14' },
  { value: '@sigma/pty-ffi', label: '@sigma/pty-ffi', description: 'deno FFI over rust portable-pty' },
];

<!-- type to filter (label, case-insensitive); ↑/↓ + Enter commits; Escape
     reverts; Tab keeps. No match + allowCustom → the “Use “xxx”” row -->
<Combobox label="backend" bind:value={backend} options={backends} />

<!-- strict: allowCustom={false} reverts stray text on blur -->
<Combobox label="runtime" allowCustom={false} options={backends} />

multiple

multiple — chips, check states, and a lossless form bridge

multiple flips the bindable to string[] in SELECTION ORDER: options toggle membership (pick to add, re-pick to remove), the trigger wears chips with per-chip remove ×, panel rows carry aria-multiselectable plus a check glyph, and picking keeps the panel open. Submission rides the form-field bridge's MULTIVALUE seam — the committed array crosses as a values PROPERTY and lands in FormData as repeated same-name entries, so getAll(name) returns every pick byte-for-byte in order; form.reset() restores the mount array and disabled fields submit nothing. showClear adds an × in the trigger lane that empties the selection — the field then submits honestly empty.

combobox · multiple + clear

node-pty
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
selection order: [node-pty] — chips remove ×, panel check state, re-pick toggles off
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
the × clears the commit — the form then contributes nothing, never "undefined" · value: bun-terminal
combobox-multiple-demo.svelte svelte
<script lang="ts">
  import Combobox from '@ui/combobox.svelte';
  import type { ComboboxOption } from '@ui/combobox.svelte';

  const backendOptions: ComboboxOption[] = [
    { value: 'node-pty', label: 'node-pty' },
    { value: 'bun-terminal', label: 'Bun.Terminal' },
    { value: '@sigma/pty-ffi', label: '@sigma/pty-ffi' },
  ];

  let pickedStacks = $state<string[]>(['node-pty']);
  let clearedRoute = $state<string | undefined>('bun-terminal');
</script>

<!-- multiple: bind an ARRAY in selection order; getAll(name) returns
     the picks byte-for-byte in order -->
<Combobox
  label="stacks (multiple)"
  multiple
  name="stacks"
  bind:value={pickedStacks}
  options={backendOptions}
  placeholder="pick several…"
/>

<!-- showClear: × in the trigger lane; clearing submits honestly empty -->
<Combobox
  label="backend (showClear)"
  showClear
  name="backend-clear"
  bind:value={clearedRoute}
  options={backendOptions}
/>

The transport is the DECIDED one (design.md): no hidden inputs, no joined-string channel — newline-bearing, quote-bearing and arbitrary Unicode values survive byte-for-byte because each value is a separate FormData entry. The breaking change is deliberate and documented: in multiple mode bind:value is string[] — there is no compatibility shim. Blur no longer commits raw text in multiple mode; chips join through explicit Enter / row click only.

multiple · showClear
<!-- multiple: bind an ARRAY in selection order; the committed
     array submits as repeated same-name FormData entries through the
     form-field bridge's MULTIVALUE seam — getAll(name) returns the
     picks byte-for-byte in order (BREAKING: value becomes string[]) -->
<Combobox label="stacks" multiple bind:value={stacks} options={backends} />

<!-- showClear: × in the trigger lane when something is committed;
     clearing submits honestly empty (no entry, never "undefined") -->
<Combobox label="backend" showClear bind:value={backend} options={backends} />

const stacks = $state<string[]>([]);

geometry

RTL — geometry from logical properties

Nothing in the component branches on direction: the chevron sits in the flex flow and the selected-row edge is border-inline-start. The writing mode does the rest.

combobox · rtl

  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
dir="rtl" — chevron inline-start, panel edge inline-start
combobox-rtl-demo.svelte svelte
<script lang="ts">
  import Combobox from '@ui/combobox.svelte';
  import type { ComboboxOption } from '@ui/combobox.svelte';

  const backendOptions: ComboboxOption[] = [
    { value: 'node-pty', label: 'node-pty' },
    { value: 'bun-terminal', label: 'Bun.Terminal' },
  ];

  let backendRtl = $state<string | undefined>('bun-terminal');
</script>

<!-- logical properties only: dir="rtl" mirrors the chevron and the
     selected-row edge with zero branches -->
<div dir="rtl">
  <Combobox label="backend (rtl)" bind:value={backendRtl} options={backendOptions} />
</div>

The chevron sits in the flex flow, the selected-row edge is border-inline-start, and the panel anchors with CSS Anchor Positioning whose offsets are logical too. The writing mode does the rest.

types

Combobox variants

The commit rules are the variants: free custom values by default, strict reverting on blur, plus the error shell.

allowCustom (default)

No match → the “Use “xxx”” row in primary; Enter/Tab commits the typed text as the value.

strict — allowCustom={false}

Stray text reverts on blur; only a listed option (or empty) can be committed.

disabled option rows

Options may disable themselves — skipped by keyboard navigation and click.

error wiring

error → aria-invalid + aria-describedby + the dashed shell.

a11y

Accessibility

Focus never enters the panel — the roving highlight rides aria-activedescendant off the input itself.

Keyboard

KeyAction
↑ / ↓Move the roving highlight through the filtered rows (opening the panel first if closed)
EnterCommit the highlighted row (or the raw text with allowCustom)
EscapeRevert to the committed display and close the panel
TabKeep the typed text: resolve to an option, custom value, or revert
any characterOpen the panel and filter live — the first match auto-highlights

ARIA

AttributeValueDescription
rolecomboboxOn the trigger input, with aria-haspopup="listbox".
aria-activedescendant{id}-opt-nThe keyboard/aria cursor; focus stays in the input the whole time.
aria-controls / aria-owns{id}-listboxThe top-layer promoted listbox is a DOM sibling of the input.
aria-expandedtrue/falseOn the input; mirrors panel state.
aria-multiselectable'true'On the listbox in multiple mode; picked rows carry aria-selected plus the check glyph.
aria-label"remove X" / "clear selection"On the chip remove × buttons and the showClear ×.
aria-invalid / aria-describedbytrue / {id}-errorError wiring — dashed shell plus the validation message.

theming

Density and tokens

Density is live through four measured channels with one floor asymmetry; the glyph slots, the anchor and the motion kernel are the family's own seams.

xs
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
sm
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
default
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
lg
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
TokenDefaultSource
--jx-text11 / 12 / 13 / 15px (measured)density
--jx-text-secondary10 / 11 / 12 / 14px (measured)density
--jx-stack4 / 4 / 8 / 8px (measured)density
--jx-hitlane 26 / 30 / 38 / 46px; shell floor FIXED 40px (measured)density
--jx-icon-chevron / -clear / -checkinline SVG fallbackscomponent
--jx-cbx-{id}anchor-namecomponent
--jx-p0 → 1 timelinecomponent
colorSchemelight ↔ dark on prefers-color-schemestructural

axes

The eight axes

The eight-axis surface (explicit-props): size · shape · radius · density · color · theme · elevation · motion — each axis takes named steps, auto (inherit the ambient context; stamps nothing), an exact number (px · coefficient · dp · hue per axis), or query() for responsive values. MEASURED, not claimed: density is the family's only live ladder (four channels, one floor asymmetry); size is the §11 echo with nothing following; the theme split is the page's same-token demonstrator; shape/radius/color/elevation are supply-only.

PropertyTypeDefaultDescription
size 'small' | 'medium' | 'large' | 'auto' | number'auto'THE §11 ECHO, NOTHING FOLLOWS — the stamp lands verbatim (measured root inline: --jx-size-effective: 14px; font-size: var(--jx-size-effective, 1rem); computed 14px / 18px at the two demo lanes), and no family paint is em-of-parent: the input's voice is the lane's var(--jx-text) (density-governed, it overrides inheritance), the shell floor is 2.5rem rem-of-page, the paddings ride px space channels, the option voices ride token steps. A stamp with nothing to scale. Number unit: px.
shape 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto''auto'SUPPLY-ONLY — stamps the carriers; zero readers in the family (grep receipt), and the shell corner is the input-family box law itself: borderRadius 0, the squared edge. Number unit: none.
radius 'small' | 'medium' | 'large' | 'auto' | number'auto'SUPPLY-ONLY — stamps --jx-radius-effective; zero readers (grep receipt). The shell, the chips and the panel are square by the family box law — the concentric broadcast finds nothing to round. Number unit: px.
density 'small' | 'medium' | 'large' | 'auto' | number'auto'LIVE THROUGH FOUR CHANNELS + THE FLOOR ASYMMETRY (all measured across xs/sm/default/lg): the label and error voices step --jx-text-secondary 10/11/12/14px; the input's voice steps --jx-text 11/12/13/15px; the field gap steps --jx-stack 4/4/8/8px; the lane's min-height steps --jx-hit−2px 26/30/38/46px. The ASYMMETRY: the shell's own floor is FIXED 2.5rem (40px) at every rung — at xs/sm the lane shrinks inside the floor (dead), at lg the 46px lane outgrows it (live), so the control grows with lg and refuses to shrink below 40. The chips stay 1.5rem fixed. The rung rides the ANCESTOR scope: under DensityDemo's [data-density] boxes the family's own attr stays absent (measured null — ambient carries no explicit lane); pass density="small" and the alias resolves small→sm · medium→default · large→lg onto the field root. Number unit: coefficient.
color 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string'auto'SUPPLY-ONLY — stamps --jx-color-effective; zero readers in the family (grep receipt). The ink is the theme-token set (the typed --jx-foreground/--jx-muted-*) plus the terminal machines; the two semantic accents (the “Use “xxx”” row, the selected edge + check glyph) read --primary/--jx-primary directly, not the lane. Number unit: hue degrees.
theme 'light' | 'dark' | 'system' | 'auto''auto'THE BRIDGE, AND THE PAGE'S SPLIT DEMONSTRATOR — the .dark class lands on the field root and what answers depends on the EMISSION FORM. TYPED POLE (frozen at :root): the shell's border and ground, the chips, and the ACTIVE + SELECTED row grounds — measured inside a .dark scope the border stays the light oklch(0 0 0) and --jx-terminal-hover still mixes its light pole. RAW MACHINES (flip): the well shadow (measured flipping to the white-inset dark well), the option ink and HOVER ground (raw --terminal-hover — the SAME sheet token whose typed twin stayed home), the panel ink, the focus ring, the check glyph. Hover a row in a dark scope and the machine lands the dark ground; arrow onto the same row and the frozen pole paints it light — one listbox, two time-bases. A third time-base rides the shell's colorScheme property, flipping on prefers-color-scheme (the media, not the class). system/auto ride tree inheritance. No number lane.
elevation 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number'auto'SUPPLY-ONLY — zero carrier reads (grep receipt). The shadow the shell carries is the control-shell law’s own F-1 well sweep (var(--shadow-well) resting, -hover deepening — intensity, not tier); an elevation lane adds nothing to it. Number unit: dp.
motion 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number'auto'AXIS SUPPLY-ONLY, KERNELS LIVE — the lane carrier is unread (grep receipt), but the family’s motion rides the sheet kernels directly: transitions on var(--motion-100)/var(--motion-150) ease-out (the well sweep, the chevron’s 180° flip, the row ground/ink, the × buttons), the prefers-reduced-motion kill over all of them, and the panel’s WAAPI surface kernel (the measured --jx-p: 0 → 1 timeline; every visible formula hangs off it in jixoai.css). Number unit: coefficient.

Receipts: the density ladder, the size echo, the frozen-pole contrast and the kernel timeline were measured on this page's served DOM (probe, task 24); the supply-only rows carry grep receipts over src/lib/ui/combobox/. The universal demo folds in below, with a query() seat: the density lane flips to the large rung at the lg viewport key (≥64rem — the lg key is 64rem VIEWPORT_SCALE, not the old 40rem container tier).

Combobox · universal props

  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
  • node-pty conpty / forkpty — the battle-tested addon
  • Bun.Terminal linux/macos since 1.3.13, windows 1.3.14
  • @sigma/pty-ffi deno FFI over rust portable-pty
  • termless VT emulator — not a pty host
combobox-universal.svelte svelte
<Combobox {options} label="px number" size={14} density="small" />
<Combobox {options} label="named steps" size="large" radius="medium" />
<Combobox {options} label="query()" density={query<{ lg: DensityLane }, DensityLane>({ lg: 'large' })} />

api

API

22 meta rows − 8 ambient axes = 14 family rows; the synthesized rest row hides behind the curation — the spread is real (HTMLInputAttributes minus value/size/color ride the rest spread to the native input) — 21 served. value/multiple's generic degradation is corrected to the instantiated contract in the curation; the drift-lock test pins it.

Properties

PropertyTypeDefaultDescription
options * ComboboxOption[]—The full option list (order = panel order): { value, label, description?, disabled? }.
value bindstring | string[]—Committed value (bind:value) — a listed option’s value or a custom string; in multiple mode the selected string[] in SELECTION ORDER (breaking: no compat shim).
multiple booleanfalseMulti-select: options toggle membership, trigger chips + panel check states, aria-multiselectable, and the bridge commits repeated same-name FormData entries via the MULTIVALUE seam.
placeholder string'Search or type...'Input placeholder while nothing is committed.
label string—Renders label[for] above the control.
name string—Form field name — intercepted off the input; the bridge submits the VALUE, never the display text.
error string—Adds aria-invalid + aria-describedby + the dashed border.
id stringautoWired into label[for] / error[id]; auto-generated when omitted.
allowCustom booleantrueAccept typed text that matches no option as the committed value (multiple: it joins the selection as a chip).
showClear booleanfalse× in the trigger lane when something is committed; clearing submits honestly empty.
disabled booleanfalseDisable the input, the chips and the chevron together.
variant 'solid' | 'acrylic' | 'auto''auto' · Own default, not ambientFloating-surface paint of the panel.
class string''Forwarded to the shell.

Universal props

PropertyTypeDefaultDescription
size'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopethe base scale — root font-size; parts size in em
shape'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto''auto' · ambient scopecorner geometry (CSS corner-shape; §14 degrade table)
radius'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopecorner size; auto = the concentric broadcast (§3)
density'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopespacing/leading scale over the kernel channels (§4)
color'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string'auto' · ambient scopethe hue axis of the fixed oklch primary system (§5)
theme'light' | 'dark' | 'system' | 'auto''auto' · ambient scopelight/dark profile; system = the JS-mutable global (§6)
elevation'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number'auto' · ambient scopeofficial M3 levels over the surface ladder (§7)
motion'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number'auto' · ambient scopeintensity across the motion kernels (§8)
jixoai-ui ui.jixoai.com the jixoai design language

Theme

Brand hue

0°

Auto cycle · 24h