| name | keystoneui-react |
| description | Manages Keystone UI components and projects — adding, searching, fixing, debugging, styling, and composing UI built on Tailwind CSS v4 + Base UI. Provides project context, component docs, and usage examples. Applies when working with Keystone UI, @keystoneui/react, components.json with @keystoneui/* registries, or any project with @keystoneui/react in its dependencies. Also triggers for "keystoneui add", "find a Keystone UI example", or "switch to Keystone UI". |
| user-invocable | false |
| allowed-tools | Bash(node packages/keystoneui-mcp/dist/index.js *), Bash(node scripts/*.mjs *), Bash(npx shadcn@latest add https://keystoneui.io/r/*) |
| metadata | {"author":"keystoneui","version":"2.1.0"} |
Keystone UI
A production-ready React component library built on Tailwind CSS v4 and Base UI (@base-ui/react), with 50+ accessible components, OKLCH semantic tokens, and dark mode.
Core Principles
- Subpath imports only —
@keystoneui/react/button, never @keystoneui/react. There is no barrel.
- Base UI, not Radix — primitives come from
@base-ui/react. The slot pattern is render, not asChild. → rules/base-vs-radix.md
- Semantic tokens —
bg-primary, text-muted-foreground, never raw colors.
- Compose, don't reinvent — use existing components and their compound parts before writing custom markup.
data-slot is the public API — every part has a stable data-slot for styling targets.
Installation
Two paths reach the same library — pick one. See cli.md for both.
pnpm add @keystoneui/react
npx shadcn@latest add https://keystoneui.io/r/button.json
CSS setup (order matters):
@import "tailwindcss";
@import "@keystoneui/react/base.css";
Then define theme tokens — see customization.md.
Critical Rules
These are always enforced. Each links to a file with code pairs.
- Semantic colors only.
bg-primary, text-muted-foreground — never raw Tailwind colors.
- Built-in variants before custom styles.
<Button variant="outline">, not className="border ...".
className for layout, not styling. Don't override component colors or typography.
- No
space-x-* / space-y-*. Use flex + gap-*.
- Use
size-* when width and height are equal. size-10, not w-10 h-10.
- Two focus patterns — never mix. Outline-based (buttons, checkboxes) or ring-based (inputs, selects).
- No
transition-all. Specify exact properties. (Button is the documented exception.)
- Hover gating.
[&>a]:hover:bg-muted, not [&>a:hover]:bg-muted — the second form bypasses @media (hover: hover).
- No manual
z-index on overlay components. Use the --z-* scale; library components manage stacking.
- Both
disabled: and data-disabled:. Always include cursor-not-allowed and opacity-50.
- Two patterns ship. Lightweight:
Form/Label/Description/ErrorMessage from /form. Rich: Field/FieldLabel/FieldDescription/FieldError from /field. Pick one per form.
<Form> renders a <form> element. Use onSubmit directly.
InputGroup requires InputGroupInput/InputGroupTextarea. Never raw Input inside InputGroup.
- Buttons inside inputs use
InputGroupAddon (and optionally InputGroupButton).
- Option sets (2–7 choices) use
ToggleGroup. Don't loop Button with manual active state.
FieldSet + FieldLegend for grouped checkboxes/radios. FieldLegend accepts variant="label" for inline forms.
- Validation:
aria-invalid on the control + FieldError (or ErrorMessage). Don't use data-invalid on Field — it doesn't style anything in Keystone UI.
- Disabled:
disabled on the control; optionally data-disabled on Field to dim the FieldLabel via the group/field selector.
- Use
render, not asChild. Base UI's slot pattern.
- Items always inside their group.
SelectItem → SelectContent, TabsTrigger → TabsList, DropdownMenuItem → DropdownMenuContent.
Modal, Drawer, AlertDialog need a title. Use className="sr-only" to hide it visually.
- Use full Card composition.
CardHeader/CardTitle/CardDescription/CardContent/CardFooter.
Button has no isLoading prop. Compose with Spinner + disabled.
data-slot is stable. Use it for consumer overrides; don't override slot values when extending.
lucide-react for all icons. No @iconify/react, @remixicon/react (in app code), or other libraries.
- No sizing classes on icons inside components. Components handle icon sizing via SVG boilerplate.
- Pass icons as components, not string keys.
icon={Check}, not icon="check".
- Never import
@radix-ui/*. Use Keystone UI's wrappers, which use @base-ui/react.
render instead of asChild. <ModalTrigger render={<Button />}>Open</ModalTrigger>.
data-open / data-closed / data-checked instead of Radix-style data-state="open".
Key Patterns
import { Modal, ModalTrigger, ModalContent, ModalTitle } from "@keystoneui/react/modal";
<Form onSubmit={handleSubmit}>
<FieldGroup>
<Field>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input id="email" name="email" type="email" />
</Field>
</FieldGroup>
<Button type="submit">Sign in</Button>
</Form>
<Field>
<FieldLabel>Email</FieldLabel>
<Input aria-invalid />
<FieldError>Invalid email.</FieldError>
</Field>
<ModalTrigger render={<Button variant="secondary" />}>Open</ModalTrigger>
<div className="flex flex-col gap-4">...</div>
<Badge variant="secondary">+20.1%</Badge>
<Button disabled={isPending}>
{isPending && <Spinner />}
{isPending ? "Saving..." : "Save"}
</Button>
Component Selection
| Need | Use |
|---|
| Action / button | Button (variants: default, secondary, outline, ghost, destructive, link) |
| Form layout | Form + FieldGroup + Field |
| Text input | Input, Textarea, InputGroup (with addons), InputOTP |
| Choice (one of many) | Select, Combobox (searchable), RadioGroup, NativeSelect |
| Choice (toggle) | ToggleGroup (2–5 options), Switch (boolean), Checkbox |
| Date / time | DateInput, Calendar |
| Overlays | Modal, Drawer, AlertDialog, Popover, Tooltip |
| Menus | DropdownMenu, Command (palette) |
| Navigation | Tabs, Breadcrumb, Pagination, Stepper |
| Data display | Table, Card, DescriptionList, Avatar, Badge, Tag, TagGroup |
| Feedback | Toast, Alert, Progress, CircularProgress, Skeleton, Spinner, Empty |
| Layout | Card, Separator, Resizable, Accordion, Collapsible, AspectRatio, Carousel |
| Bulk-action bar | SelectionBar |
Block Selection
Blocks are full-page or feature-level compositions, not primitives. If the user asks for a complete page or feature, check blocks before composing from primitives — installing a block is faster and yields a more cohesive result.
| User asks for… | Block(s) to consider | Category |
|---|
| Sign-in form / login page | signin-01, signin-02, signin-03, signin-04 | authentication, login |
| Signup / registration page | signup-01, signup-02, signup-03, signup-04, signup-05 | authentication, signup |
| Profile dropdown / user menu | profile-dropdown-01 | navigation |
| Tickets / CRM / data management table | tickets-01 | data |
| Betting panel / wager UI | betting-panel-01, betting-panel-02, betting-panel-03, betting-panel-04 | betting |
Install a block: npx shadcn@latest add https://keystoneui.io/r/<name>.json. Or via the unified CLI: keystoneui blocks to list, keystoneui blocks --category authentication to filter, keystoneui blocks <name> to view source. The --category flag works on list and search too.
Component List
54 components, all importable from @keystoneui/react/{kebab-case-name}:
accordion, alert, alert-dialog, aspect-ratio, avatar, badge, breadcrumb, button, button-group, calendar, card, carousel, checkbox, circular-progress, collapsible, combobox, command, date-input, description-list, drawer, dropdown-menu, empty, field, form, input, input-group, input-otp, item, kbd, label, modal, native-select, pagination, popover, progress, radio-group, resizable, select, selection-bar, separator, skeleton, slider, spinner, stepper, switch, table, tabs, tag, tag-group, textarea, toast, toggle, toggle-group, tooltip.
Workflow
- Discover — use MCP
search_components / list_components, the keystoneui search/keystoneui list CLI verbs, or node scripts/list_components.mjs.
- Find an example — for "X with Y" patterns (e.g., "table with pagination", "card with image"), check
apps/docs/demos/<component>/<variant>.tsx directly. These are real, working compositions authored by the team — examples include apps/docs/demos/table/with-pagination.tsx, apps/docs/demos/card/with-image.tsx. Via MCP, the equivalent is get_examples({ name: "<component>" }) which returns all demos for the component as a bundle.
- Find a block — for full-page or multi-component patterns (e.g., "sign-in page", "tickets table with bulk actions"), check
apps/docs/demos/blocks/<name>.tsx and the docs at apps/docs/content/docs/blocks/<name>.mdx. Existing categories: Sign in (signin-01..04), Signup (signup-01..05), User (profile-dropdown-01), CRM (tickets-01), Betting (betting-panel-01..04). Via MCP, use list_components({ type: "block" }) or search_components({ query: "...", type: "block" }). Always try a block before composing a page from primitives.
- Inspect —
view_component (MCP), node scripts/get_component_docs.mjs <name>, or fetch https://keystoneui.io/llms.mdx/docs/components/<name> directly (the /llms.mdx/... route returns MDX with <ComponentPreview> tags resolved to inline TSX source — single round-trip). Always read the docs before implementing complex components.
- Install —
npx shadcn@latest add <url> (vendored source) or pnpm add @keystoneui/react (npm dependency). See cli.md.
- Theme — define semantic tokens in your CSS. See customization.md.
- Verify — run MCP
audit_checklist after first install to catch missing CSS imports or tokens.
Local Sources of Truth
Before reaching for the docs site, these directories in the repo are authoritative:
apps/docs/demos/<component>/ — per-component example variants (e.g., table/with-pagination.tsx, card/with-image.tsx). Resolved by <ComponentPreview name="<component>-<variant>" /> tags in MDX.
apps/docs/demos/blocks/ — full-page block compositions (signin-01.tsx, tickets-01.tsx, profile-dropdown-01.tsx, …).
apps/docs/content/docs/components/ — per-component MDX docs.
apps/docs/content/docs/blocks/ — per-block MDX docs with install commands and components-used cross-links.
packages/ui/src/ — the component source. Read for prop semantics; consult demos for usage patterns.
For LLM-friendly fetched content, use:
https://keystoneui.io/llms.mdx/docs/components/<name> — per-component MDX with <ComponentPreview> resolved inline as tsx blocks.
https://keystoneui.io/llms.mdx/docs/blocks/<name> — same for blocks.
https://keystoneui.io/llms-components.txt — every component in a single document.
Detailed References
- mcp.md — MCP setup, the 6 tools, and recommended workflow
- cli.md —
npx shadcn@latest add, npm package install, bundled scripts, direct MDX URLs
- customization.md — CSS setup, light/dark tokens, color naming, radius scale, motion/layering, adding new tokens
- rules/styling.md — semantic colors, layout, hover gating, focus, transitions, z-scale
- rules/forms.md —
Form, FieldGroup, Field, InputGroup, ToggleGroup, FieldSet, validation
- rules/composition.md —
render, compound parts, group items, Modal title, Card composition, data-slot
- rules/icons.md — lucide-react, no sizing classes, pass as components
- rules/base-vs-radix.md —
render vs asChild, attribute semantics, animation attributes