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

docs · standard

demo-standard — one page skeleton, derived from the market leaders

The market research (.agents/audit/2026-08-30-site-walkthrough) is unambiguous: the leading registries win on demo-page CONSISTENCY, not component count. This page is the law every component page follows — and the lint that keeps it true.

six sections, one order ability-named demos <name>-<ability> variants props from one source staged lint, data-driven backlog

install

npx jixoai-ui add select — or point shadcn add at the item URL: https://ui.jixoai.com/r/select.json

install — select

$npx jixoai-ui add select

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

usage

Usage

The skeleton assembles from page-owned chrome (DocsInstall, DocsSeeAlso) and the existing canvas + section components — nothing new to invent per page.

the skeleton, assembled
<!-- the page assembles (select is the exhibit) -->
<!-- every /docs/components/<name>.html page, top to bottom -->
<DocsInstall name="select" />              <!-- Install: npx + registry URL -->

<SectionCard family="usage" title="Usage">  <!-- Usage: minimal working example -->
  <CodeBlock code={usage} lang="svelte" />
</SectionCard>

<ComponentCanvas title="select" …>         <!-- Examples: ability-named demos -->
  …demos, each with collapsible code…
</ComponentCanvas>

<PropsTable meta={selectMeta} docs={SELECT_DOCS} />  <!-- API: one source -->

<DocsSeeAlso name="select" />              <!-- See Also: reading-chain data -->

law

The skeleton is law

Every /docs/components/<name>.html page presents six sections, in this order. Page-local sections (types, a11y, theming…) may interleave — the six keep their relative order.

Keyboard

KeyAction
IntroThe one-paragraph contract — hero SectionCard, exactly one h1, pills for the capability surface
InstallCopy-ready `npx jixoai-ui add <name>` + the registry item URL (https://ui.jixoai.com/r/<name>.json) — the shadcn add path for non-CLI consumers
UsageThe minimal working example — one code block, the smallest honest mount
ExamplesAbility-named demos on the component canvas, each with collapsible code (the drawer shows the exact registry copy)
APIThe props table — Prop / Type / Default / Description, rendered from the generated meta (one source)
See AlsoRelated component links derived from the docs reading chain — data, never a hand list

ARIA

AttributeValueDescription
data-doc-installsection rootThe lint’s Install marker; the section text carries the command verbatim
data-doc-props-tabletable elementThe lint’s API marker — PropsTable stamps it itself
data-doc-see-alsosection rootThe lint’s See Also marker; must hold ≥1 link to another component page

grammar

Demos are named by ability

One phrase names one capability — what the demo LETS YOU DO, never a number, never a scene noun, never two capabilities at once.

yes — the ability

names that carry
with clear button
async loading
multiple chips
with descriptions
rtl geometry

no — anything else

names that fail review
demo 1              <!-- numbered: says nothing -->
example-two         <!-- numbered + kebab: worse */
login scene         <!-- a scene noun, not a capability -->
fancy               <!-- an opinion, not an ability -->
with clear button and async loading   <!-- TWO capabilities in one name -->

The lint enforces the mechanical subset (numbered titles fail); the semantic half — one phrase, one capability — is review law. When a demo needs a second capability, it needs a second demo.

convention

Promoted variants take the suffix

A demo that ships as its own registry item is <name>-<ability>: the composition, standalone, named after the ability it grants (the @reui/date-picker-range precedent).

registry item, promoted
// registry.json — a promoted demo becomes its own item with the
// <name>-<ability> suffix (the @reui/date-picker-range precedent):
{
  "name": "date-picker-range",
  "type": "registry:ui",
  "files": [ /* the range-mode composition, standalone */ ]
}

The suffix is the demo name, kebab-cased: with clear button promotes to select-with-clear-button. The page-level demo and the registry-level variant share one ability vocabulary — there is no third naming scheme.

one source

Props tables read the generated meta

Name, type and default come from the component's GENERATED .meta.ts (component-metadata-gen, drift-gated); the page carries only the docs curation — prose, flags, and corrections where the extractor's documented ceiling bites.

The curation layer is accountable: the drift lock (test/props-table-meta-drift.spec.ts) pins which override fields each pilot uses, so a new correction can only land as a conscious edit. As the extractor grows (JSDoc descriptions, $bindable() flags, ? optionality, alias-to-union merging), the curation shrinks — the table never silently disagrees with the interface again. The API section below renders from the select meta, live:

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)

adoption

Staged, not partial

The skeleton lint hard-fails every in-scope route and warns on the rest while printing the backlog. The scope file is data; the successor change flips the gate global when the backlog is empty.

scripts/docs-skeleton-scope.json
// scripts/docs-skeleton-scope.json — the staged lint's data
{
  "version": 1,
  "successor": "2026-08-30-docs-demo-standard-global-gate",
  "inScope": ["select", "popover", "checkbox", "card-grid",
              "date-picker", "toast", "combobox"],
  "backlog": [
    { "route": "press-button", "owner": "2026-08-30-canvas-floor-lab" },
    { "route": "table",        "owner": "2026-08-30-table-grid-toolbar" }
    // …every route lands here with its owning change before the flip
  ]
}

An out-of-scope page missing sections is BACKLOG, not violation — the gate stays green while the migration proceeds page by page. When the successor lands, the warn list must be empty or the flip fails: the exit criterion is computed, never asserted.

worked example

Adding a demo, end to end

The select page's with-descriptions demo, walked through the standard: name it by ability, show it on the canvas with collapsible code, and let the props table pick up any new prop from the regenerated meta.

Keyboard

KeyAction
1 · name“rows with descriptions” — the ability is per-option second lines; one phrase, one prop (the options array’s description field)
2 · placeA demo section under the canvas — after Usage, before API; the drawer shows the exact registry copy (?raw, the same-source law)
3 · propsA new interface prop regenerates the meta (component-metadata-gen) — the table row appears; only its prose lands in the docs curation
4 · gateverify:docs-structure hard-fails the page if a skeleton section goes missing; verify:meta fails if the meta forgets the regeneration

ARIA

AttributeValueDescription
lint rulenumbered titles“demo 1” / “example 2” names fail the skeleton lint on in-scope routes
lint rulesection orderintro < install < usage < examples < api < see-also, by first marker position
jixoai-ui ui.jixoai.com the jixoai design language

Theme

Brand hue

0°

Auto cycle · 24h