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

registry:ui · Data Entry

select — the popover listbox

Custom select built on the native Popover API + CSS Anchor Positioning: popover='auto' light-dismiss panel, listbox role with roving aria-activedescendant (↑↓/Home/End/Enter), terminal bezel styling with primary left-border selected state, RTL-aware. name= submits the committed VALUE into FormData through the faceless form-field bridge (ElementInternals); for the platform's own overlay picker use native-select instead. 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 — select

$npx jixoai-ui add select

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

popover=auto panel per-option descriptions ↑/↓ roving highlight FormData-ready rtl: logical properties only

usage

Usage

Bind the committed value; rows with descriptions and disabled entries are plain data on the options array.

Select usage
const runtimeOptions: SelectOption[] = [
  { value: 'node', label: 'node', description: 'node-pty backend' },
  { value: 'bun', label: 'bun', description: 'Bun.Terminal, 1.3.13+' },
  { value: 'deno', label: 'deno', description: '@sigma/pty-ffi over FFI' },
  { value: 'wasi', label: 'wasi (not yet)', disabled: true },
];

<Select
  label="runtime"
  placeholder="pick a runtime…"
  bind:value={runtime}
  options={runtimeOptions}
/>

select

The popover-listbox select: per-option descriptions, ↑/↓/Home/End/Enter roving highlight with focus restitution — for when the native popup can't say what you need.

  • node node-pty backend — ConPTY on windows, forkpty elsewhere
  • bun Bun.Terminal — linux/macos since 1.3.13, windows 1.3.14
  • deno @sigma/pty-ffi — FFI over rust portable-pty
  • wasi — coming soon reserved route, not implemented yet
select-usage.svelte svelte
<Select
  label="runtime"
  placeholder="pick a runtime…"
  options={runtimeOptions}
  bind:value
/>

select × 2

One family, two selects — native first

NativeSelect is the default you should ship: a real <select> (options as <option> children) whose popup list, keyboard, and type-ahead belong to the platform — it rides into FormData with a name/value pair and gets the OS overlay picker on mobile. Select is the same trigger paint on a <button> opening a popover listbox: per-option descriptions, a painted terminal-bezel panel, roving ↑/↓/Enter highlight with focus restitution — reach for it only when the native popup can't say what you need.

platform popup · FormData-ready · bound value: node
  • node node-pty backend — ConPTY on windows, forkpty elsewhere
  • bun Bun.Terminal — linux/macos since 1.3.13, windows 1.3.14
  • deno @sigma/pty-ffi — FFI over rust portable-pty
  • wasi — coming soon reserved route, not implemented yet
popover panel · descriptions · bound value: node

Open the right one: the panel is popover="auto" wired with popovertarget, so light dismiss, Escape, and top-layer rendering are the browser's; the JS only drives role="listbox" / role="option" / aria-activedescendant, the ↑/↓/Home/End/Enter highlight, and focus restitution to the trigger on every close path. The selected row reads --terminal-hover fill with a 2px --primary edge on border-inline-start — under dir="rtl" the edge flips sides by itself.

label + error wiring, both selects

