{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "empty-state",
  "type": "registry:ui",
  "title": "Empty State",
  "description": "The \"nothing here yet\" surface. Used wherever a list, table, or search returns zero rows AND the consumer can act on it (create the first item, adjust filters, link to docs). Empty states are first-class UX, not an afterthought — `LOADING_PHILOSOPHY` flags…",
  "author": "ofri-peretz <https://github.com/ofri-peretz>",
  "categories": [
    "feedback",
    "pattern"
  ],
  "dependencies": [],
  "registryDependencies": [
    "https://ds.interlace.tools/r/theme.json",
    "https://ds.interlace.tools/r/cn.json",
    "https://ds.interlace.tools/r/stack.json",
    "https://ds.interlace.tools/r/typography.json"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/patterns/empty-state.tsx",
      "target": "components/ui/patterns/empty-state.tsx",
      "type": "registry:ui",
      "content": "import * as React from 'react';\n\n// @interlace/empty-state v1.2.0 — Interlace design system.\n// Docs, props and live preview: https://ds.interlace.tools/c/empty-state\n// What changed since: https://ds.interlace.tools/c/empty-state#history\n// Generated banner — keep it, the upgrade diff reads this version.\n\n/**\n * @interlace/ui — EmptyState\n *\n * The \"nothing here yet\" surface. Used wherever a list, table, or search\n * returns zero rows AND the consumer can act on it (create the first item,\n * adjust filters, link to docs). Empty states are first-class UX, not an\n * afterthought — `LOADING_PHILOSOPHY` flags any zero-state without one\n * as a design bug.\n *\n * ## Anatomy\n *\n *   EmptyState                       (block — data-min-viewport=320)\n *     ├─ {icon}                      (optional — usually a lucide outline icon)\n *     ├─ Typography variant=h4       (title)\n *     ├─ Typography variant=body     (description)\n *     └─ {actions}                   (optional CTA cluster, often a Button)\n *\n * ## MIN_VIEWPORT — 320\n *\n * Always usable on phones; this is the failure-mode surface, so it must work\n * on every device.\n *\n * | Rule | Concept                          | Where in this file                                          |\n * | ---- | -------------------------------- | ----------------------------------------------------------- |\n * | R4   | Extends native el                | `React.ComponentProps<'div'> & EmptyStateProps`             |\n * | R6   | data-slot on root                | `data-slot=\"empty-state\"`                                   |\n * | R7   | className merged + ...rest       | `cn(BASE, className)` + `{...props}`                        |\n * | R8   | No isXxx; enums for variants     | n/a — no variants on this block                             |\n * | R10  | Composition seam                 | `icon` + `actions` slots                                    |\n * | R14  | Declares min viewport            | `data-min-viewport={String(MIN_VIEWPORT)}` + exported const |\n * | R18  | Tailwind only                    | Zero inline style                                           |\n * | R19  | Tokens only                      | bg/border/text from semantic tokens                         |\n * | R25  | Server component                 | No hooks → no `'use client'`                                |\n */\n\nimport { cn } from '@/lib/utils';\nimport { Stack } from '@/components/ui/stack';\nimport { Typography } from '@/components/ui/typography';\n\nexport const MIN_VIEWPORT = 320 as const;\n\ntype EmptyStateProps = React.ComponentProps<'div'> & {\n  /** The headline — keep it factual and short. e.g. \"No rules match these filters\". */\n  title: React.ReactNode;\n  /** Supporting copy — describe how the consumer can change the result. */\n  description?: React.ReactNode;\n  /** Optional icon slot — pass a lucide-react icon element. */\n  icon?: React.ReactNode;\n  /** Optional action slot — typically a primary Button + a quiet text link. */\n  actions?: React.ReactNode;\n};\n\nexport function EmptyState({\n  className,\n  title,\n  description,\n  icon,\n  actions,\n  ...props\n}: EmptyStateProps) {\n  return (\n    <div\n      data-slot=\"empty-state\"\n      data-min-viewport={String(MIN_VIEWPORT)}\n      className={cn(\n        'border-border bg-card/40 flex flex-col items-center justify-center rounded-lg border border-dashed px-6 py-12 text-center',\n        className,\n      )}\n      {...props}\n    >\n      <Stack gap=\"md\" align=\"center\" className=\"max-w-112\">\n        {icon ? (\n          <div className=\"text-muted-foreground\" aria-hidden>\n            {icon}\n          </div>\n        ) : null}\n        <Typography as=\"h4\" variant=\"h4\">\n          {title}\n        </Typography>\n        {description ? (\n          <Typography variant=\"body\" tone=\"muted\" align=\"center\">\n            {description}\n          </Typography>\n        ) : null}\n        {actions ? <div className=\"mt-2\">{actions}</div> : null}\n      </Stack>\n    </div>\n  );\n}\n"
    }
  ],
  "meta": {
    "tier": "pattern",
    "client": false,
    "minViewport": 320,
    "loading": false,
    "version": "1.2.0",
    "since": "1.0.0"
  },
  "docs": "## @interlace/empty-state\n\nInstalled to `components/ui/patterns/empty-state.tsx`.\n\n```tsx\nimport { /* … */ } from '@/components/ui/patterns/empty-state';\n```\n\nProps, a11y contract, live preview and source: https://ds.interlace.tools/c/empty-state\n\nRequires the `@interlace/theme` CSS baseline (installed automatically as a registry dependency)."
}
