{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "cubeui",
  "homepage": "https://cubicecho.github.io/cubeui",
  "items": [
    {
      "name": "tokens",
      "type": "registry:file",
      "title": "Tokens",
      "description": "The palette as a NativeWind stylesheet and as TypeScript, both emitted from one source so they cannot drift.",
      "dependencies": [
        "nativewind@^5.0.0-rc.0",
        "react-native-css@3.1.0-rc.0",
        "tailwindcss@^4.3.3"
      ],
      "files": [
        {
          "path": "dist/tokens.native.css",
          "type": "registry:file",
          "target": "~/cubeui-tokens.css"
        },
        {
          "path": "dist/cubeui-theme.ts",
          "type": "registry:lib"
        }
      ]
    },
    {
      "name": "utils",
      "type": "registry:lib",
      "title": "cn",
      "description": "The class merger every component calls, the one hover helper that has to be gated on platform, and the `webAs` declaration the RN→web compiler reads.",
      "dependencies": ["clsx@^2.1.1", "tailwind-merge@^3.6.0"],
      "files": [
        {
          "path": "registry/lib/utils.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/lib/web-as.d.ts",
          "type": "registry:lib"
        }
      ]
    },
    {
      "name": "color",
      "type": "registry:lib",
      "title": "Color Helpers",
      "description": "Derives an accent and a desaturated tint from a user-chosen hex colour.",
      "files": [
        {
          "path": "registry/lib/color.ts",
          "type": "registry:lib"
        }
      ]
    },
    {
      "name": "readable-text-color",
      "type": "registry:lib",
      "title": "Readable Text Color",
      "description": "Picks black or white ink for text sitting on a colour the user chose, by WCAG contrast.",
      "files": [
        {
          "path": "registry/lib/readable-text-color.ts",
          "type": "registry:lib"
        }
      ]
    },
    {
      "name": "format",
      "type": "registry:lib",
      "title": "Format",
      "description": "How a count, a size in bytes, a duration and a date are written for a reader, and how a row of them is joined: formatCount, formatBytes, formatDuration, formatDate, formatAgo and joinStats, on Intl with no dependency.",
      "files": [
        {
          "path": "registry/lib/format.ts",
          "type": "registry:lib"
        }
      ]
    },
    {
      "name": "icons",
      "type": "registry:ui",
      "title": "Icons",
      "description": "The icon set, per-icon imports on native and a tree-shaken barrel on web, with the context that gives native icons a colour to inherit.",
      "dependencies": [
        "lucide-react-native@^1.34.0",
        "lucide-react@^1.34.0",
        "nativewind@^5.0.0-rc.0",
        "react-native-css@3.1.0-rc.0",
        "react-native-svg@^15.13.0",
        "tailwindcss@^4.3.3"
      ],
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/icons-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/icons.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/icons.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "button",
      "type": "registry:ui",
      "title": "Button",
      "description": "A Pressable-backed button whose variants split into container and text halves, because native inherits no text colour.",
      "dependencies": [
        "@types/react-dom@~19.2.0",
        "class-variance-authority@^0.7.1",
        "radix-ui@^1.6.7"
      ],
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons", "@cubeui/spinner"],
      "files": [
        {
          "path": "registry/ui/button.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "card",
      "type": "registry:ui",
      "title": "Card",
      "description": "A card with typed text slots, an optional accent bar, and a whole-card press target that stays reachable by keyboard.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/color-bar"],
      "files": [
        {
          "path": "registry/ui/card.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "alert",
      "type": "registry:ui",
      "title": "Alert",
      "description": "A callout: a tinted, bordered box with an icon, a title, a line under it and an optional action. `default`, `info`, `warning` or `destructive`; only `destructive` is announced as an `alert`, the rest as a polite `status`.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/alert.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "empty",
      "type": "registry:ui",
      "title": "Empty",
      "description": "shadcn's `Empty` parts on both halves, drawn as `EmptyState` is: the muted icon bubble, the title, the line under it and what to do about it. `EmptyState` is built on them.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/empty.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "checkbox",
      "type": "registry:ui",
      "title": "Checkbox",
      "description": "Radix on web, with its whole prop surface; a Pressable announced through role and aria-checked on device. Controlled or uncontrolled on both.",
      "dependencies": ["@types/react-dom@~19.2.0", "radix-ui@^1.6.7"],
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/checkbox-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/checkbox.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/checkbox.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "code",
      "type": "registry:ui",
      "title": "Code",
      "description": "Monospaced text: Code, the inline span, and CodeBlock, a bordered block of preformatted text with an action slot for a CopyButton, a wrap prop to wrap long lines instead of scrolling sideways, and a maxHeight that scrolls.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/code.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "color-bar",
      "type": "registry:ui",
      "title": "Color Bar",
      "description": "The left-edge accent stripe a card wears, readable in both themes and labelled for anyone who cannot see it.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/color"],
      "files": [
        {
          "path": "registry/ui/color-bar.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "color-dot",
      "type": "registry:ui",
      "title": "Color Dot",
      "description": "A small swatch standing in for a colour-coded thing in a list.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/color-dot.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "input",
      "type": "registry:ui",
      "title": "Input",
      "description": "Single-line text entry, with an optional icon at its start (`leadingSlot`) or an icon-sized control at its end (`trailingSlot`). Keeps a real DOM input on web so time, number and min/max survive; a TextInput on device.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/input-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/input.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/input.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "search-input",
      "type": "registry:ui",
      "title": "Search input",
      "description": "A search box: Input with a Search icon inside at its start, type=search (a searchbox), named by `label` (default Search), and a named clear button while it holds text.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/input", "@cubeui/button", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/search-input-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/search-input.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/search-input.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "password-input",
      "type": "registry:component",
      "title": "Password Input",
      "description": "A password box with a reveal button that is named, changes its name, and does not submit the form: Input with type=password (secureTextEntry on device) and a show/hide button in its trailing slot.",
      "registryDependencies": ["@cubeui/input", "@cubeui/button", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/password-input.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "label",
      "type": "registry:ui",
      "title": "Label",
      "description": "A caption for a control. Radix on web so clicking it focuses the control; plain text on native, where that association does not exist.",
      "dependencies": ["@types/react-dom@~19.2.0", "radix-ui@^1.6.7"],
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/label-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/label.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/label.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "textarea",
      "type": "registry:ui",
      "title": "Textarea",
      "description": "Multi-line text entry. Keeps a real DOM textarea on web; a multiline TextInput on device. Takes a submit key, a ref, and maxRows to grow with its text.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/textarea-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/textarea.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/textarea.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "dialog",
      "type": "registry:ui",
      "title": "Dialog",
      "description": "A modal. Radix on web with its focus trap and scroll lock; a transparent native Modal with a dimmed backdrop and a centred card.",
      "dependencies": ["@types/react-dom@~19.2.0", "radix-ui@^1.6.7"],
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons", "@cubeui/button"],
      "files": [
        {
          "path": "registry/ui/dialog-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/dialog.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/dialog.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "popover",
      "type": "registry:ui",
      "title": "Popover",
      "description": "Radix anchored to its trigger on web; a centred native sheet on device, because anchoring is a pointer-era affordance.",
      "dependencies": ["@types/react-dom@~19.2.0", "radix-ui@^1.6.7"],
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/popover-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/popover.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/popover.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "menu",
      "type": "registry:ui",
      "title": "Menu",
      "description": "A popover of actions: radix DropdownMenu on web (arrow keys, typeahead, focus back on the trigger); the popover's centred sheet with menu and menuitem roles on device. A row takes an icon, a label, a trailing node and a destructive colour, and the menu closes when one is chosen. Checkbox rows toggle and keep the menu open; radio rows in a group choose one and close it.",
      "dependencies": ["@types/react-dom@~19.2.0", "radix-ui@^1.6.7"],
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons", "@cubeui/popover"],
      "files": [
        {
          "path": "registry/ui/menu-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/menu.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/menu.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "command",
      "type": "registry:ui",
      "title": "Command",
      "description": "A searchable list of choices: shadcn's Command parts over cmdk on web (arrow keys, Enter, fuzzy ranking); on device a search Input over pressable rows, filtered on every typed word, chosen by press. The same part names and the props MultiSelect uses on both.",
      "dependencies": ["cmdk@^1.1.1"],
      "registryDependencies": ["@cubeui/utils", "@cubeui/dialog", "@cubeui/icons", "@cubeui/input"],
      "files": [
        {
          "path": "registry/ui/command-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/command.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/command.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "select",
      "type": "registry:ui",
      "title": "Select",
      "description": "A radix listbox under its trigger on web; the options in a Modal sheet on device, which is what every native platform does.",
      "dependencies": ["@types/react-dom@~19.2.0", "radix-ui@^1.6.7"],
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/select-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/select.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/select.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "switch",
      "type": "registry:ui",
      "title": "Switch",
      "description": "Radix on web; a Pressable track with a moving thumb on device, rather than RN's Switch, whose raw colour props would not follow the theme.",
      "dependencies": ["@types/react-dom@~19.2.0", "radix-ui@^1.6.7"],
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/switch-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/switch.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/switch.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "tabs",
      "type": "registry:ui",
      "title": "Tabs",
      "description": "Radix on web; a row of Pressables over a context on device. Controlled (value/onValueChange) or uncontrolled (defaultValue) on both. A trigger can carry an icon beside its label, in the tab's active or inactive colour, and a dot or a count at its far end as `trailingSlot`. A list with more tabs than fit scrolls sideways and keeps the selected tab in view.",
      "dependencies": ["@types/react-dom@~19.2.0", "radix-ui@^1.6.7"],
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/tabs-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/tabs.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/tabs.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "tooltip",
      "type": "registry:ui",
      "title": "Tooltip",
      "description": "Radix on hover and focus on web; long-press on device, which is the platform's own way to ask what a control does.",
      "dependencies": ["@types/react-dom@~19.2.0", "radix-ui@^1.6.7"],
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/tooltip-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/tooltip.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/tooltip.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "radio-group",
      "type": "registry:ui",
      "title": "Radio Group",
      "description": "An exclusive choice, one source for both platforms: `row` (circle, label, description), `card` (a tile with an icon) or `segmented` (a full-width framed row, icon-only segments allowed), with one tab stop and arrow keys that move and select handled inside.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/radio-group.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "radio-group-field",
      "type": "registry:component",
      "title": "Radio Group Field",
      "description": "A set of exclusive choices bound to a TanStack form field, the options on the page and each able to carry a line saying what it means. The group is named by its title through aria-labelledby.",
      "dependencies": ["@tanstack/react-form@^1.33.5"],
      "registryDependencies": ["@cubeui/utils", "@cubeui/field", "@cubeui/radio-group"],
      "files": [
        {
          "path": "registry/layout/radio-group-field.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "multi-select",
      "type": "registry:component",
      "title": "Multi Select",
      "description": "A combobox that selects more than one thing, over Popover and Command: searchable on every word, with an optional create row and coloured chips. On the web radix and cmdk make it portalled and keyboard-operable; on device it opens the native sheet, and each chip is also removable with its ✕.",
      "registryDependencies": [
        "@cubeui/utils",
        "@cubeui/badge",
        "@cubeui/button",
        "@cubeui/color-dot",
        "@cubeui/command",
        "@cubeui/icons",
        "@cubeui/popover",
        "@cubeui/readable-text-color"
      ],
      "files": [
        {
          "path": "registry/layout/multi-select.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "option-select",
      "type": "registry:component",
      "title": "OptionSelect",
      "description": "A select taking its choices as options rather than seven primitives to assemble, with groups, separators and notes, and the id and aria props landing on the trigger. Radix's listbox on the web, the Select sheet on device; with searchable, a search box above the list in a popover.",
      "registryDependencies": [
        "@cubeui/button",
        "@cubeui/command",
        "@cubeui/icons",
        "@cubeui/popover",
        "@cubeui/select",
        "@cubeui/utils"
      ],
      "files": [
        {
          "path": "registry/layout/option-select.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "theme-picker",
      "type": "registry:ui",
      "title": "Theme Picker",
      "description": "Light, Dark and System as radio tiles, or with variant=\"compact\" as a full-width row of icon-only radios for a sidebar or header, and useThemePreference(), which stores the choice and applies it: a class on <html> on the web, Appearance.setColorScheme on device, where the app passes its own storage. With palettes={PALETTE_PREFERENCES} it adds a palette choice beside the theme (default, or the dark-only monokai), stored by usePalettePreference() and applied as data-palette on <html> on the web and by PaletteProvider on device. THEME_PRE_PAINT_SCRIPT is the inline <script> that paints the stored theme and palette before React mounts.",
      "dependencies": [
        "nativewind@^5.0.0-rc.0",
        "react-native-css@3.1.0-rc.0",
        "tailwindcss@^4.3.3"
      ],
      "registryDependencies": ["@cubeui/icons", "@cubeui/radio-group", "@cubeui/tokens"],
      "files": [
        {
          "path": "registry/ui/theme-preference-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/theme-preference.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/theme-preference.web.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/theme-picker.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "section",
      "type": "registry:component",
      "title": "Section",
      "description": "A heading over a group of fields or rows: title, description, an action at the far end, an optional rule and an optional card surface. The title is a heading of rank `level` (2 by default), so a long settings page is navigable.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/section.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "list-item",
      "type": "registry:component",
      "title": "List Item",
      "description": "One row of a list: `leadingSlot` (an avatar, a checkbox), a truncating `title` over a `description`, `meta` and `actionSlot` at the far end. With `onPress` the middle is one button and `leadingSlot` and `actionSlot` stay separate controls beside it, never nested in it.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/list-item.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "disclosure",
      "type": "registry:component",
      "title": "Disclosure",
      "description": "A titled part of a screen whose body shows and hides: \"Show completed (3)\", \"Raw output\". The header is one button with a turning chevron and `aria-expanded`, an optional `actionSlot` sits beside it rather than inside it, and `contentSlot` is mounted only while open. Uncontrolled with `defaultOpen`, or controlled with `open`/`onOpenChange`.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/layout/disclosure.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "disclosure-row",
      "type": "registry:component",
      "title": "Disclosure Row",
      "description": "A list row that opens: the whole heading is the button, the actions sit outside it, and the body is Item's footer. Built on the same Item every other row in the list is, with `Disclosure`'s button: `aria-expanded`, a turning chevron, and `contentSlot` mounted only while open. Controlled with `open`/`onOpenChange`.",
      "registryDependencies": ["@cubeui/item", "@cubeui/icons", "@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/disclosure-row.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "description-list",
      "type": "registry:component",
      "title": "Description List",
      "description": "Read-only facts: `DescriptionList` over `PropertyRow`s, each a label, a value, an optional hint under the value and an optional action beside it. A `<dl>` of `<dt>`/`<dd>` groups on the web, `role=\"list\"` on device; `layout=\"inline\"` puts labels beside values and wraps to stacked when narrow.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/description-list.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "setting-row",
      "type": "registry:component",
      "title": "Setting Row",
      "description": "One row of a settings page: a `title` and `description` at the start, the control that changes it in `actionSlot` at the end, dropping under the text when the row is too narrow for both. `actionSlot` may be a function handed the title's id, so a switch or select is named by the title through `aria-labelledby`.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/field"],
      "files": [
        {
          "path": "registry/layout/setting-row.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "stat-tile",
      "type": "registry:component",
      "title": "Stat Tile",
      "description": "One number on a card: a `label`, a large tabular `value`, an optional `hint` under it and an `iconSlot` before the label. Given `onPress` the tile is a button, and with `selected` a toggle (`aria-pressed`) for the filter tile over a list. `loading` holds the figure's place.",
      "registryDependencies": ["@cubeui/card", "@cubeui/icons", "@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/stat-tile.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "action-button",
      "type": "registry:component",
      "title": "Action Button",
      "description": "An icon button with a required accessible name (`label`), and a tooltip that survives being disabled: `disabled` is `aria-disabled`, so the `hint` saying why is still reachable. `onClick` on the web, `onPress` on device, where the tooltip opens on a long press.",
      "registryDependencies": ["@cubeui/button", "@cubeui/tooltip", "@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/action-button.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "confirm-button",
      "type": "registry:component",
      "title": "Confirm Button",
      "description": "A destructive button that asks first, with a required description of what is lost: an `ActionButton` that opens `ConfirmDialog`, with `requireText` to ask for the name before a big delete.",
      "registryDependencies": ["@cubeui/action-button", "@cubeui/confirm-dialog"],
      "files": [
        {
          "path": "registry/layout/confirm-button.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "section-heading",
      "type": "registry:component",
      "title": "Section Heading",
      "description": "The small muted heading above a section of content, with an optional heading `level`.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/section-heading.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "segmented",
      "type": "registry:ui",
      "title": "Segmented",
      "description": "A segmented control: the pill, with an optional icon before its label; the named row it sits in, which can hold the current value and drop the labels to icons under a width; and the two class helpers a router link needs instead.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/segmented.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "toggle-chip",
      "type": "registry:ui",
      "title": "Toggle Chip",
      "description": "A small on/off pill — a filter chip, a day toggle, a swatch — with its colour split across container and label.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/toggle-chip.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "progress",
      "type": "registry:ui",
      "title": "Progress",
      "description": "A horizontal bar showing how much of something is done: `role=\"progressbar\"` with its value announced, `value` from 0 to `max` (100 by default) as shadcn's, clamped. `label` names it; `valueLabel` reads the value in words.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/progress.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "skeleton",
      "type": "registry:ui",
      "title": "Skeleton",
      "description": "shadcn's loading placeholder: a rounded `bg-accent` block the caller sizes, pulsing on both halves (`animate-pulse` on the web, the same opacity curve in `Animated` on device).",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/skeleton-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/skeleton.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/skeleton.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "separator",
      "type": "registry:ui",
      "title": "Separator",
      "description": "shadcn's one-pixel rule, `orientation` and `decorative` as shadcn's: hidden from assistive tech by default, a `role=\"separator\"` with `decorative={false}`.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/separator.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "badge",
      "type": "registry:ui",
      "title": "Badge",
      "description": "A pill naming what something is or what state it is in — and, with no label, the dot that says the same thing in a tighter row. `onRemove` adds a trailing ✕ for a tag the user can take off.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/badge-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/badge.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/badge.web.tsx",
          "type": "registry:ui"
        }
      ],
      "dependencies": [
        "@types/react-dom@~19.2.0",
        "class-variance-authority@^0.7.1",
        "radix-ui@^1.6.7"
      ]
    },
    {
      "name": "copy-button",
      "type": "registry:ui",
      "title": "Copy button",
      "description": "An icon button that copies `value` to the clipboard and turns into a tick for a moment, named `Copy` (or `label`) and then `Copied`.",
      "registryDependencies": ["@cubeui/button", "@cubeui/icons"],
      "dependencies": ["expo-clipboard@^57.0.2"],
      "files": [
        {
          "path": "registry/ui/copy-button-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/copy-button.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/copy-button.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "download-button",
      "type": "registry:ui",
      "title": "Download button",
      "description": "An icon button that saves `source` as a file named `filename`, disabled with a spinner while a fetched `source` is pending, and `downloadBlob()`, the function it calls. The browser's own download on the web; on device the share sheet or a folder the person picks, chosen by `destination` or, by default, from a menu on the button.",
      "registryDependencies": [
        "@cubeui/button",
        "@cubeui/icons",
        "@cubeui/menu",
        "@cubeui/spinner"
      ],
      "dependencies": ["expo-file-system@^57.0.7", "expo-sharing@^57.0.22"],
      "files": [
        {
          "path": "registry/ui/download-button-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/download-button.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/download-button.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "spinner",
      "type": "registry:ui",
      "title": "Spinner",
      "description": "A loading indicator: the LoaderCircle glyph turning, one `role=\"status\"` named by `label`, sized and coloured by `className`.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/spinner-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/spinner.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/spinner.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "item",
      "type": "registry:ui",
      "title": "Item",
      "description": "shadcn's list-row parts — `Item`, `ItemMedia`, `ItemContent`, `ItemTitle`, `ItemDescription`, `ItemActions`, `ItemHeader`, `ItemFooter`, `ItemGroup`, `ItemSeparator` — on both halves, with shadcn's names, props and metrics. For a row `ListItem` does not fit: a header or footer line, a badge beside the title.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/item-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/item.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/item.web.tsx",
          "type": "registry:ui"
        }
      ],
      "dependencies": [
        "@types/react-dom@~19.2.0",
        "class-variance-authority@^0.7.1",
        "radix-ui@^1.6.7"
      ]
    },
    {
      "name": "field",
      "type": "registry:ui",
      "title": "Field",
      "description": "Label, description and error chrome around a control. The unbound half of the form set.",
      "dependencies": ["class-variance-authority@^0.7.1"],
      "registryDependencies": ["@cubeui/utils", "@cubeui/label"],
      "files": [
        {
          "path": "registry/ui/field.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "switch-field",
      "type": "registry:ui",
      "title": "Switch Field",
      "description": "A switch and its caption on one row, with the whole row in the hit target on both platforms.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/label", "@cubeui/switch"],
      "files": [
        {
          "path": "registry/ui/switch-field-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/switch-field.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/switch-field.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "alert-dialog",
      "type": "registry:ui",
      "title": "Alert Dialog",
      "description": "shadcn's modal that interrupts to ask, with the same part names on both halves: radix's `AlertDialog` on the web, the native `Dialog` as an `alertdialog` with no close button on device.",
      "dependencies": ["@types/react-dom@~19.2.0", "radix-ui@^1.6.7"],
      "registryDependencies": ["@cubeui/button", "@cubeui/dialog", "@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/alert-dialog.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/alert-dialog.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "unsaved-changes-guard",
      "type": "registry:component",
      "title": "Unsaved changes guard",
      "description": "useUnsavedChangesGuard() and UnsavedChangesDialog: one question for the three ways out of a page with unsaved edits. It takes the app's router blocker as it comes (TanStack Router's or React Router's, with no router imported), guards the page's own Close button through leave(), and holds a beforeunload listener on the web while there are changes.",
      "registryDependencies": ["@cubeui/confirm-dialog"],
      "files": [
        {
          "path": "registry/layout/unsaved-changes-guard.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "confirm-dialog",
      "type": "registry:ui",
      "title": "Confirm Dialog",
      "description": "A destructive-action confirmation dialog: Cancel plus one destructive button.",
      "registryDependencies": [
        "@cubeui/button",
        "@cubeui/dialog",
        "@cubeui/input",
        "@cubeui/label"
      ],
      "files": [
        {
          "path": "registry/ui/confirm-dialog.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "confirm",
      "type": "registry:ui",
      "title": "Confirm",
      "description": "One confirmation prompt for both platforms, as a promise: `if (await confirm({…})) remove()`.",
      "registryDependencies": ["@cubeui/confirm-dialog"],
      "files": [
        {
          "path": "registry/ui/confirm.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "form-element",
      "type": "registry:ui",
      "title": "Form Element",
      "description": "A real `<form>` on web so Enter submits; a `View` on native, where nothing raises a submit event.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/ui/form-element-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/form-element.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/form-element.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "form",
      "type": "registry:ui",
      "title": "Form",
      "description": "Fields bound to TanStack Form: label, control and error wired once, for both platforms. `useAppForm`, the text, select, checkbox and switch fields, `SubmitButton`, and `bindToForm` for the rest.",
      "dependencies": [
        "@tanstack/react-form@^1.33.5",
        "@types/react-dom@~19.2.0",
        "radix-ui@^1.6.7"
      ],
      "registryDependencies": [
        "@cubeui/utils",
        "@cubeui/button",
        "@cubeui/checkbox",
        "@cubeui/field",
        "@cubeui/form-element",
        "@cubeui/input",
        "@cubeui/label",
        "@cubeui/select",
        "@cubeui/switch",
        "@cubeui/textarea"
      ],
      "files": [
        {
          "path": "registry/ui/form.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "form-dialog",
      "type": "registry:ui",
      "title": "Form Dialog",
      "description": "Dialog chrome for a form, with a footer that leaves the submit control to the caller.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/button", "@cubeui/dialog"],
      "files": [
        {
          "path": "registry/ui/form-dialog.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "toast",
      "type": "registry:ui",
      "title": "Toast",
      "description": "Transient messages for mutations fired outside a form — the failures that otherwise reach only the console.",
      "files": [
        {
          "path": "registry/ui/toast.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "calendar",
      "type": "registry:ui",
      "title": "Calendar",
      "description": "A month of single-date selection: react-day-picker on web, a date-fns grid on device.",
      "dependencies": ["date-fns@^4.4.0", "react-day-picker@^10.0.1"],
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/ui/calendar-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/calendar.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/calendar.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "date-time-input",
      "type": "registry:ui",
      "title": "Date Time Input",
      "description": "A date popover and a time field that commit one Date back — or, with `clearable mode=\"date\"`, an optional date only.",
      "dependencies": ["date-fns@^4.4.0"],
      "registryDependencies": [
        "@cubeui/utils",
        "@cubeui/button",
        "@cubeui/calendar",
        "@cubeui/icons",
        "@cubeui/input",
        "@cubeui/popover"
      ],
      "files": [
        {
          "path": "registry/ui/date-time-input.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "date-picker",
      "type": "registry:component",
      "title": "Date Picker",
      "description": "A date and optionally a time behind a popover, plus a range picker that closes when the range is complete. Clearing is a real button, not one nested in another.",
      "dependencies": ["date-fns@^4.4.0"],
      "registryDependencies": [
        "@cubeui/utils",
        "@cubeui/button",
        "@cubeui/calendar",
        "@cubeui/icons",
        "@cubeui/input",
        "@cubeui/popover"
      ],
      "files": [
        {
          "path": "registry/ui/date-picker.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "color-picker",
      "type": "registry:ui",
      "title": "Color Picker",
      "description": "Swatches plus a hex field, replacing `<input type=\"color\">` — which has no native counterpart.",
      "registryDependencies": [
        "@cubeui/utils",
        "@cubeui/button",
        "@cubeui/input",
        "@cubeui/label",
        "@cubeui/readable-text-color"
      ],
      "files": [
        {
          "path": "registry/ui/color-picker.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "date-time-field",
      "type": "registry:ui",
      "title": "Date Time Field",
      "description": "`DateTimeInput` bound to a `Date | null` form field, named by its label and its date: \"Due September 15th, 2026\". Its own item for the weight of the calendar.",
      "registryDependencies": ["@cubeui/date-time-input", "@cubeui/form"],
      "files": [
        {
          "path": "registry/ui/date-time-field.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "color-picker-field",
      "type": "registry:ui",
      "title": "Color Picker Field",
      "description": "`ColorPicker` bound to a hex-string form field, its swatch row named by the field's label. `ColorField`, under an item name that does not collide with the web-only `color-field`.",
      "registryDependencies": ["@cubeui/color-picker", "@cubeui/form"],
      "files": [
        {
          "path": "registry/ui/color-picker-field.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "file-picker",
      "type": "registry:ui",
      "title": "File Picker",
      "description": "A drop zone, or a button, over a hidden file input on web. The native half is a placeholder, not an implementation.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/icons", "@cubeui/button"],
      "files": [
        {
          "path": "registry/ui/file-picker-base.ts",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/file-picker.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/ui/file-picker.web.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "query-state",
      "type": "registry:component",
      "title": "Query State",
      "description": "The three rungs a list of rows climbs before it is a list of rows — failed, pending, empty — with the error card and the row placeholders exported on their own.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/button", "@cubeui/card", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/layout/query-state.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "page",
      "type": "registry:component",
      "title": "Page",
      "description": "The page shell, its header, the card grid and the empty state — the four pieces every route repeats.",
      "registryDependencies": [
        "@cubeui/utils",
        "@cubeui/icons",
        "@cubeui/empty",
        "@cubeui/page-header"
      ],
      "files": [
        {
          "path": "registry/layout/page.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "detail-page",
      "type": "registry:component",
      "title": "Detail Page",
      "description": "The page shell plus a loading / not-found guard that narrows the entity for its `contentSlot`.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/page"],
      "files": [
        {
          "path": "registry/layout/detail-page.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "detail-header",
      "type": "registry:component",
      "title": "Detail Header",
      "description": "The header row of a detail view: back, colour dot, title, badge, subtitle, actions.",
      "registryDependencies": ["@cubeui/button", "@cubeui/color-dot", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/layout/detail-header.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "route-error",
      "type": "registry:component",
      "title": "Route Error",
      "description": "What shows when the tree below a route has thrown, with nothing in it that could have been the cause. The whole error boundary in one element: an alert with a title, a friendly line, an optional copyable raw message (`details`) and extra buttons beside Try again (`actionsSlot`).",
      "registryDependencies": ["@cubeui/button", "@cubeui/icons"],
      "files": [
        {
          "path": "registry/layout/route-error.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "header-content-footer",
      "type": "registry:component",
      "title": "Header / Content / Footer",
      "description": "The page chassis: chrome that stays, a body that scrolls, floors that let it. Ships the sticky preset alongside the base.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/header-content-footer.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "page-header",
      "type": "registry:component",
      "title": "Page Header",
      "description": "The title block a page wears in a header slot: an optional trail, an icon and title at a chosen heading level, a description, the page's buttons at the far end, and a search or filter row beneath. Carries the inset the page chassis leaves it.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/page-header.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "page-layout",
      "type": "registry:component",
      "title": "Page Layout",
      "description": "A whole page: a PageHeader pinned above a body that scrolls under it, in a named column.",
      "registryDependencies": ["@cubeui/header-content-footer", "@cubeui/page-header"],
      "files": [
        {
          "path": "registry/layout/page-layout.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "split-layout",
      "type": "registry:component",
      "title": "Split Layout",
      "description": "Two panes side by side on a named width scale, with the floors that keep one wide child from pushing the other off the screen. Ships SidebarLayout, the preset for the common case where one pane is the screen and the other is beside it — and, with sidebarHideBelow, the app bar (brand, nav, status, action) that stands in for a navigation rail on a narrow screen.",
      "registryDependencies": ["@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/split-layout.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "top-bar-layout",
      "type": "registry:component",
      "title": "Top Bar Layout",
      "description": "An app shell with a bar across the top and no sidebar: brand at the start, navigation after it that scrolls sideways when narrow, actions at the end, and the page below — a banner and a main landmark, the bar sticky on the web.",
      "registryDependencies": ["@cubeui/utils", "@cubeui/header-content-footer"],
      "files": [
        {
          "path": "registry/layout/top-bar-layout.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "sidebar",
      "type": "registry:component",
      "title": "Sidebar",
      "description": "An app's sidebar: a header, a body that scrolls and a footer on the sidebar palette at a fixed width, with an optional titled list of rows and the nav row itself — a link with an icon, a truncating label, a count and aria-current on the active page. Ships BarNavItem, the same place as an icon-only link for an app bar: a required name that is also its tooltip, the count as a badge on the icon and a status as a dot. A controlled collapsed state folds the sidebar to a rail of icons, with SidebarCollapseButton and useSidebar for what the app puts in it.",
      "registryDependencies": [
        "@cubeui/utils",
        "@cubeui/icons",
        "@cubeui/badge",
        "@cubeui/tooltip",
        "@cubeui/header-content-footer"
      ],
      "files": [
        {
          "path": "registry/layout/sidebar.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "card-layout",
      "type": "registry:component",
      "title": "Card Layout",
      "description": "A card with its slots already placed: icon, title, description, header action, body, and a footer split between prose and buttons.",
      "registryDependencies": ["@cubeui/card", "@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/card-layout.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "centered-layout",
      "type": "registry:component",
      "title": "Centered Layout",
      "description": "A page that is one card, centred both ways: a sign-in, a token gate, a check-your-email screen. Full height, padded off the edges, a `max-w-sm` `CardLayout` taking every card slot; on device a scroll view that keeps the form above the keyboard.",
      "registryDependencies": ["@cubeui/card-layout", "@cubeui/utils"],
      "files": [
        {
          "path": "registry/layout/centered-layout.tsx",
          "type": "registry:component"
        }
      ]
    },
    {
      "name": "dialog-layout",
      "type": "registry:component",
      "title": "Dialog Layout",
      "description": "A dialog whose body scrolls under a header that does not, and which asks before closing on unsaved work. Composes the header/content/footer chassis rather than owning a second copy.",
      "registryDependencies": [
        "@cubeui/button",
        "@cubeui/dialog",
        "@cubeui/header-content-footer",
        "@cubeui/utils"
      ],
      "files": [
        {
          "path": "registry/layout/dialog-layout.tsx",
          "type": "registry:component"
        }
      ]
    }
  ]
}
