PixelSkeleton

Animated loading placeholder that reserves layout space while async content resolves.

  • Width/height props accept any CSS length so blocks can mirror the final content footprint.
  • Pixel and linear surfaces match the rest of the kit — sharp pixel corners or smooth rounded fills.
  • rounded flips between square/avatar shapes (circle on linear, 2px chamfer on pixel).
  • Ships with role="status" and an overridable ariaLabel for screen-reader-friendly loading.
  • Takes any native attribute — drop it anywhere a placeholder block is needed.
Status
stable
Category
feedback
Since
v1.0.0

API

import { PixelSkeleton } from '@pxlkit/ui-kit';
PropTypeDefaultDescription
widthstring—CSS width (e.g. "100%", "12rem").
heightstring'1rem'CSS height (default "1rem").
roundedbooleanfalseWhen true, applies a pill/circle radius instead of the surface default.
surface'pixel' | 'linear'—Surface override; falls back to nearest provider.
ariaLabelstring'Loading'Accessible label override; falls back to "Loading".

Also takes the native attributes and event handlers of <div> (HTMLAttributes<HTMLDivElement>), except role, aria-label.

ref points to <div>.

Accessibility

WCAG target: 2.1 AA

  • status

Renders with role="status" and a polite implicit live region. Override ariaLabel when the placeholder represents a specific resource (e.g. "Loading user profile") so assistive tech announces what is loading. The pulse plays only for a reader who allows motion: under prefers-reduced-motion: reduce the block holds still, from the server-rendered markup on.

Usage

import { PixelSkeleton } from '@pxlkit/ui-kit';

export function Default() {
  return <PixelSkeleton width="12rem" height="1rem" />;
}

Examples

Default

import { PixelSkeleton } from '@pxlkit/ui-kit';

export function Default() {
  return <PixelSkeleton width="12rem" height="1rem" />;
}

Text Block

import { PixelSkeleton } from '@pxlkit/ui-kit';

export function TextBlock() {
  return (
    <div className="flex flex-col gap-2">
      <PixelSkeleton width="14rem" height="0.75rem" />
      <PixelSkeleton width="11rem" height="0.75rem" />
      <PixelSkeleton width="9rem" height="0.75rem" />
    </div>
  );
}

Rounded

import { PixelSkeleton } from '@pxlkit/ui-kit';

export function Rounded() {
  return (
    <div className="flex items-center gap-3">
      <PixelSkeleton width="2.5rem" height="2.5rem" rounded />
      <div className="flex flex-col gap-1.5">
        <PixelSkeleton width="8rem" height="0.75rem" />
        <PixelSkeleton width="5rem" height="0.75rem" />
      </div>
    </div>
  );
}

Surfaces

import { PixelSkeleton } from '@pxlkit/ui-kit';

export function Surfaces() {
  return (
    <div className="flex flex-col gap-3">
      <PixelSkeleton surface="linear" width="14rem" height="1rem" />
      <PixelSkeleton surface="pixel" width="14rem" height="1rem" />
    </div>
  );
}

Card Placeholder

import { PixelSkeleton } from '@pxlkit/ui-kit';

export function CardPlaceholder() {
  return (
    <div className="flex w-72 flex-col gap-3 rounded border border-retro-border/60 p-4">
      <PixelSkeleton width="100%" height="8rem" />
      <PixelSkeleton width="80%" height="0.875rem" />
      <PixelSkeleton width="60%" height="0.75rem" />
    </div>
  );
}

Custom Label

import { PixelSkeleton } from '@pxlkit/ui-kit';

export function CustomLabel() {
  return <PixelSkeleton width="10rem" height="1rem" ariaLabel="Loading user profile" />;
}

Related