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" and aria-label="Loading" (translatable per specs/i18n/contract.kmd).
  • Once content swaps in, aria-busy="false" and aria-label is removed.
  • Live-region MAY announce "Loading complete" on swap (per consumer choice; not mandated).

R5 — Color

  • Default light: --kdr-surface-2 for base + --kdr-surface for highlight.
  • Default dark: same tokens (which already swap per theme).
  • Highcontrast / `forcedcolors media: skeleton uses Canvas and CanvasText` system colors with a 1px border.

R6 — Composer pattern

For common shapes, KDS ships composer presets (in koder_kit / koderwebkit):

  • KoderSkeletonCard
  • KoderSkeletonListRow
  • KoderSkeletonTableRow (consumed by data-table empty/loading per

    specs/components/data-table.kmd R12)

  • 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

  • Prefetchedcontent swap-in transitions (separate motion ticket).
  • Server-rendered skeletons (SSR) — out of v0; renders on client mount.

Source: ../home/koder/dev/koder/meta/docs/stack/specs/components/skeleton.kmd