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

registry:ui · General

image — the no-CLS native picture

The general-purpose picture: lazy, async-decoded, REQUIRED intrinsic width and height (layout never shifts), alt semantics, failure fallback with src-change recovery. Lightbox is a dialog recipe, not built in.

install — image

$npx jixoai-ui add image

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

no-CLS width/height lazy + async decode failure fallback · recovery

overview

Overview

A native img with three jobs the platform lacks: the no-CLS contract, alt semantics, and a failure posture that recovers.

Image renders one native <img>: loading="lazy", decoding="async", and REQUIRED intrinsic width/ height — the attributes carry the ratio while the rendered box stays max-width: 100% / height: auto, so the layout reserves the box and never shifts. alt is the meaning; alt="" opts into decorative. No lightbox, zoom or galleries — the batch-2 ruling keeps those a dialog composition recipe.

The component's one added job is the failure posture: an onerror swap to a fallback frame (dashed hairline, muted ground, the image glyph) that keeps the intrinsic dims — failure never shifts layout either — and recovers when src changes (avatar's proven law; the consumer's own onerror is composed, never replaced). A fallback snippet replaces the default frame wholesale; keep the intrinsic dims in your slot markup. The §11 carriers JOIN the dimension literals on the broken panel — the merge law — so the axis surface survives failure.

A LEAF in the fleet (grep receipt both directions: only the blueprint scene mounts it, and the family imports nothing but the icon — "on avatar's proven laws" is a law kinship, not a composition). The eight axes resolve on the img root all no-own (census batch D): seven stamp-and-supply, and theme is the frozen pole — the fallback face rides stylex defineVars aliases that resolve at the :root scope, so a dark island re-substitutes nothing (measured below). Per-axis below; the shared grammar lives on the universal props page.

usage

Usage

width and height are REQUIRED — the no-CLS contract is not optional.

Image usage
<script lang="ts">
  import Image from '@ui/image.svelte';
</script>

<!-- width/height are REQUIRED — the no-CLS contract is not optional -->
<Image src="/icon.svg" alt="the jixoai mark" width={96} height={96} />

<!-- alt="" opts into decorative; a failed load swaps to the fallback frame -->
<Image src={maybeBroken} alt="" width={96} height={96} />

image

Left: a real load. Right: a broken source exercising the fallback — the dashed frame plus glyph; a later src change re-arms the load.

the jixoai mark broken demo
image-usage.svelte svelte
<script lang="ts">
  import Image from '@ui/image.svelte';
</script>

<!-- width/height are REQUIRED — the no-CLS contract is not optional -->
<Image src="/icon.svg" alt="the jixoai mark" width={96} height={96} />

<!-- alt="" opts into decorative; a failed load swaps to the fallback frame -->
<Image src={maybeBroken} alt="" width={96} height={96} />

law

The no-CLS contract

The img element carries loading, decoding and alt semantics; the component's one job is the failure posture — an onerror swap to a fallback frame that recovers when src changes. Intrinsic width/height stay REQUIRED: reserve the box, never shift the layout.

usage
<script lang="ts">
  import Image from '@ui/image.svelte';
</script>

<!-- width/height are REQUIRED — the no-CLS contract is not optional -->
<Image src="/icon.svg" alt="the jixoai mark" width={96} height={96} />

<!-- alt="" opts into decorative; a failed load swaps to the fallback frame -->
<Image src={maybeBroken} alt="" width={96} height={96} />
the composed fallback
<script lang="ts">
  import Image from '@ui/image.svelte';
</script>

<!-- composed fallback: keep the intrinsic dims in your slot markup —
     failure never shifts layout either. A changed src re-arms the load. -->
