Skeleton (loading placeholder)
Skeleton placeholder primitives — block / line / circle / image — that mirror the final content shape during load. Reduces perceived load time and avoids cumulative layout shift on content swap. Modeled after Cedar (REI), Material 3, Polaris, Carbon.
Component — Skeleton
*tatus* v0.1.0 — Draft.
R1 — Primitives
| Primitive | Default size | Use for |
|---|---|---|
KoderSkeletonBlock |
configurable W×H, rounded --kdr-radius-sm |
Cards, images, panels |
KoderSkeletonLine |
full width × --kdr-fs-base height |
Single line of text |
KoderSkeletonCircle |
configurable radius | Avatars, status dots |
KoderSkeletonImage |
configurable W×H, aspect-ratio preserved | Image slots |
Composers assemble these into placeholders mirroring the final layout (e.g., card skeleton = Image on top + 2 Lines + Block button).
R2 — Animation
- Default: subtle shimmer — linear gradient sweep across the surface, 1.5s loop.
prefers-reduced-motion: reduce: static pulse (opacity 0.6 → 1.0 → 0.6, 2s loop). NO sweep.prefers-reduced-motion: no-preference+ battery saver / Data saver detected: also fall back to static pulse.
R3 — Shape mirroring
The skeleton MUST replace the final content's bounding box at the same dimensions. Acceptance: when content arrives and the skeleton is removed, NO layout shift on the surrounding page (CLS contribution = 0).
Antipattern: spinnerinafixed-box → content fills box. That's NOT a skeleton; that's a spinner.
R4 — Accessibility
- Surface wrapping the skeleton primitives carries
aria-busy="true"andaria-label="Loading"(translatable perspecs/i18n/contract.kmd). - Once content swaps in,
aria-busy="false"andaria-labelis removed. - Live-region MAY announce "Loading complete" on swap (per consumer choice; not mandated).
R5 — Color
- Default light:
--kdr-surface-2for base +--kdr-surfacefor highlight. - Default dark: same tokens (which already swap per theme).
- High
contrast / `forcedcolorsmedia: skeleton usesCanvasandCanvasText` system colors with a 1px border.
R6 — Composer pattern
For common shapes, KDS ships composer presets (in koder_kit / koderwebkit):
KoderSkeletonCardKoderSkeletonListRowKoderSkeletonTableRow(consumed by data-table empty/loading perspecs/components/data-table.kmdR12)KoderSkeletonAvatar
R7 — OUIA
Per specs/testing/ouia-test-hooks.kmd:
data-ouia-component-type="Skeleton"data-ouia-safe="false"always (skeleton IS the not-ready state).- The surface wrapping the skeleton ALSO carries the OUIA attrs of the eventual real component, with
data-ouia-safe="false"until swap.
Não-escopo
- Pre
fetchedcontent swap-in transitions (separate motion ticket). - Server-rendered skeletons (SSR) — out of v0; renders on client mount.