# Skeleton

A placeholder in the shape of content that is still loading, so the layout doesn't shift when it arrives. For waits longer than a few seconds, show a status message instead.

```tsx
// demo.tsx
import {Heading, Skeleton, SkeletonText, Stack, Text} from '@earendil-works/elanor-react';

export default function SkeletonDemo() {
  return (
    <Stack gap="small" align="stretch" aria-busy>
      <Heading level={2} size={4}>
        <Skeleton>Workspace settings</Skeleton>
      </Heading>
      <Text>
        <SkeletonText lines={3} />
      </Text>
    </Stack>
  );
}
```

## Usage

```tsx
import {Skeleton, SkeletonText} from '@earendil-works/elanor-react';

<Skeleton loading={!user}>{user?.name ?? 'Placeholder name'}</Skeleton>;
<SkeletonText lines={3} />;
```

Skeleton covers its content in `semantic.color.background.skeleton` and pulses with `semantic.transition.pulse`. The content keeps its size but is hidden from screen readers and keyboard focus, so set `aria-busy` on the region that is loading. Skeleton renders a `span`; use `render={<div />}` around blocks.

## Loading

`loading` switches between the placeholder and the content in the same element, without a layout shift.

```tsx
// loading.tsx
import {Button, Card, Skeleton, Stack} from '@earendil-works/elanor-react';
import {useState} from 'react';

export default function SkeletonLoading() {
  const [loading, setLoading] = useState(false);

  return (
    <Stack gap="medium">
      <Skeleton loading={loading} render={<div />}>
        <Card label="Active projects" value="12" description="Across three teams" />
      </Skeleton>
      <Button
        hierarchy="secondary"
        pending={loading}
        onClick={() => {
          setLoading(true);
          setTimeout(() => setLoading(false), 2000);
        }}
      >
        Refresh
      </Button>
    </Stack>
  );
}
```

## Text

SkeletonText shows placeholder lines in the size of the surrounding text. Put it inside [Text](/components/text.md) or [Heading](/components/heading.md). In a [Stack](/components/stack.md), use `align="stretch"` so the lines fill the width.

```tsx
// text.tsx
import {SkeletonText, Text} from '@earendil-works/elanor-react';

export default function SkeletonTextExample() {
  return (
    <Text>
      <SkeletonText lines={4} />
    </Text>
  );
}
```

## API

### `Skeleton`

Covers content that is still loading with a placeholder of its size. Renders a `span` by default.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `loading` | `boolean` | `true` | Covers the content with a placeholder of the same size. |

Renders `<span>`. Also accepts its HTML attributes, `ref`, and `render`.

### `SkeletonText`

Placeholder lines in the size of the surrounding text. Renders a `span` by default.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `lines` | `number` | `1` | Number of placeholder lines. |

Renders `<span>`. Also accepts its HTML attributes, `ref`, and `render`.
