{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "component-library",
  "homepage": "https://haedong.dev/work",
  "items": [
    {
      "name": "theme",
      "type": "registry:theme",
      "title": "Theme",
      "description": "Monochrome tokens: two base colors (background/foreground), every other shadcn color derived from the foreground at a fixed alpha via color-mix, radius 0. Dark mode swaps the two base colors. Destructive and chart colors are left untouched. Optional — components only need the standard shadcn token names.",
      "cssVars": {
        "light": {
          "background": "#f5f5f0",
          "foreground": "#161615",
          "radius": "0px"
        },
        "dark": {
          "background": "#161615",
          "foreground": "#f5f5f0"
        },
        "theme": {
          "color-card": "var(--card)",
          "color-card-foreground": "var(--card-foreground)",
          "color-popover": "var(--popover)",
          "color-popover-foreground": "var(--popover-foreground)",
          "color-primary": "var(--primary)",
          "color-primary-foreground": "var(--primary-foreground)",
          "color-secondary": "var(--secondary)",
          "color-secondary-foreground": "var(--secondary-foreground)",
          "color-muted": "var(--muted)",
          "color-muted-foreground": "var(--muted-foreground)",
          "color-accent": "var(--accent)",
          "color-accent-foreground": "var(--accent-foreground)",
          "color-border": "var(--border)",
          "color-input": "var(--input)",
          "color-ring": "var(--ring)"
        }
      },
      "css": {
        ":root": {
          "--card": "var(--background)",
          "--card-foreground": "var(--foreground)",
          "--popover": "var(--background)",
          "--popover-foreground": "var(--foreground)",
          "--primary": "var(--foreground)",
          "--primary-foreground": "var(--background)",
          "--secondary": "color-mix(in srgb, var(--foreground) 10%, transparent)",
          "--secondary-foreground": "var(--foreground)",
          "--muted": "color-mix(in srgb, var(--foreground) 5%, transparent)",
          "--muted-foreground": "color-mix(in srgb, var(--foreground) 40%, transparent)",
          "--accent": "color-mix(in srgb, var(--foreground) 10%, transparent)",
          "--accent-foreground": "var(--foreground)",
          "--border": "color-mix(in srgb, var(--foreground) 20%, transparent)",
          "--input": "var(--border)",
          "--ring": "var(--foreground)"
        }
      }
    },
    {
      "name": "use-scramble",
      "type": "registry:hook",
      "title": "useScramble",
      "description": "Character-scramble motion in two modes: decode (useScrambleDecode / useScrambledText, useScrambleReveal for grids) and proximity (useScrambleProximity — only the characters under the cursor scramble and settle back). Skipped under prefers-reduced-motion. Shared motion idiom of every component in this registry.",
      "files": [
        {
          "path": "packages/ui/src/hooks/use-scramble.ts",
          "type": "registry:hook",
          "target": "@hooks/use-scramble.ts"
        }
      ]
    },
    {
      "name": "use-overlay",
      "type": "registry:hook",
      "title": "useOverlay",
      "description": "Modal overlay behavior shared by Sheet and Dialog: Esc closes (capture phase, so nested overlays close one at a time), focus moves in on open and back on close, Tab wraps inside the panel, page scroll locks. Also exports the dither backdrop classes.",
      "files": [
        {
          "path": "packages/ui/src/hooks/use-overlay.ts",
          "type": "registry:hook",
          "target": "@hooks/use-overlay.ts"
        }
      ]
    },
    {
      "name": "button",
      "type": "registry:block",
      "title": "Button",
      "description": "Button in three variants — outline (hairline, inverts on hover), solid (inverted, lifts to outline on hover), ghost (muted, inverts on hover) — and two sizes. Label replays a character-scramble decode on hover and on change. Uses the standard shadcn tokens (primary, border, muted-foreground, ring). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-scramble.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/button/button.tsx",
          "type": "registry:component",
          "target": "@components/button/button.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "calendar",
      "type": "registry:block",
      "title": "Calendar",
      "description": "Calendar with single and range selection and a character-scramble month transition. Keyboard navigable (roving tabindex). Uses the standard shadcn tokens (primary, accent, muted-foreground, ring). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-scramble.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/calendar/calendar.tsx",
          "type": "registry:component",
          "target": "@components/calendar/calendar.tsx"
        },
        {
          "path": "packages/ui/src/calendar/calendar.types.ts",
          "type": "registry:component",
          "target": "@components/calendar/calendar.types.ts"
        },
        {
          "path": "packages/ui/src/calendar/calendar.utils.ts",
          "type": "registry:component",
          "target": "@components/calendar/calendar.utils.ts"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "input",
      "type": "registry:block",
      "title": "Input",
      "description": "Single-line (Input) and multiline (Textarea) text fields. States are expressed by form, not color: focus turns the hairline solid, aria-invalid turns it dashed, disabled dims the field. Textarea grows with its content. Uses the standard shadcn tokens (input, primary, muted-foreground). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/input/input.tsx",
          "type": "registry:component",
          "target": "@components/input/input.tsx"
        },
        {
          "path": "packages/ui/src/input/textarea.tsx",
          "type": "registry:component",
          "target": "@components/input/textarea.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "field",
      "type": "registry:block",
      "title": "Field",
      "description": "Label + control + helper line. Wraps the control in a <label> (no ids needed) and wires the helper through aria-describedby. `error` sets aria-invalid on the control and replaces the description with a full-strength \"!\"-prefixed message — the monochrome error idiom. Pairs with the input item. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/field/field.tsx",
          "type": "registry:component",
          "target": "@components/field/field.tsx"
        }
      ],
      "docs": "Pairs with: npx shadcn@latest add https://components.haedong.dev/r/input.json"
    },
    {
      "name": "checkbox",
      "type": "registry:block",
      "title": "Checkbox",
      "description": "Native checkbox with the system's 9px hairline square indicator that fills when checked; supports indeterminate (bar). Muted at rest, full-strength when checked or hovered. Uses the standard shadcn tokens (muted-foreground, ring). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/checkbox/checkbox.tsx",
          "type": "registry:component",
          "target": "@components/checkbox/checkbox.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "radio",
      "type": "registry:block",
      "title": "Radio",
      "description": "RadioGroup + Radio over native radios: value lifted to the group (value / defaultValue / onValueChange), horizontal or vertical, 9px hairline dot that fills when checked. Keyboard and form behavior come from the browser. Uses the standard shadcn tokens (muted-foreground, ring). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/radio/radio.tsx",
          "type": "registry:component",
          "target": "@components/radio/radio.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "switch",
      "type": "registry:block",
      "title": "Switch",
      "description": "Native checkbox with role=\"switch\" and a 19×11 hairline track; on = the track fills with currentColor and the 5px thumb slides right in the background color. Muted at rest, full-strength when on or hovered. Uses the standard shadcn tokens (muted-foreground, background, ring). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/switch/switch.tsx",
          "type": "registry:component",
          "target": "@components/switch/switch.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "tabs",
      "type": "registry:block",
      "title": "Tabs",
      "description": "Tabs, TabsList, Tab, TabsPanel. Underline form: the list draws one hairline and the selected tab lays a full-strength rule over it. Roving tabindex, arrow/Home/End keys with automatic activation, aria-controls/labelledby wired by id. Labels decode on hover. Uses the standard shadcn tokens (border, primary, muted-foreground, ring). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-scramble.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/tabs/tabs.tsx",
          "type": "registry:component",
          "target": "@components/tabs/tabs.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "table",
      "type": "registry:block",
      "title": "Table",
      "description": "Table, TableHeader, TableBody, TableRow, TableHead, TableCell, TableCaption — a semantic <table> where every row ends in a hairline and the header is muted uppercase. Top-aligned mono cells, horizontal scroll wrapper, optional `striped` zebra (even rows filled with --muted). Uses the standard shadcn tokens (border, muted, muted-foreground). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/table/table.tsx",
          "type": "registry:component",
          "target": "@components/table/table.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "sheet",
      "type": "registry:block",
      "title": "Sheet",
      "description": "Sheet, SheetHeader, SheetTitle, SheetClose — a side panel that is the page surface again (background + one hairline on the open edge) behind a 4px checker-dither backdrop. Portals to body; Esc, backdrop click and Close dismiss; focus, Tab wrap and scroll lock via use-overlay. Uses the standard shadcn tokens (background, foreground, border, muted-foreground, ring). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-overlay.json",
        "https://components.haedong.dev/r/use-scramble.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/sheet/sheet.tsx",
          "type": "registry:component",
          "target": "@components/sheet/sheet.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "dialog",
      "type": "registry:block",
      "title": "Dialog",
      "description": "Dialog, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose — Sheet's centered sibling: the page surface boxed by a hairline on all sides behind the dither backdrop. Portals to body; Esc, backdrop click and Close dismiss; focus, Tab wrap and scroll lock via use-overlay; aria-labelledby/describedby wired by id. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-overlay.json",
        "https://components.haedong.dev/r/use-scramble.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/dialog/dialog.tsx",
          "type": "registry:component",
          "target": "@components/dialog/dialog.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "popover",
      "type": "registry:block",
      "title": "Popover",
      "description": "Popover, PopoverTrigger, PopoverContent, PopoverClose — the system's second surface layer: a hairline box in front of the page, no fill, no shadow, no backdrop. Non-modal: outside click, Esc and focus leaving close it; focus moves in and back to the trigger. Positioned by absolute offsets from the trigger (side bottom/top, align start/center/end). Trigger supports asChild. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-scramble.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/popover/popover.tsx",
          "type": "registry:component",
          "target": "@components/popover/popover.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "badge",
      "type": "registry:block",
      "title": "Badge",
      "description": "Inline label in Button's three resting looks without the hover: outline (hairline box), solid (inverted), ghost (muted). Mono uppercase, 20px tall. Uses the standard shadcn tokens (border, primary, muted-foreground). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/badge/badge.tsx",
          "type": "registry:component",
          "target": "@components/badge/badge.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "separator",
      "type": "registry:block",
      "title": "Separator",
      "description": "The hairline as an element: horizontal <hr>, horizontal with a muted uppercase label set into the rule, or vertical (self-stretch in a flex row). Uses the standard shadcn tokens (border, muted-foreground). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/separator/separator.tsx",
          "type": "registry:component",
          "target": "@components/separator/separator.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "progress",
      "type": "registry:block",
      "title": "Progress",
      "description": "A 9px hairline track filled with the system's 4px dither (var(--border) checker) and a solid foreground bar for the done part; indeterminate pulses the track. role=progressbar with aria-value*. Uses the standard shadcn tokens (border, primary). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/progress/progress.tsx",
          "type": "registry:component",
          "target": "@components/progress/progress.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "skeleton",
      "type": "registry:block",
      "title": "Skeleton",
      "description": "A pulsing placeholder that is nothing but the 4px dither (var(--border) checker); size it with className. aria-hidden — mark the loading region aria-busy instead. Uses the standard shadcn tokens (border). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/skeleton/skeleton.tsx",
          "type": "registry:component",
          "target": "@components/skeleton/skeleton.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "alert",
      "type": "registry:block",
      "title": "Alert",
      "description": "Alert, AlertTitle, AlertDescription — status without color: a hairline box with an ASCII prefix column (i / OK / ? / !); errors get the dashed full-strength border. Errors and warnings are role=alert, info and success role=status. Uses the standard shadcn tokens (border, primary, muted-foreground). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/alert/alert.tsx",
          "type": "registry:component",
          "target": "@components/alert/alert.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "toast",
      "type": "registry:block",
      "title": "Toast",
      "description": "toast() + <Toaster />: Alerts that arrive, stacked in a corner as a non-modal layer (no backdrop, no shadow). Module store read via useSyncExternalStore — no provider; auto-dismiss (default 5s, Infinity to persist) and a Close button. Depends on the alert item.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/alert.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/toast/toast.tsx",
          "type": "registry:component",
          "target": "@components/toast/toast.tsx"
        }
      ],
      "docs": "Render <Toaster /> once in your root layout, then call toast({ title, description?, tone?, duration? }) anywhere."
    },
    {
      "name": "tooltip",
      "type": "registry:block",
      "title": "Tooltip",
      "description": "A one-line hairline box in front of the page (Popover's rule). Shows on hover after a delay or on focus at once; hides on leave, blur and Esc. role=tooltip wired to the trigger with aria-describedby; the single child element is the trigger. Positioned by absolute offsets (side top/bottom, align start/center/end). Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/tooltip/tooltip.tsx",
          "type": "registry:component",
          "target": "@components/tooltip/tooltip.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "dropdown-menu",
      "type": "registry:block",
      "title": "Dropdown Menu",
      "description": "DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator — the popover item with menu semantics: role=menu/menuitem, arrow/Home/End roving (items are not tabbable; Tab leaves and closes), Enter/Space/click selects, closes and returns focus to the trigger. Outside click and Esc come from Popover. Depends on the popover item.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/popover.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/dropdown-menu/dropdown-menu.tsx",
          "type": "registry:component",
          "target": "@components/dropdown-menu/dropdown-menu.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "select",
      "type": "registry:block",
      "title": "Select",
      "description": "Input's box as the trigger, Popover's box as a listbox. Data-driven (options array), value lifted (value / defaultValue / onValueChange), hidden input for forms via name, placeholder, disabled options, aria-invalid dashed state. Arrow keys open and move, Enter/Space select, Esc and outside click close (from Popover). Depends on the popover item.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/popover.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/select/select.tsx",
          "type": "registry:component",
          "target": "@components/select/select.tsx"
        }
      ],
      "docs": "Pairs with: npx shadcn@latest add https://components.haedong.dev/r/field.json"
    },
    {
      "name": "card",
      "type": "registry:block",
      "title": "Card",
      "description": "Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter — the hairline box as a container: one rule around, one between sections (divide-y), no fill, no shadow. Uses the standard shadcn tokens (border, background, muted-foreground). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/card/card.tsx",
          "type": "registry:component",
          "target": "@components/card/card.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "accordion",
      "type": "registry:block",
      "title": "Accordion",
      "description": "Accordion, AccordionItem, AccordionTrigger, AccordionContent — Tabs turned vertical: hairline-separated full-width triggers with an ASCII + / - sign, panels open beneath (role=region, unmount when closed). Single (collapsible or not) or multiple; value lifted as string[]. Arrow/Home/End move between triggers; labels decode on hover. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-scramble.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/accordion/accordion.tsx",
          "type": "registry:component",
          "target": "@components/accordion/accordion.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "slider",
      "type": "registry:block",
      "title": "Slider",
      "description": "A native range input wearing Progress's track: 9px hairline box, solid foreground for the done part, 4px dither for the rest, a 9px square thumb. Value lifted (value / defaultValue / onValueChange as number); keyboard, forms and labelling come from the browser. Uses the standard shadcn tokens (border, primary). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/slider/slider.tsx",
          "type": "registry:component",
          "target": "@components/slider/slider.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "command",
      "type": "registry:block",
      "title": "Command",
      "description": "Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, CommandDialog — a command palette in the terminal voice: a \"> \" prompt line, hairline, filtered groups. Combobox pattern: focus stays in the input, ArrowUp/Down move aria-activedescendant, Enter selects; items filter on value + keywords, empty groups hide. CommandDialog mounts it in the dialog item (⌘K). Depends on the dialog item.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/dialog.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/command/command.tsx",
          "type": "registry:component",
          "target": "@components/command/command.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "scramble-text",
      "type": "registry:block",
      "title": "Scramble Text",
      "description": "ScrambleText + useScrambleChars — the site's signature text: characters scramble under the cursor and settle back, and can decode left-to-right once (landing). Each character in its own span for hit-testing, scrambled frames aria-hidden, settled text in an sr-only span. Depends on use-scramble.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-scramble.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/scramble-text/scramble-text.tsx",
          "type": "registry:component",
          "target": "@components/scramble-text/scramble-text.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "date-picker",
      "type": "registry:block",
      "title": "Date Picker",
      "description": "Select's trigger box + Popover's box holding the Calendar. Single date, value lifted (value / defaultValue / onValueChange), hidden ISO input via name, placeholder, minDate / maxDate / disabledDates / weekStartsOn passed through, Field-compatible aria-invalid / aria-describedby. Picking a day closes and returns focus. Depends on the calendar and popover items.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/calendar.json",
        "https://components.haedong.dev/r/popover.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/date-picker/date-picker.tsx",
          "type": "registry:component",
          "target": "@components/date-picker/date-picker.tsx"
        }
      ],
      "docs": "Pairs with: npx shadcn@latest add https://components.haedong.dev/r/field.json"
    },
    {
      "name": "combobox",
      "type": "registry:block",
      "title": "Combobox",
      "description": "Select with a search line: the same trigger box, a Command (prompt + filtered list) in the popover. Data-driven options with keywords, value lifted, hidden input via name, Field-compatible. Focus lands in the search input on open; arrows/Enter from Command; picking closes and returns focus. Depends on the command and popover items.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/command.json",
        "https://components.haedong.dev/r/popover.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/combobox/combobox.tsx",
          "type": "registry:component",
          "target": "@components/combobox/combobox.tsx"
        }
      ],
      "docs": "Pairs with: npx shadcn@latest add https://components.haedong.dev/r/field.json"
    },
    {
      "name": "alert-dialog",
      "type": "registry:block",
      "title": "Alert Dialog",
      "description": "AlertDialog, AlertDialogTitle (prefixed ? or ! by tone), AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction — a decision that must be answered: role=alertdialog, the backdrop does not dismiss, Esc cancels, focus starts on Cancel. Same surface and dither backdrop as Dialog via use-overlay; tone=error adds the dashed border. Depends on the button item.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-overlay.json",
        "https://components.haedong.dev/r/button.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/alert-dialog/alert-dialog.tsx",
          "type": "registry:component",
          "target": "@components/alert-dialog/alert-dialog.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "code-block",
      "type": "registry:block",
      "title": "Code Block",
      "description": "CodeBlock + CopyButton — a hairline box of mono text that keeps its case, with a muted Copy beside it that decodes into Copied / Failed and settles back (aria-live). CopyButton is exported alone for any copy affordance. Depends on use-scramble.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-scramble.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/code-block/code-block.tsx",
          "type": "registry:component",
          "target": "@components/code-block/code-block.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "kbd",
      "type": "registry:block",
      "title": "Kbd",
      "description": "A key as a key: a 20px hairline box in the mono voice, muted. One key per element. Uses the standard shadcn tokens (border, muted-foreground). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/kbd/kbd.tsx",
          "type": "registry:component",
          "target": "@components/kbd/kbd.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "breadcrumb",
      "type": "registry:block",
      "title": "Breadcrumb",
      "description": "Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator — a path in the mono voice: muted links, / between, the current page full-strength with aria-current. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/breadcrumb/breadcrumb.tsx",
          "type": "registry:component",
          "target": "@components/breadcrumb/breadcrumb.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "toggle-group",
      "type": "registry:block",
      "title": "Toggle Group",
      "description": "Toggle + ToggleGroup — buttons that stay down: Button's outline look with aria-pressed, pressed is the inversion. Standalone (pressed / defaultPressed / onPressedChange) or grouped with shared hairlines and a lifted string[] value (single or multiple). No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/toggle-group/toggle-group.tsx",
          "type": "registry:component",
          "target": "@components/toggle-group/toggle-group.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "pagination",
      "type": "registry:block",
      "title": "Pagination",
      "description": "Page numbers in the site's numbering voice (01 02 03) with arrows, the current page inverted, gaps collapsed to …. page / count / onPageChange / siblings. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/pagination/pagination.tsx",
          "type": "registry:component",
          "target": "@components/pagination/pagination.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "avatar",
      "type": "registry:block",
      "title": "Avatar",
      "description": "A person as a 36px hairline square: the image when it arrives, the dither while it loads, initials when there is none or it fails. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/avatar/avatar.tsx",
          "type": "registry:component",
          "target": "@components/avatar/avatar.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "empty",
      "type": "registry:block",
      "title": "Empty",
      "description": "Empty, EmptyTitle, EmptyDescription, EmptyContent — nothing here, said with the system's empty texture: a hairline box filled with the 4px dither (var(--border) checker) and a small plate on the page surface carrying title, muted description and an action. No icon, no fill color. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/empty/empty.tsx",
          "type": "registry:component",
          "target": "@components/empty/empty.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "spinner",
      "type": "registry:block",
      "title": "Spinner",
      "description": "Progress with no size: one mono character turning through | / - \\ at 10 fps, the terminal's spinner. Inline, text-sized, currentColor — sits in a sentence or a Button. role=status with a fixed sr-only label; frames are aria-hidden; holds one frame under prefers-reduced-motion. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/spinner/spinner.tsx",
          "type": "registry:component",
          "target": "@components/spinner/spinner.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "collapsible",
      "type": "registry:block",
      "title": "Collapsible",
      "description": "Collapsible, CollapsibleTrigger, CollapsibleContent — one Accordion item on its own: an inline text trigger with the ASCII + / - sign that shows or hides a region beneath it (role=region, unmounts when closed). open/defaultOpen/onOpenChange; the label decodes on hover. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/use-scramble.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/collapsible/collapsible.tsx",
          "type": "registry:component",
          "target": "@components/collapsible/collapsible.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "scroll-area",
      "type": "registry:block",
      "title": "Scroll Area",
      "description": "A native scroll container whose scrollbar wears the system's track: a 9px rail with an inner hairline and the 4px dither, and a plate for a thumb — page surface with a full-strength hairline, inverting on hover (::-webkit-scrollbar in Chrome/Safari/Edge; Firefox gets scrollbar-width: thin in the foreground color). Nothing re-implemented — scrolling stays the browser's. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/scroll-area/scroll-area.tsx",
          "type": "registry:component",
          "target": "@components/scroll-area/scroll-area.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "button-group",
      "type": "registry:block",
      "title": "Button Group",
      "description": "Buttons that belong together as one hairline strip: neighbours overlap by a pixel so the rules between them are single, the hovered or focused one lifts above the others. role=group; horizontal or vertical. Holds no state — put Buttons inside. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/button-group/button-group.tsx",
          "type": "registry:component",
          "target": "@components/button-group/button-group.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "input-group",
      "type": "registry:block",
      "title": "Input Group",
      "description": "InputGroup, InputGroupInput, InputGroupAddon — one Input box with a prefix or suffix cell (a > prompt, a unit, a ghost Button) sharing the hairline. The group owns the box: focus turns it full-strength, aria-invalid dashed, a disabled input dims it. aria-invalid / aria-describedby on the group are forwarded to the input, so it drops into a Field. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/input-group/input-group.tsx",
          "type": "registry:component",
          "target": "@components/input-group/input-group.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "theme-toggle",
      "type": "registry:block",
      "title": "Theme Toggle",
      "description": "ThemeToggle (light / dark / system as a RadioGroup) plus themeInitScript() for the root layout. Picking a mode marks <html> in both conventions — data-theme for this registry's theme.css and the .dark class for a stock shadcn theme — stores it under storageKey, syncs every toggle on the page and follows prefers-color-scheme in system mode. Also exports applyTheme / setTheme for custom controls. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/radio.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/theme-toggle/theme-toggle.tsx",
          "type": "registry:component",
          "target": "@components/theme-toggle/theme-toggle.tsx"
        },
        {
          "path": "packages/ui/src/theme-toggle/theme-script.ts",
          "type": "registry:component",
          "target": "@components/theme-toggle/theme-script.ts"
        }
      ],
      "docs": "Inline themeInitScript(storageKey) from theme-toggle/theme-script.ts in your root layout's <head> so the first paint has the right theme. Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "sidebar",
      "type": "registry:block",
      "title": "Sidebar",
      "description": "Sidebar, SidebarGroup, SidebarItem — a column of links under muted group labels: a <nav> on a ScrollArea that sticks to the top and never grows past the viewport (a long menu scrolls inside it), a label + list per group (aria-labelledby), and one link per item — muted, full-strength when active or hovered, aria-current=page; asChild styles your router's Link. No collapse, no mobile drawer. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/scroll-area.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/sidebar/sidebar.tsx",
          "type": "registry:component",
          "target": "@components/sidebar/sidebar.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "typography",
      "type": "registry:block",
      "title": "Typography",
      "description": "The system's two voices as class strings — LABEL (mono 12px uppercase wide, for the shell) and PROSE (14px normal case, for writing), plus CODE (verbatim) — and a Prose container that shapes a document's h1–h3, p, a, ul/ol (ASCII dashes, mono numbers), blockquote, code, pre and hr with hairlines only. Sets no font family: the body face is yours. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/typography/typography.tsx",
          "type": "registry:component",
          "target": "@components/typography/typography.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "input-otp",
      "type": "registry:block",
      "title": "Input OTP",
      "description": "A one-time code as a row of 36px hairline cells, one mono character each, over a single native input (numeric keyboard, autocomplete=one-time-code, paste, forms, screen readers). The active cell goes full-strength with an ASCII _ caret; aria-invalid dashes every cell, so it drops into a Field. value/defaultValue/onValueChange, onComplete, length, pattern. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils"
      ],
      "files": [
        {
          "path": "packages/ui/src/input-otp/input-otp.tsx",
          "type": "registry:component",
          "target": "@components/input-otp/input-otp.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "data-table",
      "type": "registry:block",
      "title": "Data Table",
      "description": "Table with sort, select and page, composed from the registry's Table, Checkbox and Pagination. Data-driven (columns + rows). Sort is drawn in ASCII (^ / v) after the header label with aria-sort on the th; none → asc → desc → none. Selection is a Checkbox column with select-all (indeterminate for a part), lifted as row ids. pageSize adds Pagination under the table. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/table.json",
        "https://components.haedong.dev/r/checkbox.json",
        "https://components.haedong.dev/r/pagination.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/data-table/data-table.tsx",
          "type": "registry:component",
          "target": "@components/data-table/data-table.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    },
    {
      "name": "context-menu",
      "type": "registry:block",
      "title": "Context Menu",
      "description": "ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuLabel, ContextMenuSeparator — a Dropdown Menu that opens where you right-click (or on the ContextMenu / Shift+F10 keys when the area has focus). Items, roving focus, Esc / outside click and menu semantics are the Dropdown Menu's; a zero-size anchor at the pointer gives it its place. Closing returns focus to the area. Uses the standard shadcn tokens. No runtime dependencies beyond React.",
      "registryDependencies": [
        "utils",
        "https://components.haedong.dev/r/dropdown-menu.json"
      ],
      "files": [
        {
          "path": "packages/ui/src/context-menu/context-menu.tsx",
          "type": "registry:component",
          "target": "@components/context-menu/context-menu.tsx"
        }
      ],
      "docs": "Optional monochrome tokens: npx shadcn@latest add https://components.haedong.dev/r/theme.json"
    }
  ]
}
