All components smmdlg
Empty State
Zero-data placeholders with an accent indicator, title, description, and actions.
No projects yet
Create your first project to get started.
1234567891011121314151617181920212223242526272829import { EmptyState } from "#components/ui/ark-ui/empty-state.tsx";import { Button } from "#components/ui/ark-ui/button.tsx";import { indigoPalette } from "@stylexblocks/ui/themes/palettes.ts";
export function Example() { return ( // size/align resolve once on Root; every part inherits them via context. // The indicator tints from the palette; Icon sizes an svg to it. <EmptyState.Root size="md" align="center" palette={indigoPalette}> <EmptyState.Content> <EmptyState.Indicator> <EmptyState.Icon viewBox="0 0 24 24" fill="none"> <path d="…" stroke="currentColor" /> </EmptyState.Icon> </EmptyState.Indicator> <EmptyState.Title>No projects yet</EmptyState.Title> <EmptyState.Description> Create your first project to get started. </EmptyState.Description> <EmptyState.Actions> <Button variant="solid" size="xs" palette={indigoPalette}> New project </Button> <Button variant="ghost" size="xs">Import</Button> </EmptyState.Actions> </EmptyState.Content> </EmptyState.Root> );}Sizes
No projects yet
Create your first project to get started.
No projects yet
Create your first project to get started.
No projects yet
Create your first project to get started.
Install
1npx stylexblocks add empty-stateSource
components/ui/ark-ui/empty-state.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227import * as stylex from "@stylexjs/stylex";import { createSlotRecipeContext } from "#lib/ark-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe } from "#lib/recipe-props.ts";import { emptyStateIndicatorIconVars } from '@stylexblocks/ui/slot-recipes/empty-state.stylex.ts'
import { colorPalette } from '@stylexblocks/ui/color-palette.stylex.ts'import { fontSizes } from '@stylexblocks/ui/tokens/font-sizes.stylex.ts'import { fontWeights } from '@stylexblocks/ui/tokens/font-weights.stylex.ts'import { lineHeights } from '@stylexblocks/ui/tokens/line-heights.stylex.ts'import { semanticColors } from '@stylexblocks/ui/semantic-tokens/colors.stylex.ts'import { sizes } from '@stylexblocks/ui/tokens/sizes.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'
const emptyStateRoot = stylex.create({ base: { width: sizes.full, height: sizes.full, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', },})
const emptyStateRootSizes = stylex.create({ sm: { paddingInline: spacing._4, paddingBlock: spacing._6, }, md: { paddingInline: spacing._8, paddingBlock: spacing._12, }, lg: { paddingInline: spacing._12, paddingBlock: spacing._16, },})
const emptyStateContent = stylex.create({ base: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '0.25rem', },})
const emptyStateContentSizes = stylex.create({ sm: { fontSize: fontSizes.xs, lineHeight: lineHeights.xs, }, md: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, lg: { fontSize: fontSizes.md, lineHeight: lineHeights.md, },})
const emptyStateContentAligns = stylex.create({ start: { alignItems: 'flex-start', }, center: { alignItems: 'center', textAlign: 'center', },})
const emptyStateIndicator = stylex.create({ base: { display: 'flex', alignItems: 'center', justifyContent: 'center', color: colorPalette.solid, [emptyStateIndicatorIconVars.width]: '1em', [emptyStateIndicatorIconVars.height]: '1em', },})
const emptyStateIndicatorSizes = stylex.create({ sm: { marginBottom: spacing._2, }, md: { marginBottom: spacing._3, }, lg: { marginBottom: spacing._4, },})
const emptyStateIndicatorIcon = stylex.create({ base: { width: emptyStateIndicatorIconVars.width, height: emptyStateIndicatorIconVars.height, },})
const emptyStateTitle = stylex.create({ base: { fontWeight: fontWeights.medium, },})
const emptyStateTitleSizes = stylex.create({ sm: { fontSize: fontSizes.md, lineHeight: lineHeights.md, }, md: { fontSize: fontSizes.lg, lineHeight: lineHeights.lg, }, lg: { fontSize: fontSizes.xl, lineHeight: lineHeights.xl, },})
const emptyStateDescription = stylex.create({ base: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, color: semanticColors.fgMuted, },})
const emptyStateDescriptionSizes = stylex.create({ sm: { fontSize: fontSizes.xs, lineHeight: lineHeights.xs, }, md: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, lg: { fontSize: fontSizes.md, lineHeight: lineHeights.md, },})
const emptyStateActions = stylex.create({ base: { display: 'flex', flexDirection: 'row', gap: spacing._2, },})
const emptyStateActionsSizes = stylex.create({ sm: { marginTop: spacing._2, }, md: { marginTop: spacing._3, }, lg: { marginTop: spacing._4, },})
export type EmptyStateSize = keyof typeof emptyStateRootSizes
export type EmptyStateAlign = keyof typeof emptyStateContentAligns
// Title/description render semantic <h3>/<p>; the preflight does not zero// heading and paragraph margins, so the component carries its own resets.const emptyStateLocal = stylex.create({ noMargin: { marginBlock: 0 },});
// defineSlotRecipe validates the shape at compile time: default values must// exist in the axis maps, and slot keys must be `styles` or a declared axisconst emptyStateSlotRecipe = defineSlotRecipe({ slots: { root: { styles: emptyStateRoot, sizes: emptyStateRootSizes }, content: { styles: emptyStateContent, sizes: emptyStateContentSizes, aligns: emptyStateContentAligns, }, indicator: { styles: emptyStateIndicator, sizes: emptyStateIndicatorSizes }, title: { styles: emptyStateTitle, sizes: emptyStateTitleSizes }, description: { styles: emptyStateDescription, sizes: emptyStateDescriptionSizes }, actions: { styles: emptyStateActions, sizes: emptyStateActionsSizes }, }, defaultVariants: { size: "md", align: "center" },});
/** * EmptyState — recipe and component colocated in one file. `<EmptyState.Root * size align>` resolves the recipe once and every part inherits it through * context; each part composes via `asChild`. `EmptyState.Icon` sizes an svg * to the indicator's icon custom properties. */const { withProvider, withContext } = createSlotRecipeContext(emptyStateSlotRecipe);
export const EmptyState = { Root: withProvider("div", "root", "EmptyStateRoot"), Content: withContext("div", "content", "EmptyStateContent"), Indicator: withContext("div", "indicator", "EmptyStateIndicator"), Icon: withContext("svg", () => [emptyStateIndicatorIcon.base], "EmptyStateIcon"), Title: withContext( "h3", (v) => [emptyStateTitle.base, emptyStateTitleSizes[v.size], emptyStateLocal.noMargin], "EmptyStateTitle", ), Description: withContext( "p", (v) => [ emptyStateDescription.base, emptyStateDescriptionSizes[v.size], emptyStateLocal.noMargin, ], "EmptyStateDescription", ), Actions: withContext("div", "actions", "EmptyStateActions"),};