# Skeleton

Ghost lines in the shape of content that is still loading; loading is a ghost, never a spinner.

A skeleton holds the place of text that is on its way, so the page keeps its shape and nothing jumps when the content lands. Ask for about as many lines as the content will take; the web face draws 1 to 12 and clamps anything outside that range. Show loading this way, never with a spinner.

## Use it

Web:

```rust
use ocean_ui_core::Skeleton;
Skeleton { lines: 3 }
```

Native:

```rust
use ocean_ui_native::{Show, core::Skeleton};
Skeleton { lines: 3 }.show(ui);
```

Cargo feature `base`; since 2026-10-29.

## Props

| Name | Type | Default | What |
|---|---|---|---|
| `lines` | `u8` | `—` | How many ghost lines to draw; 0 draws one, and the web face draws at most twelve. |

On the Mac: ![Skeleton, dark](/ui/native/skeleton-dark.png) ![Skeleton, light](/ui/native/skeleton-light.png)

## Examples

### A paragraph

Loading is a ghost of what comes, never a spinner.

```rust
Skeleton { lines: 3 }
```

### One line

A single line for a title or a figure.

```rust
Skeleton { lines: 1 }
```

### In a card

Ghosts keep the card's height, so nothing jumps when rows arrive.

```rust
ocean_ui_web::Card { title: "Recent activity", description: None, action: None }.render(
    out,
    |out| Skeleton { lines: 5 }.html(out),
);
```

## Accessibility

- The whole skeleton is aria-hidden, so a screen reader skips the ghost lines.
- The content that replaces it is read in its place; where the wait matters, say it is loading in words nearby.
- It takes no focus.

## Related

- [Empty](/ui/components/empty.md)
- [Progress](/ui/components/progress.md)
- [Card](/ui/components/card.md)
