Initial Commit
This commit is contained in:
141
packages/ui/react/react-ui/src/components/empty/empty.md
Normal file
141
packages/ui/react/react-ui/src/components/empty/empty.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Empty
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - empty.variants — `emptyVariants` is the cva config……
|
||||
- [empty.variants](./empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
- empty.variants — `emptyVariants` is the cva config……
|
||||
|
||||
- [empty.variants](./empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
|
||||
- empty.variants — `emptyVariants` is the cva config……
|
||||
|
||||
- [empty.variants](./empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
|
||||
- empty.variants — `emptyVariants` is the cva config…
|
||||
|
||||
- [empty.variants](./empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
- [empty.variants](./empty.variants.md) — `emptyVariants` is the cva config that powers the `<Empty>` chassis component (with `emptyHeaderVariants`, `emptyMediaV…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Empty>` is the chassis empty-state primitive for `@suaveplan/react-ui`. It mirrors the upstream shadcn `empty` registry item (per Wave 3a's reconciliation finding #6) and ships as a family of six components — `<Empty>` (root), `<EmptyHeader>`, `<EmptyMedia>`, `<EmptyTitle>`, `<EmptyDescription>`, `<EmptyContent>` — that compose into the conventional empty-state layout. Per the shadcn skill rule, ALWAYS use `<Empty>` for empty states rather than building custom empty-state markup — hand-rolled empty states diverge across teams in spacing, iconography, and CTA layout.
|
||||
|
||||
The root `<Empty>` exposes a `size` axis with three rungs (`sm` / `default` / `lg`) that control vertical breathing room. The component renders `role="status"` with `aria-live="polite"` so assistive tech announces the empty state when it appears (e.g. when a search returns no results) without interrupting the user.
|
||||
|
||||
## Features
|
||||
|
||||
- Six composition primitives — `Empty`, `EmptyHeader`, `EmptyMedia`, `EmptyTitle`, `EmptyDescription`, `EmptyContent`.
|
||||
- Three sizes via the `size` axis (`sm` / `default` / `lg`) on the root.
|
||||
- `role="status"` + `aria-live="polite"` so AT announces the empty state when it appears.
|
||||
- `<EmptyMedia>` is `aria-hidden="true"` because the title / description carries the accessible name.
|
||||
- `<EmptyTitle>` renders as `<h3>`, `<EmptyDescription>` renders as `<p>` so the document outline stays clean.
|
||||
- All sub-components forward refs.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { FolderIcon } from "lucide-react";
|
||||
|
||||
export function NoProjects() {
|
||||
return (
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia>
|
||||
<FolderIcon className="size-6" />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>No projects yet</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
Get started by creating a new project.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
<EmptyContent>
|
||||
<button type="button">Create project</button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from "@suaveplan/react-ui";
|
||||
import { useTranslation } from "@suaveplan/react-i18n";
|
||||
import { InboxIcon } from "lucide-react";
|
||||
|
||||
export function EmptyInbox() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Empty size="lg">
|
||||
<EmptyHeader>
|
||||
<EmptyMedia className="bg-sky-500/10 text-sky-600">
|
||||
<InboxIcon className="size-6" />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>{t("react-ui.empty.defaultTitle")}</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
{t("react-ui.empty.defaultDescription")}
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
</Empty>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Empty>`
|
||||
|
||||
`forwardRef<HTMLDivElement, EmptyProps>` — root container. Renders `role="status"` and `aria-live="polite"`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Vertical breathing room. |
|
||||
| _all_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is. |
|
||||
|
||||
### `<EmptyHeader>`
|
||||
|
||||
`forwardRef<HTMLDivElement, EmptyHeaderProps>` — flex column wrapping `<EmptyMedia>`, `<EmptyTitle>`, `<EmptyDescription>`.
|
||||
|
||||
### `<EmptyMedia>`
|
||||
|
||||
`forwardRef<HTMLDivElement, EmptyMediaProps>` — wrapper for the icon / illustration. Always `aria-hidden="true"`.
|
||||
|
||||
### `<EmptyTitle>`
|
||||
|
||||
`forwardRef<HTMLHeadingElement, EmptyTitleProps>` — heading rendered as `<h3>`.
|
||||
|
||||
### `<EmptyDescription>`
|
||||
|
||||
`forwardRef<HTMLParagraphElement, EmptyDescriptionProps>` — supplementary description rendered as `<p>`.
|
||||
|
||||
### `<EmptyContent>`
|
||||
|
||||
`forwardRef<HTMLDivElement, EmptyContentProps>` — flex row for action affordances.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Empty components do NOT call `useTranslation()` so they do not interact with the i18n provider directly. The chassis ships translation keys (`react-ui.empty.defaultTitle`, `react-ui.empty.defaultDescription`, `react-ui.empty.noResults`) that consumers can use as fallbacks via `t('react-ui.empty.defaultTitle')` at the call site.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `role="status"` + `aria-live="polite"` is set unconditionally on the root so AT announces the empty state when it appears (e.g. when a search returns no results) without interrupting the user. Consumers needing a more aggressive announcement should wrap in their own `aria-live="assertive"` container; consumers wanting silence should pass `role="presentation"` to override.
|
||||
- `<EmptyMedia>` is `aria-hidden="true"` because the icon is decorative — the accessible name comes from the title and description text.
|
||||
- The chassis intentionally does NOT inject a default fallback title / description — the bundled translation keys (`react-ui.empty.defaultTitle`, `react-ui.empty.defaultDescription`) are convenience fallbacks consumers opt into via `t()`. Hardcoding fallback text would force every consumer to translate it even when their own copy already exists.
|
||||
- The `size` axis affects only the root's `gap` and `p` — sub-component spacing remains constant. This keeps the visual rhythm consistent across all empty-state sizes and means consumers can swap `size` without rewriting their composition.
|
||||
- `<EmptyContent>` uses `flex-wrap` so action buttons stack vertically on narrow viewports rather than overflow horizontally.
|
||||
Reference in New Issue
Block a user