

<Preview name="EmptyStateVariantsExample" />

## Overview [#overview]

The **EmptyState** component provides a consistent way to communicate that a section has no content to display. By default it fills the available parent space, centers its content, and renders a bordered surface shell.

Use `variant="ghost"` when the parent already owns the surrounding shell. Use `variant="overlay"` when the empty state should sit above existing content, such as a filtered table or a loading result set.

## Usage [#usage]

```tsx
import {
  EmptyState,
  EmptyStateDescription,
  EmptyStateHeading,
  EmptyStateMedia,
  EmptyStateTitle,
} from "@tilt-legal/cubitt-components/empty-state";
import { Inbox } from "@tilt-legal/cubitt-icons/ui/outline";
```

```tsx
<EmptyState>
  <EmptyStateMedia>
    <Inbox />
  </EmptyStateMedia>
  <EmptyStateHeading>
    <EmptyStateTitle>No projects found</EmptyStateTitle>
    <EmptyStateDescription>
      Create your first project to get started with your workflow.
    </EmptyStateDescription>
  </EmptyStateHeading>
</EmptyState>
```

## Surface Behavior [#surface-behavior]

`default` owns a bordered surface at parent +2. `ghost` does not advance or render a surface, so it is best inside another component that already owns structure and spacing. `overlay` is absolutely positioned and uses the current parent surface for its contextual gradient so it blends into the content underneath. If an overlay is mounted in a custom stack and needs a different elevation, override it with `className`.

`ghost` remains the correct name for EmptyState because it means “no shell.” Dropzone uses `minimal` for its own upload-target variant, where pointer and drag states still belong to Dropzone.

## Examples [#examples]

### Variants [#variants]

Switch between the default, ghost, and overlay variants. The overlay preview keeps the table header visible and covers only the skeleton body rows.

<Tabs className="bg-transparent border-none rounded-none" items="['Preview', 'Code']">
  <Tab value="Preview" className="p-0">
    <Preview name="EmptyStateVariantsExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    const variants = ["default", "ghost", "overlay"] as const;
    const [variant, setVariant] =
      useState<"default" | "ghost" | "overlay">("default");

    <>
      {variant === "overlay" ? (
        <div className="relative overflow-hidden">
          <Table>
            <TableHeader>{/* header stays visible */}</TableHeader>
            <TableBody>{/* skeleton rows stay mounted underneath */}</TableBody>
          </Table>
          <EmptyState className="top-11 h-auto p-8" variant="overlay">
            <EmptyStateMedia>
              <Magnifier />
            </EmptyStateMedia>
            <EmptyStateHeading>
              <EmptyStateTitle>No matching matters</EmptyStateTitle>
              <EmptyStateDescription>
                Try adjusting your filters or search terms.
              </EmptyStateDescription>
            </EmptyStateHeading>
          </EmptyState>
        </div>
      ) : (
        <EmptyState className="h-[268px]" variant={variant}>
          <EmptyStateMedia>
            <Inbox />
          </EmptyStateMedia>
          <EmptyStateHeading>
            <EmptyStateTitle>No projects found</EmptyStateTitle>
            <EmptyStateDescription>
              Create your first project to get started with your workflow.
            </EmptyStateDescription>
          </EmptyStateHeading>
        </EmptyState>
      )}
      <PreviewFooter>
        <ToggleGroup
          aria-label="Empty state variant"
          groupVariant="segmented"
          multiple={false}
          onValueChange={(value) => {
            if (variants.includes(value as (typeof variants)[number])) {
              setVariant(value as (typeof variants)[number]);
            }
          }}
          value={variant}
        >
          <ToggleGroupItem value="default">Default</ToggleGroupItem>
          <ToggleGroupItem value="ghost">Ghost</ToggleGroupItem>
          <ToggleGroupItem value="overlay">Overlay</ToggleGroupItem>
        </ToggleGroup>
      </PreviewFooter>
    </>;
    ```
  </Tab>
</Tabs>

### With Action Button [#with-action-button]

Use `EmptyStateAction` to add a call-to-action inside the empty state.

<Tabs className="bg-transparent border-none rounded-none" items="['Preview', 'Code']">
  <Tab value="Preview" className="p-0">
    <Preview name="EmptyStateWithActionExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { Button } from "@tilt-legal/cubitt-components/button";
    import {
      EmptyState,
      EmptyStateAction,
      EmptyStateDescription,
      EmptyStateHeading,
      EmptyStateMedia,
      EmptyStateTitle,
    } from "@tilt-legal/cubitt-components/empty-state";
    import { File, Plus } from "@tilt-legal/cubitt-icons/ui/outline";

    <EmptyState>
      <EmptyStateMedia>
        <File />
      </EmptyStateMedia>
      <EmptyStateHeading>
        <EmptyStateTitle>No documents</EmptyStateTitle>
        <EmptyStateDescription>
          Upload or create your first document to get started.
        </EmptyStateDescription>
      </EmptyStateHeading>
      <EmptyStateAction>
        <Button onClick={() => undefined}>
          <Plus />
          Create Document
        </Button>
      </EmptyStateAction>
    </EmptyState>
    ```
  </Tab>
