# Skeleton
> Placeholder while you wait for content to load, or to visualise content that doesn't exist yet.

## Import

```js
import {
  Skeleton,
  SkeletonText,
  SkeletonCircle
} from '@gemini-suite/vera-react';

// you may also access skeletonDisplay util function
// import { skeletonDisplay } from '@gemini-suite/vera-react';
```

## Background

**Skeletons** provide an alternative to the [Spinner component](/components/spinner). Rather than showing an abstract widget, they are used in place of content being loaded creating anticipation of what is to come.

**Skeletons** improve [perceived performance](https://developer.mozilla.org/en-US/docs/Learn/Performance/Perceived_performance), which tends to improve user experience by reducing load time frustration and making the page feel more responsive.

## Examples

### Basic

The default `Skeleton` renders rectangular block.

The `width` will be 100% unless a different width is specified. A standalone `Skeleton` that doesn't wrap any content, inserts a zero-width space character so by default it will have the same height as a single line of text by adopting the `line-height`. You should use the `height` prop to change the height of the `Skeleton`.

```jsx
<Flex flow="column">
  <Skeleton />
  <Skeleton width="50%" height={100} />
  <Skeleton width="20ch" height="$space$spacingXl" />
</Flex>
```

### Text

`SkeletonText` can be used to represent a line or multiple lines of text. Useful for rendering a placeholder for text content such as paragraphs.

`SkeletonText` has rounded corners and provides control of content length. Use `lines` property to simulate size of loaded content.
Last line is visually shorter than others.

```jsx
<Flex flow="column">
  <SkeletonText width="25ch" />
  <SkeletonText lines={4} />
</Flex>
```

Height of `SkeletonText` is the same height as a single line of text unless an explicit `height` is specified. This allows for inheriting the size of text from a parent element.

```jsx
<AutoGrid alignItems="center">
  <Text fontSize="giga" lineHeight="giga">
    <SkeletonText />
  </Text>
  <Text fontSize="giga" lineHeight="giga">
    {'Giga'}
  </Text>
  <Text fontSize="alpha" lineHeight="alpha">
    <SkeletonText />
  </Text>
  <Text fontSize="alpha" lineHeight="alpha">
    {'Alpha'}
  </Text>
  <Text fontSize="gamma" lineHeight="gamma">
    <SkeletonText />
  </Text>
  <Text fontSize="gamma" lineHeight="gamma">
    {'Gamma'}
  </Text>
  <Text variant="zeta">
    <SkeletonText />
  </Text>
  <Box className="zeta">{'Zeta'}</Box>
  <Box css={{ fontSize: '0.5rem' }}>
    <SkeletonText />
  </Box>
  <Box css={{ fontSize: '0.5rem' }}>{'font-size: 0.5rem'}</Box>
</AutoGrid>
```

### Circle

`SkeletonCircle` renders a circular skeleton. Useful as a placeholder for graphic elements such as icons, [avatars](/components/avatar) and circular images. Use `size` property to adjust the size.

```jsx
<Flex>
  <SkeletonCircle />
  <SkeletonCircle size="4rem" />
  <SkeletonCircle size="100px" />
</Flex>
```

### Content wrapping

When part of the content is known you don't have to use standalone **Skeleton** components. In these instances, you can pass children and the **Skeleton** component will infer its size from them.

<Callout variant="tip">

When `Skeleton`, `SkeletonText` or `SkeletonCircle` has content wrapped inside, width is automatically defined by that content thanks to the `width: fit-content` CSS rule.

</Callout>

```jsx
<Flex flow="column">
  <Skeleton>
    <p>Dolor magna ad Lorem laborum id velit voluptate ipsum pariatur.</p>
    <p>Eu non esse enim enim in aliqua anim nisi aliquip.</p>
  </Skeleton>
  <SkeletonText>
    {
      'Ut aliqua non esse aliquip adipisicing culpa irure enim enim culpa ut velit cupidatat.'
    }
  </SkeletonText>
  <SkeletonCircle>
    <Avatar.Root>
      <Avatar.Image src="https://images.unsplash.com/photo-1552058544-f2b08422138a?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=faces&amp;fit=crop&amp;h=200&amp;w=200&amp;ixid=eyJhcHBfaWQiOjE3Nzg0fQ" />
    </Avatar.Root>
  </SkeletonCircle>
</Flex>
```

### Loaded state

Use `isLoaded` property to reveal content wrapped inside the **Skeleton**.

Alternatively use standard [conditional rendering](https://reactjs.org/docs/conditional-rendering.html) to render skeleton placeholder or content.

```jsx
() => {
  const [isLoaded, setIsLoaded] = React.useState(false);

  return (
    <Flex flow="column" gap="spacingXl">
      <Switch.Root isActive={isLoaded} onIsActiveChange={setIsLoaded}>
        <Switch.Label>{'Toggle loaded'}</Switch.Label>
        <Switch.Indicator>
          <Switch.On>{'On'}</Switch.On>
          <Switch.Off>{'Off'}</Switch.Off>
        </Switch.Indicator>
      </Switch.Root>
      <Grid
        columns={{
          '@initial': '1',
          '@mqMediumAndUp': '2'
        }}
      >
        <GridItem>
          <Flex flow="column">
            <Box>Conditional rendering</Box>
            <Box>
              {!isLoaded ? (
                <Skeleton width={200} height={50} />
              ) : (
                <img
                  src="https://dummyimage.com/200x50/f3f5f6/082d35"
                  alt=""
                  className="vaT"
                />
              )}
            </Box>
            <Box className="delta">
              {!isLoaded ? <SkeletonText width="40%" /> : "I'm loaded."}
            </Box>
            <Box>
              {!isLoaded ? (
                <SkeletonCircle />
              ) : (
                <Avatar.Root of="Torvald Halvor">
                  <Avatar.Fallback>
                    <Avatar.Initials />
                  </Avatar.Fallback>
                </Avatar.Root>
              )}
            </Box>
          </Flex>
        </GridItem>
        <GridItem>
          <Flex flow="column">
            <Box>Loaded flag</Box>
            <Skeleton isLoaded={isLoaded}>
              <img
                src="https://dummyimage.com/200x50/f3f5f6/082d35"
                alt=""
                className="vaT"
              />
            </Skeleton>
            <SkeletonText isLoaded={isLoaded} className="delta">
              {"I'm loaded and inside skeleton."}
            </SkeletonText>
            <SkeletonCircle isLoaded={isLoaded}>
              <Avatar.Root of="Torvald Halvor">
                <Avatar.Fallback>
                  <Avatar.Initials />
                </Avatar.Fallback>
              </Avatar.Root>
            </SkeletonCircle>
          </Flex>
        </GridItem>
      </Grid>
    </Flex>
  );
};
```

### Complex example

```jsx
() => {
  const [isLoaded, setIsLoaded] = React.useState(false);

  return (
    <Flex flow="column" gap="spacingXl">
      <Switch.Root isActive={isLoaded} onIsActiveChange={setIsLoaded}>
        <Switch.Label>{'Toggle loaded'}</Switch.Label>
        <Switch.Indicator>
          <Switch.On>{'On'}</Switch.On>
          <Switch.Off>{'Off'}</Switch.Off>
        </Switch.Indicator>
      </Switch.Root>
      <ContentBlock center={false} gutters="none">
        <Flex
          flow="column"
          className="pVm bdrAs bdr-neutral--subtle roundedAm elevationS"
        >
          <SkeletonText isLoaded={isLoaded} className="mHm">
            <h3>Heading</h3>
          </SkeletonText>
          <Skeleton isLoaded={isLoaded} width="100%">
            <AspectRatio ratio={2 / 1}>
              <img src="https://dummyimage.com/400x200/f3f5f6/082d35" alt="" />
            </AspectRatio>
          </Skeleton>
          <Box className="pHm">
            {isLoaded ? (
              'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum dapibus elementum nisi.'
            ) : (
              <SkeletonText lines={2} />
            )}
          </Box>
          <Flex className="pHm" gap="spacingM">
            <SkeletonCircle isLoaded={isLoaded}>
              <Avatar.Root of="Torvald Halvor">
                <Avatar.Image src="https://images.unsplash.com/photo-1552058544-f2b08422138a?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=faces&amp;fit=crop&amp;h=200&amp;w=200&amp;ixid=eyJhcHBfaWQiOjE3Nzg0fQ" />
                <Avatar.Fallback>
                  <Avatar.Initials />
                </Avatar.Fallback>
              </Avatar.Root>
            </SkeletonCircle>
            <SkeletonText isLoaded={isLoaded}>{'Torvald Halvor'}</SkeletonText>
          </Flex>
        </Flex>
      </ContentBlock>
    </Flex>
  );
};
```

### Without animation

You can disable the animation by setting `animation` prop to `false`. This is useful when you want to use **Skeleton** as a static placeholder without any loading effect.

```jsx
<Flex flow="column">
  <Skeleton width="50%" height={50} animation={false} />
  <SkeletonText lines={2} animation={false} />
  <SkeletonCircle size="4rem" animation={false} />
</Flex>
```

#### Display scopes

Use the `skeletonDisplay` utility to control the animation of multiple **Skeleton** components at once. It generates a CSS class to apply to a parent element, cascading the animation settings to all descendant skeletons. This is useful for disabling animations on a group of skeletons without passing `animation={false}` to each one.

```js
import { skeletonDisplay } from '@gemini-suite/vera-react';
```

```jsx
<Table.Root
  aria-label="Placeholder table example with skeleton without animation"
  highlightOnHover={false}
  overflowStrategy="wrap"
  className={skeletonDisplay({ animation: false })}
>
  <Table.Header>
    <Table.ColumnHeader width={250}>
      <SkeletonText />
    </Table.ColumnHeader>
    <Table.ColumnHeader minWidth={200}>
      <SkeletonText />
    </Table.ColumnHeader>
    <Table.ColumnHeader width={140}>
      <SkeletonText />
    </Table.ColumnHeader>
    <Table.ColumnHeader width={80} align="right">
      <SkeletonText />
    </Table.ColumnHeader>
  </Table.Header>
  <Table.Body>
    {[...Array(5)].map((_, index) => (
      <Table.Row key={index}>
        <Table.Cell width={250}>
          <Flex>
            <SkeletonCircle />
            <SkeletonText width="20ch" />
          </Flex>
        </Table.Cell>
        <Table.Cell minWidth={200}>
          <SkeletonText width="50%" />
        </Table.Cell>
        <Table.Cell width={140}>
          <Skeleton width="4ch" />
        </Table.Cell>
        <Table.Cell width={80} align="right">
          <SkeletonText width="4ch" />
        </Table.Cell>
      </Table.Row>
    ))}
  </Table.Body>
</Table.Root>
```

---

## Accessibility features

<ul>
  <li iconName="check">

The animation can be disabled by enabling the `prefers-reduced-motion` setting at the OS level.

  </li>
  <li iconName="check">

Skeleton Loader has the `aria-busy` attribute set to true by default to indicate that the content is loading.

  </li>
</ul>

---

## API Reference

### Skeleton

| Name        | Type                                                      | Default | Description                                                                                                                                                                                                                                                                                  | Required |
| ----------- | --------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`        | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `div`   | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css`       | `StitchesCss`                                             |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                |          |
| `width`     | `StitchesCss['width']`                                    |         | Width of Skeleton box. The width can be any valid value for [CSS width property](https://developer.mozilla.org/en-US/docs/Web/CSS/width) .                                                                                                                                                   |          |
| `height`    | `StitchesCss['height']`                                   |         | Height of Skeleton box. The height can be any valid value for [CSS height property](https://developer.mozilla.org/en-US/docs/Web/CSS/height) .                                                                                                                                               |          |
| `isLoaded`  | `boolean`                                                 | `false` | When `true`, content inside Skeleton is visible.                                                                                                                                                                                                                                             |          |
| `animation` | `boolean`                                                 | `true`  | When `true`, Skeleton has a loading animation.                                                                                                                                                                                                                                               |          |

### SkeletonText

| Name        | Type                                                      | Default | Description                                                                                                                                                                                                                                                                                  | Required |
| ----------- | --------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`        | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `div`   | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css`       | `StitchesCss`                                             |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                |          |
| `lines`     | `number`                                                  |         | Number of lines rendered by SkeletonText. When `lines > 1`, last line has 60% width.                                                                                                                                                                                                         |          |
| `width`     | `StitchesCss['width']`                                    |         | Width of SkeletonText lines wrapper. The width can be any valid value for [CSS width property](https://developer.mozilla.org/en-US/docs/Web/CSS/width) .                                                                                                                                     |          |
| `height`    | `StitchesCss['height']`                                   |         | Height of SkeletonText line. The height can be any valid value for [CSS height property](https://developer.mozilla.org/en-US/docs/Web/CSS/height) .                                                                                                                                          |          |
| `isLoaded`  | `boolean`                                                 | `false` | When `true`, content inside Skeleton is visible.                                                                                                                                                                                                                                             |          |
| `animation` | `boolean`                                                 | `true`  | When `true`, Skeleton has a loading animation.                                                                                                                                                                                                                                               |          |

### SkeletonCircle

| Name        | Type                                                      | Default | Description                                                                                                                                                                                                                                                                                  | Required |
| ----------- | --------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`        | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `div`   | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css`       | `StitchesCss`                                             |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                |          |
| `size`      | `StitchesCss['width']`                                    |         | Size of SkeletonCircle, that covers both its width and height. Accepts any valid [CSS length value](https://developer.mozilla.org/en-US/docs/Web/CSS/length) .                                                                                                                               |          |
| `isLoaded`  | `boolean`                                                 | `false` | When `true`, content inside Skeleton element is visible.                                                                                                                                                                                                                                     |          |
| `animation` | `boolean`                                                 | `true`  | When `true`, Skeleton has a loading animation.                                                                                                                                                                                                                                               |          |