The split changes nothing semantically: label[for] binds to the control (the <button> trigger in Select's case), and the error prop wires aria-invalid + aria-describedby + the dashed shell — the same monochrome invalid signal as the rest of the family.

plan is required

  • — choose —
  • free community tier
  • pro dedicated backend

plan is required

Select usage
const runtimeOptions: SelectOption[] = [
  { value: 'node', label: 'node', description: 'node-pty backend' },
  { value: 'bun', label: 'bun', description: 'Bun.Terminal, 1.3.13+' },
  { value: 'deno', label: 'deno', description: '@sigma/pty-ffi over FFI' },
  { value: 'wasi', label: 'wasi (not yet)', disabled: true },
];

<Select
  label="runtime"
  placeholder="pick a runtime…"
  bind:value={runtime}
  options={runtimeOptions}
/>

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.

select · rtl

  • node node-pty backend — ConPTY on windows, forkpty elsewhere
  • bun Bun.Terminal — linux/macos since 1.3.13, windows 1.3.14
  • deno @sigma/pty-ffi — FFI over rust portable-pty
dir="rtl" on the wrapper — trigger chevron and panel edge line flipped without a physical property in sight
select-rtl-demo.svelte svelte
<script lang="ts">
  import Select from '@ui/select.svelte';
</script>

<div dir="rtl" class={cx(rt.flex, rt.col, rt.gap16, rt.frame, rt.p16)}>
  <Select
    label="runtime (rtl)"
    bind:value={runtimeRtl}
    options={runtimeOptions.slice(0, 3)}
    placeholder="pick…"
  />
  <span class={cx(rt.inkMuted, rt.text12)}>
    dir="rtl" on the wrapper — trigger chevron and panel edge line flipped without a
    physical property in sight
  </span>
</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

Select variants

The popover listbox with plain rows, rows carrying descriptions, a disabled row, and the error state.

select · variants

  • node node-pty backend — ConPTY on windows, forkpty elsewhere
  • bun Bun.Terminal — linux/macos since 1.3.13, windows 1.3.14
  • deno @sigma/pty-ffi — FFI over rust portable-pty
  • wasi — coming soon reserved route, not implemented yet
  • — choose —
  • free
  • pro

plan is required

select-types-demo.svelte svelte
<script lang="ts">
  import Select from '@ui/select.svelte';
</script>

<div class="grid gap-4 sm:grid-cols-2">
  <div class="border border-border p-4">
    <Select
      label="descriptions"
      options={runtimeOptions}
      placeholder="open for second lines…"
    />
  </div>
  <div class="border border-border p-4">
    <Select
      label="error"
      options={[
        { value: '', label: '— choose —' },
        { value: 'free', label: 'free' },
        { value: 'pro', label: 'pro' },
      ]}
      error="plan is required"
    />
  </div>
</div>

a11y

Accessibility

A real listbox contract on top of the native popover: roving highlight via aria-activedescendant, focus restitution to the trigger on every close path.

Keyboard

KeyAction
TabMoves focus to the trigger button
↑ / ↓ (closed)Opens the panel, like the native select
↑ / ↓ (open)Moves the highlight, skipping disabled rows; no wrap
Home / EndJumps to the first / last enabled row
Enter / SpaceChooses the highlighted row and closes the panel
Esc / outside clickLight dismiss from popover="auto"; focus returns to the trigger

ARIA

AttributeValueDescription
aria-haspopup"listbox"On the trigger button, with aria-expanded synced live
aria-activedescendant{id}-opt-{index}Roving highlight ID on the focusable list
aria-selected'true' / 'false'On each role="option" row
aria-invalid'true'On the trigger when the error prop is provided
aria-describedby{id}-errorPoints at the "! message" validation line

theming

Density and tokens

Trigger and panel rows share the density-scope rhythm; resize the scope and the trigger, rows, and label stack resize together.

xs
  • node node-pty backend — ConPTY on windows, forkpty elsewhere
  • bun Bun.Terminal — linux/macos since 1.3.13, windows 1.3.14
  • deno @sigma/pty-ffi — FFI over rust portable-pty
  • wasi — coming soon reserved route, not implemented yet
sm
  • node node-pty backend — ConPTY on windows, forkpty elsewhere
  • bun Bun.Terminal — linux/macos since 1.3.13, windows 1.3.14
  • deno @sigma/pty-ffi — FFI over rust portable-pty
  • wasi — coming soon reserved route, not implemented yet
default
  • node node-pty backend — ConPTY on windows, forkpty elsewhere
  • bun Bun.Terminal — linux/macos since 1.3.13, windows 1.3.14
  • deno @sigma/pty-ffi — FFI over rust portable-pty
  • wasi — coming soon reserved route, not implemented yet
lg
  • node node-pty backend — ConPTY on windows, forkpty elsewhere
  • bun Bun.Terminal — linux/macos since 1.3.13, windows 1.3.14
  • deno @sigma/pty-ffi — FFI over rust portable-pty
  • wasi — coming soon reserved route, not implemented yet
TokenDefaultSource
--jx-hit28 / 32 / 40 / 48pxdensity
--jx-text11 / 12 / 13 / 15pxdensity
--jx-line16 / 18 / 20 / 24pxdensity
--jx-inset8 / 8 / 12 / 16pxdensity
--jx-gap8 / 8 / 12 / 16pxdensity

axes

Universal props

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/container-conditional values. The TRIGGER surface (.jx-field) and the PORTALED listbox both carry the resolved lanes (the batch C portal law: the panel stamps its own carriers, self-carried across the top-layer promotion); elevation carries the family own level2 — the anchored panel's menu rung. Open a demo listbox to see the panel's carriers live.

Select · universal props

  • node node-pty backend — ConPTY on windows, forkpty elsewhere
  • bun Bun.Terminal — linux/macos since 1.3.13, windows 1.3.14
  • deno @sigma/pty-ffi — FFI over rust portable-pty
  • wasi — coming soon reserved route, not implemented yet
  • node node-pty backend — ConPTY on windows, forkpty elsewhere
  • bun Bun.Terminal — linux/macos since 1.3.13, windows 1.3.14
  • deno @sigma/pty-ffi — FFI over rust portable-pty
  • wasi — coming soon reserved route, not implemented yet
select-universal.svelte svelte
<Select label="runtime" options={runtimeOptions} size={18} density="small" />

api

API

Props extend native button attributes on the trigger; form submission rides the faceless jx-form-field bridge (the committed value, never the label, reaches FormData).

Properties

PropertyTypeDefaultDescription
options * SelectOption[]—The full option list; order = panel order.
value bindstring—Committed value; undefined shows the placeholder.
placeholder string'Select...'Trigger text when nothing is selected.
label string—Field label rendered as label[for] above the trigger.
name string—Form field name — the bridge submits the committed value under it.
error string—Error text: sets aria-invalid, wires aria-describedby, dashes the shell.
multiple booleanfalseReserved extension direction — not implemented in v1 (warns).
variant 'solid' | 'acrylic' | 'auto''auto' · Own default, not ambientFloating-surface fill of the panel.
onchange (value: string) => void—Commit hook (issue #6): fires with the newly committed value on every commit route (click, Enter, Space), alongside the bind:value write — sugar over bind:value. The native change event never fires on the trigger button, so this prop owns the channel outright.
disabled booleanfalseDisables the trigger and the form-bridge field.

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