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

registry:ui · General

icon — a name, not a picture

The glyph renderer every component shares. You hand it a NAME from the generated set — the IconName union closes at build time, so a misspelled glyph is a compile error, not a blank square in production. The component owns the whole <svg> root: viewBox from the icon data, the square size edge, currentColor painting by artwork nature (stroke artwork strokes, fill artwork fills), round caps and joins, aria-hidden. Sizing and stroke weight are props — never wrapper CSS fighting an inline svg.

install — icon

$npx jixoai-ui add icon

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

IconName union — typo = compile error size / strokeWidth props currentColor by artwork nature aria-hidden baked in inline core · SSR-safe

usage

Usage

Import the component, hand it a name — the control around it carries the meaning.

Icon usage
<script lang="ts">
  import Icon from '@ui/icon';
</script>

<!-- decorative by contract — the accessible name lives on the control -->
<button aria-label="close">
  <Icon name="x" />
</button>

<!-- size + stroke weight are props; class + attrs land on the root -->
<Icon name="chevronRight" size={13} />
<Icon name="folderOpen" size={20} strokeWidth={1.5} class="text-primary" />

icon

One name renders one glyph. The playground drives the lower instance; the upper rows are the default sizes, the stroke ladder, and currentColor theming.

size=12
size=16
size=24
size=32
sw=1.5
sw=2
sw=2.5
primary muted
driven by the playground <Icon name="rotateCcw" size=20 strokeWidth=2 />
icon-usage.svelte svelte
<script lang="ts">
  import Icon from '@ui/icon';
</script>

<!-- decorative by contract — the accessible name lives on the control -->
<button aria-label="close">
  <Icon name="x" />
</button>

<!-- size + stroke weight are props; class + attrs land on the root -->
<Icon name="chevronRight" size={13} />
<Icon name="folderOpen" size={20} strokeWidth={1.5} class="text-primary" />

law

The name is a union, not a string

IconName is generated together with the set: the union and the runtime data come from one artifact, so the compiler and the renderer can never disagree. A typo fails svelte-check at the exact prop — it never becomes a runtime blank, a silent fallback, or a shipped misspelling.

the type-safety law
<Icon name="chevrinRight" />
       ~~~~~~~~~~~~~~~~ Type '"chevrinRight"' is not assignable to
       type 'IconName' — the union is generated with the artifact,
       so a typo is a COMPILE error, never a shipped blank glyph.

The union, the iterable (ICON_NAMES) and the loaders (getIcon / loadIcon / preloadIcons) all ride the one generated module — $lib/icon-set.gen. Its config, budgets and install tiers live on the icons page.

async semantics

Two paths, one box

The default case is synchronous: the artifact's inline chunk answers getIcon() immediately, so server-side rendering paints the glyph and hydration matches with zero wiring. Lazy chunks exist only past the budget (or behind inlineFirstChunk: false) — their pending and rejected markup are the IDENTICAL reserved span, so hydration never rewrites the box and the layout never shifts.

the two paths
import { loadIcon, preloadIcons } from '$lib/icon-set.gen';

// inline-core names answer synchronously — SSR paints them in the
// server HTML, hydration matches, zero wiring owed. Names past the
// chunk budget ride loadIcon(): one cached dynamic import per chunk.
await loadIcon('fileVideo');                     // pull one lazy chunk
await preloadIcons(['folderOpen', 'fileAudio']); // warm a set ahead of a mount

// while pending — and after a rejected chunk — the component renders
// the FIXED reserved box (pending and rejected are the IDENTICAL span,
// so hydration never rewrites it and the layout never shifts):
//   <span data-jx-icon-pending aria-hidden="true"
//     style="display:inline-block;width:20px;height:20px"></span>
// a failed chunk warns once per chunk per session; the box stays.

a11y

Decorative by contract

Every glyph renders aria-hidden — an icon is never the accessible name. The meaning lives in the surrounding text or the control's aria-label.

ARIA

AttributeValueDescription
aria-hidden"true" (baked in)The glyph is decoration; it never joins the accessibility tree and cannot become a name.
aria-labelconsumer-ownedIcon-only controls name themselves (e.g. on the wrapping button) — the component offers no label prop on purpose.
data-jx-iconon the rootStable hook for consumer CSS that must target the rendered svg.

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. §13: size numbers are the axis' number lane VERBATIM (px, same semantics — explicit numbers additionally stamp the size carrier); strings and the absent state (the density ruler's --jx-icon) stay the glyph's own per its recorded no-ambient-size law.

Icon · universal props

icon-universal.svelte svelte
<!-- §13: the number lane verbatim -->
<Icon name="check" size={24} />
<Icon name="check" size="1.25em" />
<Icon name="check" />

api

API

Three props and a verbatim spread — the component owns every other root attribute.

Properties

PropertyTypeDefaultDescription
name * IconName—The glyph's name in the generated set ($lib/icon-set.gen). The union closes at build time — a typo is a compile error.
strokeWidth number | string2Stroke weight — stroke artwork only; inert on fill-nature glyphs.
class string''Lands on the root <svg> verbatim (currentColor theming rides the text color utilities).
...rest SVGAttributes<SVGSVGElement>spreadEvery other attribute lands on the root; the component also owns xmlns, viewBox, fill/stroke by artwork nature, round caps/joins, aria-hidden and data-jx-icon.

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