<Image src={flakyUrl} alt="build output" width={640} height={360}>
  {#snippet fallback()}
    <span style="width: 640px; height: 360px; display: grid; place-items: center;">
      retry later
    </span>
  {/snippet}
</Image>

image · composed fallback

default fallback — the dashed frame keeps the dims broken demo
composed fallback slot — your markup, your dims broken demo
image-fallback-demo.svelte svelte
<script lang="ts">
  import Image from '@ui/image.svelte';
</script>

<!-- composed fallback: keep the intrinsic dims in your slot markup —
     failure never shifts layout either. A changed src re-arms the load. -->
<Image src={flakyUrl} alt="build output" width={640} height={360}>
  {#snippet fallback()}
    <span style="width: 640px; height: 360px; display: grid; place-items: center;">
      retry later
    </span>
  {/snippet}
</Image>

api

Props

Props extend the native img attributes (Omit<'alt' | 'width' | 'height' | 'color'> — the required trio re-declared, the native color cleared for the axis); the eight axis rows render into the shared section beneath. 9 content rows here; no EXTRA lane — no family prop shares an axis name.

Properties

PropertyTypeDefaultDescription
alt * string—The picture's meaning; "" marks it decorative.
width * number | string—REQUIRED intrinsic width — the no-CLS contract. Never sees the size axis (the batch A native rule: the family owns only the axis names it destructures).
height * number | string—REQUIRED intrinsic height — the no-CLS contract.
src string—Via the native spread; a CHANGED src re-arms the load after failure (avatar's proven law).
fallback Snippetdefault frameComposed failure state — keep the intrinsic dims in your slot markup; the default frame carries them so failure never shifts layout either.
onerror native handler—COMPOSED, never replaced: your handler fires first, then the failure posture engages.
class string''Rendered width/height classes when different from intrinsic.
style string''Inline style passthrough — composed AFTER the family's carrier stamp (the #4 seam law).
...rest HTMLImgAttributesspreadEvery other native img attribute passes through (srcset, sizes, crossorigin…); Omit clears alt/width/height (re-declared required) and color (the §1 native-collision rule).

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)

axes

The eight axes on image

An all-no-own first-time contract (census batch D, W3-D5) on a media leaf: the img paints pixels and the fallback frame is a fixed face, so nothing the axes know how to move exists here. Seven axes stamp-and-supply — and the supply matters only inside the COMPOSED fallback slot, whose content inherits the re-based channels. Theme is the frozen pole, and image is its purest case: every panel voice is a stylex defineVars alias, resolved at the :root scope before the panel ever inherits it. The carriers stamp the img root and JOIN the dimension literals on the broken panel (the merge law), greppable in the raw SSR.

Reading the table: Property is the axis, Type is the real carrier or consumption it drives on THIS family, Default is the lane default — the named steps, number unit, and consumption are in each description.

PropertyTypeDefaultDescription
size 'small' | 'medium' | 'large' | 'auto' | number'auto'SUPPLY-ONLY, MEASURED INERT ON THE PAINT — the §11 stamp lands on the img root (the computed font-size moves to exactly the stamped px) and NOTHING follows: the box is the width/height ATTRIBUTES' own (the no-CLS contract), the fallback glyph is a fixed 32px prop, and pixels have no em voice (the measured contrast to heading's em ladder). Number unit: px.
shape 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto''auto'SUPPLY-ONLY — stamps --jx-shape-effective and --jx-radius-factor-effective; no family css reads them (grep receipt: zero corner declarations in ui/image/). The img draws no corners; the fallback panel is square. Number unit: none.
radius 'small' | 'medium' | 'large' | 'auto' | number'auto'SUPPLY-ONLY — stamps --jx-radius-effective; no family css reads it (grep receipt: zero readers), and a leaf has no composed children to feed the concentric anchor. Number unit: px.
density 'small' | 'medium' | 'large' | 'auto' | number (+ the five legacy spellings)'auto'SUPPLY-ONLY ON SELF, SUPPLIED TO THE SLOT — data-density stamps the img root AND the broken panel, and the family reads none of the re-based channels (grep receipt: the panel's padding rides the kernel --space-24 step via the --jx-space-24 alias, not the density channels). What the breakpoint moves is the COMPOSED fallback slot's content, which inherits the re-based channels. The merge law keeps the stamp on the panel across failure. Number unit: coefficient.
color 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string'auto'SUPPLY-ONLY — stamps --jx-color-effective; no family css reads it (grep receipt: zero readers). The panel's ink is the muted alias, not the hue carrier. Number unit: hue degrees.
theme 'light' | 'dark' | 'system' | 'auto''auto'THE FROZEN POLE, MEASURED (the emission-form class) — the broken panel's ground/ink/border/padding ride tokens['--jx-muted'] / ['--jx-muted-foreground'] / ['--jx-border'] / ['--jx-space-24']: stylex defineVars aliases, declared (and therefore resolved) at the stylex :root theme scope. The panel inherits the already-substituted light values, so a scoped .dark re-substitutes NOTHING — probe receipt: --muted flips on the panel, --jx-muted does not, and the two panels compute byte-identical faces. The img itself paints pixels — nothing thematic at all. light and system stamp nothing — tree inheritance.
elevation 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number'auto'SUPPLY-ONLY — stamps --jx-elevation-effective; no family css reads it (grep receipt: zero shadow declarations — the fallback frame is flat, no art-shadow recipe). Number unit: dp.
motion 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number'auto'SUPPLY-ONLY — stamps --jx-motion-effective; no family css reads it. Zero transitions: lazy loading and async decode are the platform's own, not the axis's. Number unit: coefficient.

Deviations, cited: the adoption is the census batch D row (the long tail — openspec/changes/explicit-props/research/migration-census.md). The §1 collision rule: the interface is Omit<HTMLImgAttributes, 'alt' | 'width' | 'height' | 'color'> — the required trio re-declared on the family's terms, the native color cleared for the hue lane, and every axis name is the family's own destructured lane. Meta note: image carries no generated meta yet (the ambient-vocabulary carriers bijection doesn't know the family) — this table is the hand-table form; the universal section renders from the shared schema either way.

the eight axes on image
<!-- theme: the FROZEN pole — the broken panel's ground, ink,
     border and padding ride stylex defineVars aliases (declared, and
     therefore resolved, at the stylex :root scope), so a dark island
     re-substitutes NOTHING: the panel is byte-identical -->
<Image src="/definitely-missing.png" alt="dark island" width={96} height={96} theme="dark" />

<!-- size: the stamp lands on the img root and nothing follows — the
     box is the width/height attributes' own, the glyph is a fixed
     prop; pixels have no em voice -->
<Image src="/icon.svg" alt="stamped" width={96} height={96} size={14} />

image · the frozen pole and the inert stamp

broken, light — the ambient profile light fallback
theme="dark" — .dark stamps; the face is byte-identical (the frozen pole) dark island fallback
size auto — the box is the attributes' own auto size
size={14} — the stamp lands; the pixels don't follow (measured) stamped size

The theme pair is the frozen pole, measured: both fallback panels compute the same ground, border and ink — the voices are defineVars aliases resolved at the stylex :root scope (--muted flips on the panel; --jx-muted does not — the same emission-form receipt as hero's lead, with no raw-var voice to flip). The size pair is the inertness probe: the stamped img's computed font-size is exactly 14px while the rendered box stays the attributes' 96×96 — pixels have no em voice. Disclosure: the demos' usage mirrors above are HAND-AUTHORED to match the stage markup (the same-source migration is the recorded follow-up) — the component source in each drawer is the registry's own.

image-axes.svelte svelte
<!-- theme: the FROZEN pole — the broken panel's ground, ink,
     border and padding ride stylex defineVars aliases (declared, and
     therefore resolved, at the stylex :root scope), so a dark island
     re-substitutes NOTHING: the panel is byte-identical -->
<Image src="/definitely-missing.png" alt="dark island" width={96} height={96} theme="dark" />

<!-- size: the stamp lands on the img root and nothing follows — the
     box is the width/height attributes' own, the glyph is a fixed
     prop; pixels have no em voice -->
<Image src="/icon.svg" alt="stamped" width={96} height={96} size={14} />
one real query() case
<script lang="ts">
  import Image from '@ui/image.svelte';
  import { query } from '@lib/universal-props-query.svelte';
  import type { DensityLane } from '@lib/defaults.svelte';
</script>

<!-- below 48rem the base (large rung) applies; at 48rem+ the md case
     wins and the DEFAULT fallback frame stamps sm — the data-density
     on the broken panel flips with the viewport. No fallback snippet
     here on purpose: the snippet branch renders YOUR markup only
     (nothing stamps it). -->
<Image
  src="/definitely-missing.png"
  alt="responsive failure"
  width={640}
  height={360}
  density={query<{ md: DensityLane }, DensityLane>({ md: 'small' }, 'large')}
/>

image · query()

responsive failure

Media keys are min-width: below 48rem the base applies — the large rung; at 48rem and wider the md case wins and the DEFAULT fallback frame stamps sm (the broken panel's data-density flips lg ↔ sm across the breakpoint — the family's own panel, measured). No fallback snippet here ON PURPOSE: the snippet branch renders your markup only — nothing stamps it — so the default frame is the honest demo surface. The string lane takes both generics.

image-query-demo.svelte svelte
<script lang="ts">
  import Image from '@ui/image.svelte';
  import { query } from '@lib/universal-props-query.svelte';
  import type { DensityLane } from '@lib/defaults.svelte';
</script>

<!-- below 48rem the base (large rung) applies; at 48rem+ the md case
     wins and the DEFAULT fallback frame stamps sm — the data-density
     on the broken panel flips with the viewport. No fallback snippet
     here on purpose: the snippet branch renders YOUR markup only
     (nothing stamps it). -->
<Image
  src="/definitely-missing.png"
  alt="responsive failure"
  width={640}
  height={360}
  density={query<{ md: DensityLane }, DensityLane>({ md: 'small' }, 'large')}
/>
TokenDefault
the intrinsic boxwidth/height — REQUIRED attributes
the merge lawcarriers + width/height literals
the fallback facedashed hairline · muted ground · muted-foreground ink
the panel padding--jx-space-24 (24px)
the fallback glyph32px · strokeWidth 1.5
the hooksdata-jx-image · data-jx-image-broken

a11y

Accessibility

A native img with real alt semantics; the fallback keeps the name and stays decorative when alt is empty.

Keyboard

KeyAction
—Not interactive — a picture with alt semantics

ARIA

AttributeValueDescription
altstring (required)The picture's meaning; "" marks it decorative.
role / aria-labelimg / "image unavailable"On the default fallback frame — only when alt is non-empty.
aria-hiddentrueOn the fallback when alt="" keeps the picture decorative through failure.
jixoai-ui ui.jixoai.com the jixoai design language

Theme

Brand hue

0°

Auto cycle · 24h