# Skeleton (/docs/skeleton)





## API Reference [#api-reference]

`Skeleton` is a moduix loading primitive and does not wrap a Base UI primitive.
Use `Skeleton`, `SkeletonRect`, and `SkeletonCircle` for placeholders, then compose rows and
columns with `SkeletonRow` and `SkeletonColumn`. Skeleton parts are marked `aria-hidden` because
they describe loading shape, not user-readable content.

## Basic [#basic]

<Preview cssProperties="skeletonPlaygroundCssProperties">
  <SkeletonExample />

  <Preview.Code>
    {`
          import { Skeleton, SkeletonColumn } from "moduix";

          export function SkeletonDemo() {
            return (
              <SkeletonColumn gap={10}>
                <Skeleton height={18} />
                <Skeleton height={18} width="86%" />
                <Skeleton height={18} width="64%" />
              </SkeletonColumn>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSSProperties>
    {(context) => <SkeletonCssPropertiesPanel {...context} />}
  </Preview.CSSProperties>

  <Preview.CSSPlayground>
    {(context) => <SkeletonCssPlaygroundPanel {...context} />}
  </Preview.CSSPlayground>
</Preview>

## Anatomy [#anatomy]

`Skeleton` is composed from visual placeholder blocks and optional layout helpers. Use the shape
parts for the visible loading state and the layout parts only when they match the spacing of the
loaded UI.

```text
SkeletonColumn
├─ Skeleton
├─ Skeleton
└─ Skeleton
```

```tsx
<SkeletonColumn gap={10}>
  <Skeleton height={18} />
  <Skeleton height={18} width="86%" />
  <Skeleton height={18} width="64%" />
</SkeletonColumn>
```

| Part             | Role                                                                                            |
| ---------------- | ----------------------------------------------------------------------------------------------- |
| `Skeleton`       | Base placeholder block. Use `width`, `height`, `radius`, `grow`, and `animated` to size it.     |
| `SkeletonRect`   | Rectangular shortcut for cards, media, and panels. It defaults to a taller block height.        |
| `SkeletonCircle` | Circular shortcut for avatars, icons, and round media. It uses one `size` for width and height. |
| `SkeletonRow`    | Horizontal layout helper with `gap`, `pt`, `pb`, and `mobileStack` spacing controls.            |
| `SkeletonColumn` | Vertical layout helper with `gap`, `pt`, `pb`, and `grow` spacing controls.                     |

`Skeleton` does not use portal-like service layers such as `portal`, `positioner`, `backdrop`, or
`viewport`. In most cases, keep the default shape styling and adjust dimensions with props. Use
`className` and the CSS variables only when the placeholder needs to match a custom surface.

## Composition [#composition]

Use `Skeleton` directly for text lines and small custom blocks. Use `SkeletonRect` and
`SkeletonCircle` when the placeholder has a clear geometric shape, then place those shapes inside
`SkeletonRow` or `SkeletonColumn` to mirror the final layout.

All parts accept `className` and standard `div` props. The component exposes `--skeleton-bg`,
`--skeleton-radius`, and `--skeleton-animation` for targeted styling; avoid broad overrides in a
shared parent when a page demonstrates multiple skeleton styles.

## Examples [#examples]

### Card [#card]

Use `SkeletonRect` with smaller line skeletons to reserve the shape of card content while data is loading.

<Preview>
  <SkeletonCardExample />

  <Preview.Code>
    {`
          import { Skeleton, SkeletonColumn, SkeletonRect } from "moduix";
          import styles from "./skeleton-card-demo.module.css";

          export function SkeletonCardDemo() {
            return (
              <div className={styles.card}>
                <SkeletonRect height={148} radius="var(--radius-lg)" />
                <SkeletonColumn gap={12}>
                  <Skeleton height={20} width="70%" />
                  <Skeleton height={14} />
                  <Skeleton height={14} width="82%" />
                </SkeletonColumn>
              </div>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .card {
            display: flex;
            width: min(20rem, calc(100vw - var(--spacing-8)));
            flex-direction: column;
            gap: var(--spacing-4);
          }
        `}
  </Preview.CSS>
</Preview>

### Media Object [#media-object]

Combine `SkeletonCircle`, `SkeletonColumn`, and `grow` when mirroring avatar rows, list items, and comments.

<Preview>
  <SkeletonMediaObjectExample />

  <Preview.Code>
    {`
          import { Skeleton, SkeletonCircle, SkeletonColumn, SkeletonRow } from "moduix";
          import styles from "./skeleton-media-object-demo.module.css";

          export function SkeletonMediaObjectDemo() {
            return (
              <SkeletonRow className={styles.mediaObject} gap={12}>
                <SkeletonCircle size={48} />
                <SkeletonColumn grow gap={8}>
                  <Skeleton height={16} width="46%" />
                  <Skeleton height={14} />
                  <Skeleton height={14} width="72%" />
                </SkeletonColumn>
              </SkeletonRow>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .mediaObject {
            width: min(24rem, calc(100vw - var(--spacing-8)));
          }
        `}
  </Preview.CSS>
</Preview>

### Layout Props [#layout-props]

Use `gap`, `pt`, `pb`, `grow`, and `mobileStack` on the layout primitives when the skeleton must
reserve the same spacing as the loaded UI.

<Preview>
  <SkeletonLayoutPropsExample />

  <Preview.Code>
    {`
          import { Skeleton, SkeletonColumn, SkeletonRow } from "moduix";
          import styles from "./skeleton-layout-props-demo.module.css";

          export function SkeletonLayoutPropsDemo() {
            return (
              <SkeletonColumn className={styles.layoutProps} gap={12} pt={4} pb={4}>
                <SkeletonRow gap={12} mobileStack={false}>
                  <Skeleton width={72} height={48} />
                  <SkeletonColumn grow gap={8}>
                    <Skeleton height={14} width="62%" />
                    <Skeleton height={14} />
                  </SkeletonColumn>
                </SkeletonRow>
                <SkeletonRow gap={12} mobileStack={false}>
                  <Skeleton width={72} height={48} />
                  <SkeletonColumn grow gap={8}>
                    <Skeleton height={14} width="48%" />
                    <Skeleton height={14} />
                  </SkeletonColumn>
                </SkeletonRow>
              </SkeletonColumn>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .layoutProps {
            width: min(24rem, calc(100vw - var(--spacing-8)));
          }
        `}
  </Preview.CSS>
</Preview>

### Static [#static]

Pass `animated={false}` when motion is unnecessary or when the placeholder appears in dense repeated layouts.

<Preview>
  <SkeletonStaticExample />

  <Preview.Code>
    {`
          import { SkeletonRect } from "moduix";

          export function StaticSkeletonDemo() {
            return <SkeletonRect width={320} height={72} animated={false} />;
          }
        `}
  </Preview.Code>
</Preview>

### Class Names [#class-names]

Pass `className` to the root or layout primitives when styling with CSS Modules, Tailwind CSS, or CSS-in-JS.

<Preview>
  <SkeletonClassNameExample />

  <Preview.Code>
    {`
          import { Skeleton, SkeletonColumn } from "moduix";
          import styles from "./custom-skeleton-demo.module.css";

          export function CustomSkeletonDemo() {
            return (
              <SkeletonColumn className={styles.customBlock} gap={10}>
                <Skeleton className={styles.customSkeleton} height={18} />
                <Skeleton className={styles.customSkeleton} height={18} width="78%" />
                <Skeleton className={styles.customSkeleton} height={18} width="52%" />
              </SkeletonColumn>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customBlock {
            display: flex;
            width: min(20rem, calc(100vw - var(--spacing-8)));
            --skeleton-bg: var(--color-primary);
            --skeleton-radius: var(--radius-full);
            --skeleton-animation: none;
          }

          .customSkeleton {
            opacity: 0.28;
          }
        `}
  </Preview.CSS>
</Preview>