</Tabs>

### Error State [#error-state]

Set `error` on `EmptyState` to apply error styling to `EmptyStateMedia` and `EmptyStateTitle`.

<Tabs className="bg-transparent border-none rounded-none" items="['Preview', 'Code']">
  <Tab value="Preview" className="p-0">
    <Preview name="EmptyStateErrorExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { Button } from "@tilt-legal/cubitt-components/button";
    import {
      EmptyState,
      EmptyStateAction,
      EmptyStateDescription,
      EmptyStateHeading,
      EmptyStateMedia,
      EmptyStateTitle,
    } from "@tilt-legal/cubitt-components/empty-state";
    import {
      RefreshAnticlockwise,
      TriangleWarning,
    } from "@tilt-legal/cubitt-icons/ui/outline";

    <EmptyState error>
      <EmptyStateMedia>
        <TriangleWarning />
      </EmptyStateMedia>
      <EmptyStateHeading>
        <EmptyStateTitle>Something went wrong</EmptyStateTitle>
        <EmptyStateDescription>
          We couldn't load your data. Please try again.
        </EmptyStateDescription>
      </EmptyStateHeading>
      <EmptyStateAction>
        <Button onClick={() => undefined} variant="secondary">
          <RefreshAnticlockwise />
          Try Again
        </Button>
      </EmptyStateAction>
    </EmptyState>
    ```
  </Tab>
</Tabs>

## API Reference [#api-reference]

### EmptyState [#emptystate]

The root container component. By default it fills the available parent space with centered content and generous padding.

| Prop        | Type                                | Default     | Description                                                                                                                                             |
| ----------- | ----------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `variant`   | `"default" \| "ghost" \| "overlay"` | `"default"` | `default` renders a bordered shell, `ghost` removes shell styling, and `overlay` positions the state absolutely with a contextual parent-level gradient |
| `error`     | `boolean`                           | `false`     | Enables error styling for `EmptyStateMedia` and `EmptyStateTitle`                                                                                       |
| `className` | `string`                            | -           | Additional CSS classes. Use this to adjust fill, spacing, positioning, or overlay elevation                                                             |
| `children`  | `React.ReactNode`                   | -           | Child components                                                                                                                                        |

### EmptyStateMedia [#emptystatemedia]

Container for the icon with a circular background. Automatically applies error styling when the parent has `error`.

| Prop        | Type                   | Default | Description                             |
| ----------- | ---------------------- | ------- | --------------------------------------- |
| `size`      | `"sm" \| "md" \| "lg"` | `"md"`  | Controls the media circle and icon size |
| `className` | `string`               | -       | Additional CSS classes                  |
| `children`  | `React.ReactNode`      | -       | Icon element                            |

### EmptyStateHeading [#emptystateheading]

Container for the title and description.

| Prop        | Type              | Default | Description            |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string`          | -       | Additional CSS classes |
| `children`  | `React.ReactNode` | -       | Title and description  |

### EmptyStateTitle [#emptystatetitle]

The title text. Automatically applies error styling when the parent has `error`.

| Prop        | Type              | Default | Description            |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string`          | -       | Additional CSS classes |
| `children`  | `React.ReactNode` | -       | Title content          |

### EmptyStateDescription [#emptystatedescription]

The description text.

| Prop        | Type              | Default | Description            |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string`          | -       | Additional CSS classes |
| `children`  | `React.ReactNode` | -       | Description content    |

### EmptyStateAction [#emptystateaction]

Container for action buttons.

| Prop        | Type              | Default | Description            |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string`          | -       | Additional CSS classes |
| `children`  | `React.ReactNode` | -       | Button element(s)      |

## Design Guidelines [#design-guidelines]

* Use `default` for standalone empty panels.
* Use `ghost` when a parent component already provides the surface, border, padding, or interaction frame.
* Use `overlay` when the underlying content should remain mounted underneath the empty state.
* Include a clear title and a short description that explains what happened or what the user can do next.
* Add an action when there is an obvious next step.
* Use the error state for failed data loading, not validation errors.
