# GPUIxUI > The documentation for GPUIxUI, a native UI component library for GPUix and React. Components render through GPUix host elements, not the DOM. This is every page the site serves, as Markdown, in the order the sidebar shows them. `/llms.txt` is the same list as an index, one line per page. The site is a browser application documenting a native library, so a browser preview is a labelled approximation of the component unless the page says it mounts the real renderer (`/live`, the wasm build). `.agents/slop/COMPONENTS.md` in the repository — not a page's existence — is the status record for every component. --- # Guide Source: https://gpuixui.sudokw.com/getting-started Add a component, render a window, pick a theme. ## Install [#install] ```sh npx shadcn@latest add @sudokw/primitives-button ``` ```json { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "@gpuix/react" } } ``` React 19. `@gpuix/native` ships prebuilt for Apple Silicon, Linux x64 and Windows. Any other host builds the binding once, as [`apps/playground/README.md`](https://github.com/Cyberistic/GPUIXUI/blob/master/apps/playground/README.md#the-binding-on-this-host) describes. ## First component [#first-component] ```tsx import { render } from "@gpuix/react"; import { Button } from "@gpuixui/primitives"; import { darkTheme } from "@gpuixui/tokens"; import { ThemeProvider } from "@gpuixui/tokens/react"; render( , ); ``` `render` takes **one** element: a fragment at the root paints only its last child. ## Theme [#theme] ```tsx import { getTheme, platformTheme } from "@gpuixui/tokens"; import { ThemeProvider } from "@gpuixui/tokens/react"; const theme = platformTheme(getTheme("dark"), "win32"); {app}; ``` Modes: `light`, `dark`. Contrast: `default`, `high`. Platforms: `darwin`, `win32`, `linux`, `default`. ## Next [#next] - [Components](/components) - [Blocks](/blocks) - [Themes](/themes) - [Registry](/registry) - [Reference](/reference) --- # Omarchy Source: https://gpuixui.sudokw.com/getting-started/omarchy Develop on Hyprland/Arch, and follow the theme the desktop is running. ## Run [#run] ```sh git clone https://github.com/Cyberistic/GPUIXUI.git cd GPUIXUI nub install nub run dev:playground ``` ## Follow the live theme [#follow-the-live-theme] ```tsx import { readFileSync } from "node:fs"; import { decodeOmarchyPalette, paletteToTokens } from "@gpuixui/tokens"; import { ThemeProvider } from "@gpuixui/tokens/react"; const file = decodeOmarchyPalette( readFileSync( `${process.env.HOME}/.local/state/omarchy/current/theme/colors.toml`, "utf8", ), ); {app}; ``` --- # Build prompt Source: https://gpuixui.sudokw.com/getting-started/prompt A copyable build prompt, generated from the pages Paste this into an agent before it starts. It is generated at build time from the pages the site ships: component rows from the Components index, block rows from the block pages' frontmatter. --- # Terminal Source: https://gpuixui.sudokw.com/getting-started/terminal Run the same view inside kitty or Ghostty instead of a window. `render` takes `terminal: true`, and the runtime paints the app into the terminal it was started from: ```tsx import { render } from "@gpuix/react"; render(, { terminal: true }); ``` `GPUIX_TERMINAL=1` sets the same option, so an unchanged entrypoint runs either way: ```sh GPUIX_TERMINAL=1 nub src/main.tsx ``` The playground in this repository: ```sh nub run --filter playground build GPUIX_TERMINAL=1 nub apps/playground/dist/main.js ``` --- # Examples Source: https://gpuixui.sudokw.com/examples Every worked example a package ships, and the file to copy. Each package keeps a worked example beside its components, generated from `packages/*/examples/*.example.tsx`. They are GPUix modules, not browser components: the page shows the source, not a rendering. ## Running one [#running-one] ```bash nub run --filter @gpuixui/primitives test:runtime ``` ```bash nub run --filter playground build nub apps/playground/dist/smoke/run.js --only examples-mount ``` The scenario's checks are in `apps/playground/README.md`. ## Adding one [#adding-one] Create `packages//examples/.example.tsx` exporting one component with a ` example: …` header, and add it to that package's `examples/index.ts`. --- # Introduction Source: https://gpuixui.sudokw.com/introduction Native React components for GPUix **Native components, open source, built on GPUix.** Every component renders through GPUix host elements: no DOM, CSS or browser runtime. ```tsx import { Badge } from "@gpuixui/components/badge"; import { lightTheme } from "@gpuixui/tokens"; import { ThemeProvider } from "@gpuixui/tokens/react"; Connected ; ``` Compound parts validate their placement: a `DialogTitle` inside `Dialog` but outside `DialogContent` throws, and the message names the parent and what to do instead. ```tsx Rename project Rename project Used everywhere the project is named. Cancel Save ; ``` ## Start here [#start-here] Clone the workspace, render the first component and pick a theme. Every component the library ships, with its keyboard contract and API. Whole windows assembled from the library's parts. What the pinned runtime provides, what it does not, and what to do instead. --- # API reference Source: https://gpuixui.sudokw.com/reference Every export the workspace publishes The public API is each package's `exports` map: an import outside it is internal and may move without a major version. Generated from this workspace's source, on `@gpuix/native` 0.11.0 and `@gpuix/react` 0.11.0. The filter travels in the URL (`?q=createTheme`). [`/llms.txt`](/llms.txt) lists every page, grouped the way the sidebar groups it. [`/llms-full.txt`](/llms-full.txt) is the same pages as one Markdown document: prose, API tables and limits. ## Where the signatures live [#where-the-signatures-live] | Question | Where | | -------- | ----- | | The props, defaults and consequences of a part | The component's page under [Components](/components) or [Primitives](/primitives) | | Whether a component ships, and what the runtime measured | [`.agents/slop/COMPONENTS.md`](https://github.com/Cyberistic/GPUIXUI/blob/master/.agents/slop/COMPONENTS.md) | | Colour, type, spacing, radius, density and platform styles | The [guide](/getting-started) and [`docs/tokens.md`](https://github.com/Cyberistic/GPUIXUI/blob/master/docs/tokens.md) | | What the runtime provides and refuses | [`docs/gpuix-capabilities.md`](https://github.com/Cyberistic/GPUIXUI/blob/master/docs/gpuix-capabilities.md) | --- # Registry Source: https://gpuixui.sudokw.com/registry Install a component's source with the shadcn CLI. Every component is a [shadcn registry](https://ui.shadcn.com/docs/registry) item. `shadcn add` copies the source into your project; it renders through GPUix, not the DOM. ## Install [#install] ```sh npx shadcn@latest add @sudokw/primitives-button ``` Or by direct address: ```sh npx shadcn@latest add https://gpuixui.sudokw.com/primitives-button.json ``` That installs the component, its parts, the `@gpuixui/*` runtime and the npm packages they import. Items are `-`; list them with `npx shadcn@latest search @sudokw`. ## Set the JSX runtime [#set-the-jsx-runtime] ```json { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "@gpuix/react" } } ``` ## Render it [#render-it] ```tsx import { render } from "@gpuix/react"; import { Button } from "@/components/ui/gpuixui/primitives/components/Button"; import { ThemeProvider } from "@/components/ui/gpuixui/primitives/components/ThemeProvider"; import { darkTheme } from "@/components/ui/gpuixui/tokens"; render( , ); ``` `ThemeProvider` is required. `@gpuix/native` ships binaries for Apple Silicon, Linux x64 and Windows. Files land under your `ui` alias in `gpuixui/`: ``` src/components/ui/gpuixui/primitives/components/Button.tsx src/components/ui/gpuixui/tokens/index.ts ``` The runtime items (`gpuix-tokens`, `gpuix-tw`, `gpuix-motion`, `gpuix-primitives`) install once per project. The site serves the index at [`/registry.json`](/registry.json) and one document per item at `/.json`. --- # Themes Source: https://gpuixui.sudokw.com/themes Edit the tokens live, pick a look, import a shadcn theme. ## Reading the audit [#reading-the-audit] `mutedForeground on muted — 3.90:1 (needs 4.5:1)`. A ratio that **cannot be computed** prints **unmeasurable** — a wash, not a pass; the prefix names the report it came from. Switch box: `switch height — 22 cannot hold a knob that needs 32` `switch width — 0 cannot be laid out (needs a size greater than zero)` Edits are stored per mode over the platform style; a browser that refuses site storage keeps the editor. **Reset** clears them; **Copy theme** prints it. ## The looks [#the-looks] ## Bring a shadcn theme [#bring-a-shadcn-theme] ```tsx import { shadcnTheme } from "@gpuixui/tokens"; const theme = shadcnTheme(css, "dark"); ``` **Default** is shadcn's neutral theme, audited as warnings. ## styles.css [#stylescss] ## The style reference [#the-style-reference] ### Type scale [#type-scale] ### Spacing, radius and density [#spacing-radius-and-density] ## Blocks [#blocks] --- # Components Source: https://gpuixui.sudokw.com/components Every component the library ships --- # Accordion Source: https://gpuixui.sudokw.com/components/accordion Stacked sections, one or several open ## Installation [#installation] ## Usage [#usage] ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@gpuixui/components/accordion"; What is a GPUix surface? A tree of hosted elements the renderer lays out and paints. Why does the panel animate? Its measured height is the tween target. ; ``` ## Composition [#composition] ```text Accordion ├── AccordionItem │ ├── AccordionTrigger │ └── AccordionContent ``` ## API reference [#api-reference] | Part | Prop | Type | Default | | ---- | ---- | ---- | ------- | | `Accordion` | `type` | `"single" \| "multiple"` | required | | `Accordion` | `collapsible` | `boolean` — single mode only | `false` | | `Accordion` | `value` | `string \| null` (single) · `ReadonlyArray` (multiple) | — ; a stored open set that is not a list means uncontrolled | | `Accordion` | `defaultValue` | the same union, for the uncontrolled form | `null` · `[]` — a stored default that is not a list opens no sections | | `Accordion` | `onValueChange` | `(value: string \| null) => void` · `(values: ReadonlyArray) => void` | — | | `AccordionItem` | `value` | `string` | required | | `AccordionItem` | `disabled` | `boolean` | `false` | | `AccordionTrigger` | `style` | `StateStyle<{ open, disabled, focused }>` | the recipe | | `AccordionContent` | `children` | `ReactNode` | — | | any part | `className` | `string` | — | --- # Alert Source: https://gpuixui.sudokw.com/components/alert A persistent, in-flow message about the current state ## Installation [#installation] ## Usage [#usage] ```tsx import { Alert, AlertContent, AlertDescription, AlertTitle, } from "@gpuixui/components/alert"; 3 tokens are missing The platform themes fall back to the default palette. ; ``` ## Composition [#composition] ```text Alert ├── AlertIcon ├── AlertContent │ ├── AlertTitle │ ├── AlertDescription │ └── AlertLink └── AlertDismiss ``` ## API reference [#api-reference] | Part | Prop | Type | Default | Consequence | | ---- | ---- | ---- | ------- | ----------- | | `Alert` | `tone` | `"info" \| "success" \| "warning" \| "danger"` | `"info"` — a stored name the union does not carry | the surface, edge and readable colour every part inherits | | `Alert` | `children` | parts | — | the composition; a part outside the root throws | | any part | `className` | `string` | — | joined after that part's recipe; `style` still merges last | | `AlertLink` | `onSelect` | `() => void` | — | the text action's handler, on click, `Enter` or `Space` | | `AlertDismiss` | `onDismiss` | `() => void` (required) | — | runs on click, `Enter` or `Space`; there is no optional dismiss | | `AlertDismiss` | `label` | `string` | `"Dismiss"` | the control's accessible name | --- # Aspect ratio Source: https://gpuixui.sudokw.com/components/aspect-ratio A box whose height follows its width through one ratio ## Usage [#usage] ```tsx import { AspectRatio } from "@gpuixui/components/aspect-ratio"; export const Still = () => (
); ``` ## Composition [#composition] ```text AspectRatio └── content ``` ## API [#api] | Part | Prop | Type | Default | | ---- | ---- | ---- | ------- | | `AspectRatio` | `ratio` | `number` — width divided by height | `1` | | `AspectRatio` | `className` | `string` | — | | `AspectRatio` | `style` | `StyleDesc` — merged last | — | | `AspectRatio` | `children` | `ReactNode` — the content that fills the box | — | | `AspectRatio` | `testId` | `string` | — | --- # Auth Source: https://gpuixui.sudokw.com/components/auth Sign in, sign up, reset and one-time codes ### Sign in [#sign-in] ### Sign up [#sign-up] ### Forgot password [#forgot-password] ### One-time code [#one-time-code] ## Installation [#installation] ## Usage [#usage] ```tsx import { Button, Input, Label, Separator, Stack, Text } from "@gpuixui/primitives"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@gpuixui/components/card"; export function SignInCard() { return ( Login to your account Enter your email below to login to your account Don't have an account? ); } ``` ## Composition [#composition] | Part | Responsibility | | ---- | -------------- | | `Card` | the centred surface, `variant="outline"` | | `CardHeader` / `CardTitle` / `CardDescription` | the mark, the title and the sentence under it | | `CardContent` | the field column, the actions and the divider | | `CardFooter` | the route to the neighbouring card | | `Label` / `Input` | one named field | | `Checkbox` | the terms gate and the remembered session | | `OTPField` | the six code cells | | `Separator` | the "or continue with" divider | | `Button` | the primary action, the providers, the links and the resend control | | `Text` | the footer sentence and the inline note | ## API reference [#api-reference] Each recipe adds its own callback set. ## Recipes [#recipes] | Member | Contract | | ------ | -------- | | `LoginPage` | `onSubmit`, `onProvider`, `pending`, `error` | | `SignupPage` | `onSubmit`, `pending`, `error` | | `ForgotPasswordPage` | `onSubmit`, `pending` | | `OtpPage` | `onSubmit`, `onResend` | | `SignInPage` | the full page — navbar, form card and quote panel — over the same callbacks | ## Keyboard [#keyboard] | Key | Contract | | --- | -------- | | `Tab` | walks fields, links, the primary action, both providers and the footer in order | | `Enter` | submits the focused form; the primary action carries the busy state | | `Space` | toggles the focused checkbox; the terms gate is submitted with the form | | `Backspace` | in an empty one-time-code cell, moves focus to the previous cell | | `Tab` from a code cell | leaves the group; the cells are one tab stop each, not a roving group | --- # Auto height Source: https://gpuixui.sudokw.com/components/auto-height A reveal that grows to its content's measured height ## Usage [#usage] ```tsx import { AutoHeight } from "@gpuixui/components/auto-height"; Retry budget: 5 attempts Timeout: 30 seconds ; ``` ## API [#api] | Prop | Type | Default | | ---- | ---- | ------- | | `children` | `ReactNode` | — | | `open` | `boolean` (required) | — | | `level` | `MotionLevel` | `"normal"` | | `timing` | `SurfaceTiming` | the `level`'s | | `className` | `string` | — | | `contentClassName` | `string` | — | | `style` | `StateStyle` | — | ## Motion [#motion] The box grows to the content's measured height on the `normal` level (200 ms): it eases out on the way open and in on the way back. A caller that moves a box between two resting states passes its own `timing`, so the height and the fade paint on one curve. The content fades in `20 ms` after the height starts, so the two do not race, and it fades out with the closing box. The subtree stays mounted for the exit window and unmounts after it. Under `MotionProvider preference="reduced"` the duration is zero and the measured height paints at once. --- # Avatar Source: https://gpuixui.sudokw.com/components/avatar A person or team as a picture, falling back to initials ## Installation [#installation] ## Usage [#usage] ```tsx import { Avatar } from "@gpuixui/components/avatar"; ; // Without a picture the initials are derived from the name. ; ``` ## API reference [#api-reference] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `name` | `string` | — (required) | the accessible label **and** the initials' source | | `src` | `string` | — | a path, data URL or `http(s)` URL; without it the initials paint | | `size` | `"xs" \| "sm" \| "md" \| "lg"` | `"md"` | the diameter from `avatarSize`; a stored name the scale does not carry falls back to `md` | | `status` | `"online" \| "away" \| "busy" \| "offline"` | — | paints the dot and becomes the node's description; a stored name the union does not carry paints no dot | | `color` | `string` | the muted role | fills the disc | | `initials` | `boolean` | `true` | `false` paints a plain colour dot; the name stays the accessible one | | `radius` | `number` | a circle | a rounded square in place of the circle | | `clickThrough` | `boolean` | on, unless the avatar has its own press handler | lets a press fall through to the row, card or trigger the avatar sits in; an avatar given `onClick` keeps its press | | `className` | `string` | — | joined after the root recipe | | `style` | `StyleDesc \| (state) => StyleDesc` | — | merged last | --- # Avatar group Source: https://gpuixui.sudokw.com/components/avatar-group A facepile of overlapping avatars with an overflow chip ## Usage [#usage] ```tsx import { AvatarGroup } from "@gpuixui/components/avatar-group"; ; ``` ## API [#api] | Prop | Type | Default | | ---- | ---- | ------- | | `members` | `ReadonlyArray` (a value that is not a list reads as none; an entry without a text `name` is dropped) | — | | `max` | `number` | everyone | | `size` | `"xs" \| "sm" \| "md" \| "lg"` | `"md"` | | `overlap` | `number` | quarter of the diameter | | `ringColor` | `string` | theme `background` | | `label` | `string` | `${count} avatars` | | `stepMs` | `number` | `40` | | `rise` | `number` | `6` | | `level` | `MotionLevel` | `"normal"` | | `className` | `string` | — | | `style` | `StateStyle` | — | --- # Badge Source: https://gpuixui.sudokw.com/components/badge A short status label on a pill, never a control ## Installation [#installation] ## Usage [#usage] ```tsx import { Badge } from "@gpuixui/components/badge"; Passed ; main; ``` ## API reference [#api-reference] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `variant` | `"neutral" \| "primary" \| "success" \| "warning" \| "danger" \| "info" \| "outline"` | `"neutral"` — a stored name the union does not carry | the fill, ink and edge in **Variants** below | | `size` | `"sm" \| "md"` | `"md"` — a stored name the union does not carry | the fixed type step and the pill's height in **Sizes** below | | `className` | `string` | none | joined last | | `style` | `StyleDesc` | none | merged last | | `children` | `ReactNode` | — | the label | **Variants** | Variant | Fill | Ink | Edge | | ------- | ---- | --- | ---- | | `neutral` | `muted` | `mutedForeground` | none | | `primary` | `primary` | `primaryForeground` | none | | `success` | `successSoft` | `successSoftForeground` | `success` at 30% | | `warning` | `warningSoft` | `warningSoftForeground` | `warning` at 30% | | `danger` | `destructiveSoft` | `destructiveSoftForeground` | `destructive` at 30% | | `info` | `infoSoft` | `infoSoftForeground` | `info` at 30% | | `outline` | none | `mutedForeground` | `borderStrong` | **Sizes** | Size | Type | Pill height | | ---- | ---- | ----------- | | `sm` | 12/16, weight 400 | 20px | | `md` | 14/20, weight 500 | 24px | --- # Breadcrumb Source: https://gpuixui.sudokw.com/components/breadcrumb The trail to the current page, with a collapsing middle ## Installation [#installation] ## Usage [#usage] ```tsx import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@gpuixui/components/breadcrumb"; go("Projects")}>Projects go("GPUIXUI")}>GPUIXUI breadcrumb.tsx ; ``` ## Composition [#composition] ```text Breadcrumb └── BreadcrumbList └── BreadcrumbItem ├── BreadcrumbLink ├── BreadcrumbPage └── BreadcrumbSeparator ``` ## API reference [#api-reference] | Part | Prop | Type | Default | Consequence | | ---- | ---- | ---- | ------- | ----------- | | `Breadcrumb` | `maxItems` | `number` | — | rendered entries to keep; below `3` is clamped to `3`, absent means no collapse | | `Breadcrumb` | `separator` | `ReactNode` | `/` | the glyph between entries; a node, so a chevron works | | `Breadcrumb` | `children` | parts | — | `BreadcrumbList` | | `BreadcrumbList` | `children` | parts | — | the items and any authored separators | | `BreadcrumbItem` | `children` | parts | — | `BreadcrumbLink` or `BreadcrumbPage` | | `BreadcrumbLink` | `onSelect` | `() => void` | — | runs on click, `Enter` or `Space` | | `BreadcrumbLink` | `current` | `boolean` | `false` | renders the current page instead of a link | | `BreadcrumbPage` | `children` | text | — | the current page; not focusable | | `BreadcrumbSeparator` | `children` | text | `/` | the default glyph when none is authored | | all parts | `className` | `string` | — | an extra recipe class, joined last | --- # Bubble Source: https://gpuixui.sudokw.com/components/bubble The message surface, with variants and reactions A bubble is the surface one message wears. It is independent of `Message`: use it for a reply, a quoted message, a suggestion or a notification. `Message` owns the author, avatar, timestamp and row layout; `Bubble` owns the painted box. ## Installation [#installation] ```bash nub add @gpuixui/components ``` ## Usage [#usage] ```tsx import { Bubble, BubbleAction, BubbleActions, BubbleContent, } from "@gpuixui/components"; Roll it out to the whole team. ; ``` ## Composition [#composition] ```text Bubble ├── BubbleContent ├── BubbleActions │ └── BubbleAction └── BubbleReactions ``` `BubbleGroup` stacks consecutive bubbles from one sender: ```text BubbleGroup ├── Bubble group="first" ├── Bubble group="middle" └── Bubble group="last" ``` ## Variants [#variants] | Variant | Description | | ------------- | ---------------------------------------------------------- | | `default` | The primary fill, usually the reader's own message. | | `secondary` | The neutral secondary fill. | | `muted` | A quiet supporting surface. | | `tinted` | A soft primary tint. | | `outline` | A bordered surface on the page fill. | | `ghost` | Unframed prose; it takes the whole row instead of 80%. | | `destructive` | A soft destructive surface for a failed turn or an error. | A bubble is at most 80% of its row (`ghost` is 100%). Pass `maxWidth` to change the cap. ## Grouping [#grouping] `BubbleGroup` tightens the gap to the theme's `md` (8px). Each bubble names its place with `group`: `first` joins below, `middle` joins both sides, `last` joins above and keeps a short tail on the sender's corner, and `single` is the whole radius. The joined corner is the theme's `sm` step and the outer corners keep `xl`, so a run reads as one column without losing its frame. ## Shape [#shape] The surface is shadcn's: `px-3 py-2` (12px by 8px), the `body` type role (14px/20px), and the theme's `xl` corner. A bubble is at most 80% of its row (`ghost` is 100%); pass `maxWidth` to change the cap. ## Actions and reactions [#actions-and-reactions] `BubbleActions` is laid out even while hidden and fades in on pointer-over or when one of its `BubbleAction`s holds focus, so revealing it never moves the bubble. `BubbleReactions` is the pill on the bubble's edge: inset 12px from the sender's side, a full radius, the `muted` fill and a page-fill ring around it so it reads as cut out of the surface. It hangs a quarter of its height over the bubble's bottom edge — `translate-y-3/4`'s crossing, expressed as a negative inset from inside the column — and the rest of the pill's box lays out below the edge, so the next row cannot paint over it. ## API [#api] | Part | Renders | Props | | ---- | ------- | ----- | | `Bubble` | the row: optional avatar, content column, 80% cap | `variant?`, `align?`, `group?`, `maxWidth?`, `avatar?`, `className?`, `children` | | `BubbleContent` | the framed surface | `className?`, `children` | | `BubbleActions` | a named group that reserves its height and fades in | `label?`, `className?`, `children` | | `BubbleAction` | one small ghost action, and the `Button` busy state | `label` (required), `onClick`, `disabled?`, `className?`, `loading?`, `loadingLabel?`, `leading?`, `trailing?`, `reducedMotion?` | | `BubbleReactions` | a named pill that hangs over the surface's edge | `label` (required), `className?`, `children` | | `BubbleGroup` | a tighter column | `gap?`, `className?`, `children` | --- # Button Source: https://gpuixui.sudokw.com/components/button The primary action, with busy and disabled states ## Installation [#installation] ## Usage [#usage] ```tsx import { Button } from "@gpuixui/primitives"; ; ``` ## API reference [#api-reference] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `variant` | `"primary" \| "secondary" \| "outline" \| "ghost" \| "destructive" \| "link"` | `"primary"` | paint from the variant table | | `size` | `"sm" \| "md" \| "lg"` | `"md"` | height and padding from density | | `square` | `boolean` | `false` | icon-only geometry | | `fullWidth` | `boolean` | `false` | `width: 100%` | | `disabled` | `boolean` | `false` | blocks activation, leaves the tab order | | `tabIndex` | `number` | `0`, or `-1` while `disabled` | the action's Tab stop; a caller's value wins both ways | | `loading` | `boolean` | `false` | spinner, `Loading` description, blocks activation | | `loadingLabel` | `string` | the provider's `loading.busy` (`Loading`) | the spinner's accessible name | | `reducedMotion` | `boolean` | the provider's preference (`false` when none) | lands the busy spinner at rest; the prop wins | | `leading` / `trailing` | `ReactNode` | none | icon or element beside the label | | `label` | `string` | none | accessible name; required for an icon-only button | | `onActivate` | `(event: EventPayload) => void` | none | click or activating key, with that event; not called while busy | | `onClick` | `(event) => void` | none | raw click, called before `onActivate` | | `onKeyDown` / `onKeyUp` | `(event: KeyEvent) => void` | none | composed **after** the activation | | `onFocus` / `onBlur` | `(event) => void` | none | composed before the button's own | | `onMouseEnter` / `onMouseLeave` / `aria-description` | the tooltip trigger pair and its words | none | a `Tooltip` forwards them; a hover lands on the element itself | | `ref` | `Ref` | none | the element itself, for a caller that focuses or measures it | | `className` | `string` | none | joined last | | `style` | `StyleDesc` | none | merged last | | `testId` | `string` | none | stable locator | | `children` | `ReactNode` | none | visible label | --- # Calendar Source: https://gpuixui.sudokw.com/components/calendar A month grid for a day, several days, or a range ## Installation [#installation] ## Usage [#usage] ```tsx import { Calendar } from "@gpuixui/components/calendar"; import { useState } from "react"; export const ReviewDate = () => { const [day, setDay] = useState("2026-10-17"); return ; }; ``` ```tsx ``` ## API reference [#api-reference] ### `Calendar` [#calendar] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `mode` | `"single" \| "multiple" \| "range"` | `"single"` | selects the value shape `selected` and `onSelect` speak | | `selected` | `string \| null` · `ReadonlyArray` · `{ from, to } \| null` | none | the controlled selection, typed by `mode` | | `onSelect` | `(day \| days \| range) => void` | none | the next selection; the component never stores it | | `numberOfMonths` | `1 \| 2` | `1` | months painted side by side, sharing one nav pair | | `showOutsideDays` | `boolean` | `true` | paints the neighbouring months' days, which are also selectable | | `captionLayout` | `"label" \| "dropdown"` | `"label"` | a month caption, or month and year jump controls | | `weekStartsOn` | `0 \| 1` | `1` | the first column: Sunday (`0`) or Monday (`1`) | | `fromYear` / `toYear` | `number` | today ± 10 | the years a dropdown or a `Shift` page may reach | | `disabled` | `{ before?, after?, range?, weekdays?, filter? }` | none | refuses days; `before`/`after` also bound paging and the arrows | | `today` | `string` | the runtime clock | the ISO day painted as today; pass it for a deterministic surface | | `defaultMonth` | `string` | the selection or today | the ISO day whose month the grid opens on | | `autoFocus` | `boolean` | `false` | takes focus on mount, for a calendar opened inside a popover | | `label` | `string` | `"Calendar"` | the grid's accessible name, followed by the selection summary | | `onDismiss` | `() => void` | none | called by `Escape`; a popover passes its close | | `className` | `string` | none | joined after the recipe, so it wins conflicts | --- # Card Source: https://gpuixui.sudokw.com/components/card A bounded surface that groups related content ## Installation [#installation] ## Usage [#usage] ```tsx import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@gpuixui/components/card"; Pro plan Everything in Starter, plus seats and invoicing. {rows} {actions} ; ``` ## Composition [#composition] ```text Card ├── CardHeader │ ├── CardTitle │ └── CardDescription ├── CardContent └── CardFooter ``` ## API reference [#api-reference] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `variant` | `"outline" \| "elevated" \| "plain"` | `"outline"` — a stored name the union does not carry | the edge and lift in **Treatments** below | | `className` | `string` | none | a recipe class string joined after the component's own, so it wins a conflict | | `style` | `StyleDesc` | none | merged last, after the compiled recipe | | `children` | `PartChildren` | none | the parts; text and numbers are rejected for every part that hosts parts | **Treatments** | Treatment | Edge | Lift | | --------- | ---- | ---- | | `outline` | 1px `border` | none | | `elevated` | none | `elevation.sm` | | `plain` | none | none | All three paint the `card` fill and a fixed 8px corner; `outline` edges in `border-input`, compiled to `border`. --- # Carousel Source: https://gpuixui.sudokw.com/components/carousel Discrete slide stepping, a dot per slide, arrow keys ## Installation [#installation] ## Usage [#usage] ```tsx import { Carousel, CarouselControl, CarouselIndicators, CarouselSlide, CarouselTrack, } from "@gpuixui/components"; import { useState } from "react"; export const WorkspacePreviews = () => { const [index, setIndex] = useState(0); return ( Overview Activity Members ); }; ``` ## Composition [#composition] ```text Carousel ├── CarouselTrack │ └── CarouselSlide ├── CarouselControl └── CarouselIndicators ``` ## API reference [#api-reference] ### `Carousel` [#carousel] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `label` | `string` | — | the region's accessible name; required | | `value` | `number` | — | controlled slide index | | `defaultValue` | `number` | `0` | the opening slide when uncontrolled | | `onValueChange` | `(value: number) => void` | none | the next index | | `loop` | `boolean` | `false` | wraps past both ends and keeps both controls live | | `className` | `string` | — | joined last | | `children` | `ReactNode` | none | the track and the controls, directly inside | ### `CarouselTrack` [#carouseltrack] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `children` | `ReactNode` | none | the slides, as direct children | | `className` | `string` | — | joined last | ### `CarouselSlide` [#carouselslide] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `children` | `ReactNode` | none | the slide's content | | `style` | `StateStyle` | — | `{ active, index }`, merged last | | `className` | `string` | — | joined last | ### `CarouselControl` [#carouselcontrol] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `direction` | `"previous" \| "next"` | — | which way it steps; required | | `label` | `string` | `"Previous slide"` / `"Next slide"` | the accessible name | | `disabled` | `boolean` | `false` | adds to the edge rule; never removes it | ### `CarouselIndicators` [#carouselindicators] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `label` | `string` | `"Choose a slide"` | the dot group's accessible name | | `style` | `StateStyle` | — | `{ active, count }`, merged last | | `className` | `string` | — | joined last | --- # Chat Source: https://gpuixui.sudokw.com/components/chat A conversation with a following transcript Chat is the whole conversation: a fixed header, the flexible transcript and a fixed composer. The transcript is a `MessageScroller`, each turn is a `Message` around a `Bubble`, and the field is `ChatComposer`. The transport is yours; the status machine is shared so the composer and the header cannot disagree. Every turn wears a bubble — the reader's on the `default` fill and the end edge, the agent's `muted` on the start — and the row supplies the avatar, author and timestamp. Only the row that starts a run paints its avatar and meta line; the rows after it are a `MessageGroup` (6px apart) whose bubbles name `group="first"` and `group="last"` so their inner corners join. ## Installation [#installation] ```bash nub add @gpuixui/components ``` ## Usage [#usage] ```tsx import { Bubble, BubbleContent, ChatComposer, ChatHeader, ChatMessages, ChatRoot, Message, MessageAvatar, MessageContent, MessageHeader, chatMove, } from "@gpuixui/components"; {messages.map((message) => ( {message.body} ))} ; ``` ## Composition [#composition] ```text ChatRoot ├── ChatHeader ├── ChatMessages the MessageScroller │ ├── MessageScrollerViewport │ │ └── MessageScrollerContent │ │ └── MessageScrollerItem │ └── MessageScrollerJumpToLatest └── ChatComposer ``` ## The fixed rows [#the-fixed-rows] `ChatRoot` is a column whose middle grows. `ChatHeader` and `ChatComposer` do not shrink; `ChatMessages` is `flex: 1 1 0` with `min-height: 0`, so a row arriving, a reply streaming or the jump control appearing changes nothing above or below the transcript. Give `ChatRoot` a bounded height — directly, or through a sized flex parent — because the transcript needs one to scroll. The passed-in `ChatMessages` children are rows. Wrap each turn in a `Message` (or a `MessageGroup` for consecutive rows from one sender). A `Bubble` is always a child of `MessageContent`; the message never builds one. ## Status [#status] The four states are `idle`, `submitting`, `streaming` and `error`, and `chatMove` is the only rule set. Send is refused while a reply is open, the refused draft stays in the field, Stop emits the stop intent, and Escape stops a reply. The header announces the state; the failure sentence replaces the status line in place, so a failed reply does not take height from the transcript. ## While a reply is open [#while-a-reply-is-open] - Send is replaced by Stop. - Enter still arrives as the field's image submit; the machine refuses it, so the draft is kept. - A reply's row can grow token by token; the transcript follows it only while the reader is at the end. ## Failed replies [#failed-replies] The failure copy is one line in the header with the full sentence in `aria-description`; Retry is beside it. It used to be a row between the viewport and the composer, which shrank the viewport when it appeared — the layout shift this release removes. ## API [#api] | Part | Renders | Props | | ---- | ------- | ----- | | `ChatRoot` | `role="region"` named by `label` | `status?`, `defaultStatus?`, `onStatusChange?`, `onSubmit` (required), `onStop?`, `onRetry?`, `error?`, `label?`, `className?`, `children` | | `ChatHeader` | a fixed row: title, status/error, actions | `title?`, `description?`, `actions?`, `className?` | | `ChatMessages` | the scroller, the empty panel and the jump rail | `empty?`, `emptyTitle?`, `emptyDescription?`, `className?`, `children` | | `ChatComposer` | a field and its Send/Stop control | `value?`, `defaultValue?`, `onValueChange?`, `label?`, `placeholder?`, `submitLabel?`, `stopLabel?`, `hint?`, `attachments?`, `disabled?`, `className?`, `style?` | `ChatMessages` is also the transcript's public seam: render `MessageScrollerItem` rows inside it (through `Message`) and pass `MessageScrollerItem scrollAnchor` on a turn's first row if you want that turn placed near the top. --- # Checkbox Source: https://gpuixui.sudokw.com/components/checkbox An independent yes/no with an optional mixed state ## Installation [#installation] ## Usage [#usage] ```tsx import { Checkbox } from "@gpuixui/primitives"; import { useState } from "react"; export const Digest = () => { const [email, setEmail] = useState(true); return ( ); }; ``` ## Composition [#composition] ```text Checkbox └── CheckboxIndicator CheckboxGroup └── Checkbox └── CheckboxIndicator ``` ## API reference [#api-reference] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `checked` | `boolean` | — | controlled state; omit to let the box keep its own | | `defaultChecked` | `boolean` | `false` | initial state when uncontrolled | | `onCheckedChange` | `(next: boolean) => void` | none | the next boolean, only when it changed | | `onChange` | `(next: boolean) => void` | none | deprecated alias for `onCheckedChange`; the new name wins when both are passed | | `children` | `ReactNode` | the default glyph | the mark's content | | `indeterminate` | `boolean` | `false` | paints a dash and announces `mixed`; overrides `checked` | | `disabled` | `boolean` | `false` | refuses pointer and keys and leaves the tab order | | `invalid` | `boolean` | `false` | the empty box takes the destructive edge | | `size` | `"sm" \| "md" \| "lg"` | `"md"` | the drawing's side from `iconSize`; the target stays 24px | | `accent` | `"primary" \| "destructive"` | `"primary"` | the fill and mark roles | | `label` | `string` | none | the accessible name; a visible `Label` does not supply it | | `description` | `string` | none | announced help (`aria-description`); wins over a tooltip's text | | `className` | `string` | — | joined after the recipe, so it wins conflicts | | `style` | `StateStyle` | — | resolved against the live state and merged last | | `testId` | `string` | — | stable locator | | `ref` / `aria-id` / `onLayout` | host address | none | the box is the addressed element: focus it, measure it, and name it for automation | | `onMouseEnter` / `onMouseLeave` / `aria-description` | the tooltip trigger pair and its words | none | forwarded to the box, so a checkbox can be a `Tooltip` trigger; the tip's words arrive as `aria-description` | | `onClick` / `onKeyDown` / `onKeyUp` / `onFocus` / `onBlur` | host handlers | none | composed with the box's own; yours runs first | ### `CheckboxIndicator` [#checkboxindicator] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `children` | `ReactNode` | the default check/dash | the glyph | | `className` | `string` | — | compiled through the recipe registry after the part's rules | | `style` | `StyleDesc` | — | merged last | --- # Code block Source: https://gpuixui.sudokw.com/components/code-block Source or output, tokenized by @tanstack/highlight ## Usage [#usage] ```tsx import { CodeBlock } from "@gpuixui/components/code-block"; ; ``` ## API [#api] | Prop | Type | Default | | ---- | ---- | ------- | | `code` | `string` | — | | `title` | `string` | falls back to `path` | | `path` | `string` | — | | `language` | `string` | path's extension | | `lineNumbers` | `boolean` | `false` | | `height` | `number` | — | | `level` | `SurfaceLevel` | `"normal"` | | `emptyLabel` | `string` | `"No code"` | | `copyLabel` | `string` | `"Copy"` | | `copiedLabel` | `string` | `"Copied"` | | `onCopy` | `(code: string) => void` | — | | `className` | `string` | — | | `style` | `StateStyle` | — | --- # Collapsible Source: https://gpuixui.sudokw.com/components/collapsible One section that reveals its body on demand For several sections at once, use [Accordion](/components/accordion); it prerenders its bodies. ## Installation [#installation] ## Usage [#usage] ```tsx import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@gpuixui/components/collapsible"; Advanced options Retry budget: 3 attempts ; ``` ## Composition [#composition] ```text Collapsible ├── CollapsibleTrigger └── CollapsibleContent ``` ## API reference [#api-reference] | Part | Prop | Type | Default | Consequence | | ---- | ---- | ---- | ------- | ----------- | | `Collapsible` | `open` | `boolean` | — | controlled state; omit it to let the root keep its own | | `Collapsible` | `defaultOpen` | `boolean` | `false` | the uncontrolled starting state, open on the first frame | | `Collapsible` | `onOpenChange` | `(open: boolean) => void` | none | the next state the trigger reports | | `Collapsible` | `children` | `CollapsibleTrigger` and `CollapsibleContent` | — | the direct parts; a misplaced family part throws | | `CollapsibleTrigger` | `disabled` | `boolean` | `false` | leaves the tab order, dims, and refuses click, `Enter` and `Space` | | `CollapsibleTrigger` | `className` | `string` | — | joined after the recipe, so it wins conflicts | | `CollapsibleTrigger` | `style` | `StateStyle` | the recipe | resolved against the live state and merged last | | `CollapsibleContent` | `className` | `string` | — | joined after the recipe on the measured box | | `CollapsibleContent` | `children` | `ReactNode` | — | the body; mounted only while open, and during the exit | --- # Color picker Source: https://gpuixui.sudokw.com/components/color-picker A swatch that opens the saturation, hue and alpha rails ## Installation [#installation] ## Usage [#usage] ```tsx import { useState } from "react"; import { ColorPicker } from "@gpuixui/components"; const [accent, setAccent] = useState("#2F6BFF"); ; ``` ## API reference [#api-reference] ### `ColorPicker` [#colorpicker] | Prop | Type | Default | Consequence | | ---- | ---- | ------- | ----------- | | `label` | `string` | — | the trigger and panel's name; a blank value reads as `"Color picker"` so the trigger and the `