6.7 KiB
Empty
(no submodules)
- empty.variants — `emptyVariants` is the cva config……-
empty.variants —
emptyVariantsis the cva config that powers the<Empty>chassis component (withemptyHeaderVariants, `emptyMediaV… -
empty.variants —
emptyVariantsis the cva config…… -
empty.variants —
emptyVariantsis the cva config that powers the<Empty>chassis component (withemptyHeaderVariants, `emptyMediaV… -
empty.variants —
emptyVariantsis the cva config…… -
empty.variants —
emptyVariantsis the cva config that powers the<Empty>chassis component (withemptyHeaderVariants, `emptyMediaV… -
empty.variants —
emptyVariantsis the cva config… -
empty.variants —
emptyVariantsis the cva config that powers the<Empty>chassis component (withemptyHeaderVariants, `emptyMediaV… -
empty.variants —
emptyVariantsis the cva config that powers the<Empty>chassis component (withemptyHeaderVariants, `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
sizeaxis (sm/default/lg) on the root. role="status"+aria-live="polite"so AT announces the empty state when it appears.<EmptyMedia>isaria-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
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
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 ownaria-live="assertive"container; consumers wanting silence should passrole="presentation"to override.<EmptyMedia>isaria-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 viat(). Hardcoding fallback text would force every consumer to translate it even when their own copy already exists. - The
sizeaxis affects only the root'sgapandp— sub-component spacing remains constant. This keeps the visual rhythm consistent across all empty-state sizes and means consumers can swapsizewithout rewriting their composition. <EmptyContent>usesflex-wrapso action buttons stack vertically on narrow viewports rather than overflow horizontally.