plugins/frontend/skills/radix-ui/SKILL.md
Expert guidance for building with Radix UI -- Primitives (unstyled accessible components) and Themes (pre-styled design system). Covers composition patterns, asChild prop, accessibility, animation, theming, color system, and keyboard navigation. Trigger when working with Radix components, @radix-ui packages, or building accessible UI primitives. Also trigger on mentions of "radix", "radix-ui", "radix primitives", "radix themes", "@radix-ui/react-*", or the unified "radix-ui" package. TRIGGER WHEN: working with Radix components, @radix-ui packages, or building accessible UI primitives DO NOT TRIGGER WHEN: the task is outside the specific scope of this component.
npx skillsauth add acaprino/anvil-toolset radix-uiInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Guidance for building accessible, composable UIs with Radix UI. Covers both Primitives (unstyled, headless) and Themes (pre-styled design system).
Docs: https://www.radix-ui.com
| Product | What it is | Install | Use when |
|---------|-----------|---------|----------|
| Primitives | Unstyled, accessible component primitives | npm i radix-ui | Building custom design systems, need full style control |
| Themes | Pre-styled component library built on Primitives | npm i @radix-ui/themes | Want production-ready styled components out of the box |
shadcn/ui is built on top of Radix Primitives. If working with shadcn, route to the shadcn-ui skill instead.
radix-ui or @radix-ui/react-* packagesasChild prop for composition| Need | Route to | |------|----------| | shadcn/ui components (Radix + Tailwind, pre-composed) | shadcn-ui | | CSS architecture, modern CSS features | frontend-design | | Page layout composition, grid systems | frontend-layout agent | | Animations, micro-interactions, visual polish | frontend-design agent | | Distinctive visual identity | frontend-css skill | | daisyUI components (different library, class-based) | daisyui |
This skill covers architecture, composition patterns, and the most common components. For any specific component's full API -- spawn a quick-searcher agent.
| Resource | URL pattern |
|----------|-------------|
| Primitives component | https://www.radix-ui.com/primitives/docs/components/{name} |
| Primitives guides | https://www.radix-ui.com/primitives/docs/guides/{topic} |
| Themes component | https://www.radix-ui.com/themes/docs/components/{name} |
| Themes overview | https://www.radix-ui.com/themes/docs/overview/{topic} |
| Themes theming | https://www.radix-ui.com/themes/docs/theme/{topic} |
| Colors reference | https://www.radix-ui.com/colors |
Radix Primitives are unstyled, accessible UI components. They handle the hard parts (WAI-ARIA compliance, keyboard navigation, focus management, screen reader support) while you own all styling. Three key principles:
asChild for element delegation, controlled/uncontrolled# Unified package (recommended, tree-shakeable)
npm install radix-ui
# Or individual packages
npm install @radix-ui/react-dialog @radix-ui/react-dropdown-menu
See references/primitives-components.md for detailed sub-component APIs.
| Category | Components | |----------|------------| | Overlay | Dialog, Alert Dialog, Popover, Hover Card, Tooltip | | Menu | Dropdown Menu, Context Menu, Menubar | | Navigation | Navigation Menu, Tabs | | Form | Checkbox, Radio Group, Select, Slider, Switch, Toggle, Toggle Group, Form | | Disclosure | Accordion, Collapsible | | Media | Avatar, Aspect Ratio, Progress | | Layout | Scroll Area, Separator, Toolbar | | Utility | Label, Portal, Slot, Visually Hidden, Direction Provider | | Deprecated | Toast (use Sonner instead) |
Every Radix component is split into named parts that you compose:
import * as Dialog from "radix-ui/components/dialog"
<Dialog.Root>
<Dialog.Trigger>Open</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/50" />
<Dialog.Content className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-white p-6 rounded-lg">
<Dialog.Title>Edit Profile</Dialog.Title>
<Dialog.Description>Make changes to your profile.</Dialog.Description>
{/* form content */}
<Dialog.Close className="absolute right-4 top-4">X</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
The asChild prop delegates rendering to the child element, merging Radix behavior onto your own component:
// Default: Radix renders a <button>
<Dialog.Trigger>Open</Dialog.Trigger>
// asChild: Radix merges onto YOUR element
<Dialog.Trigger asChild>
<Link href="/settings">Open Settings</Link>
</Dialog.Trigger>
Requirements for custom components with asChild:
React.forwardRef (or ref prop in React 19)const MyButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ children, ...props }, ref) => (
<button ref={ref} {...props} className="my-button">
{children}
</button>
)
)
<Dialog.Trigger asChild>
<MyButton>Open</MyButton>
</Dialog.Trigger>
asChild nests to combine behaviors:
<Tooltip.Trigger asChild>
<Dialog.Trigger asChild>
<MyButton>Edit</MyButton>
</Dialog.Trigger>
</Tooltip.Trigger>
All stateful components support both patterns:
// Uncontrolled (default) - component manages its own state
<Accordion.Root type="single" defaultValue="item-1">
// Controlled - you manage state
const [value, setValue] = useState("item-1")
<Accordion.Root type="single" value={value} onValueChange={setValue}>
Radix exposes state via data-* attributes for CSS styling:
| Attribute | Values | Used for |
|-----------|--------|----------|
| [data-state] | "open" / "closed" | Overlays, disclosure |
| [data-state] | "checked" / "unchecked" / "indeterminate" | Checkboxes, switches |
| [data-state] | "active" / "inactive" | Tabs, toggles |
| [data-disabled] | present/absent | Disabled elements |
| [data-orientation] | "vertical" / "horizontal" | Accordion, tabs, separator |
| [data-highlighted] | present/absent | Menu items (keyboard focus) |
| [data-side] | "top" / "right" / "bottom" / "left" | Positioned content |
| [data-align] | "start" / "center" / "end" | Positioned content |
/* Tailwind */
<Accordion.Content className="data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp">
/* Plain CSS */
.AccordionContent[data-state="open"] {
animation: slideDown 300ms ease-out;
}
.AccordionContent[data-state="closed"] {
animation: slideUp 300ms ease-in;
}
See references/patterns.md for detailed animation patterns.
Radix suspends unmounting during CSS animations, enabling exit animations:
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
.DialogOverlay[data-state="open"] { animation: fadeIn 200ms ease-out; }
.DialogOverlay[data-state="closed"] { animation: fadeOut 200ms ease-in; }
Accordion and Collapsible expose size variables for smooth height animation:
.AccordionContent[data-state="open"] {
animation: slideDown 300ms ease-out;
}
@keyframes slideDown {
from { height: 0; }
to { height: var(--radix-accordion-content-height); }
}
Use forceMount to prevent Radix from unmounting content, letting your library control the exit:
<Dialog.Portal forceMount>
<AnimatePresence>
{open && (
<Dialog.Content forceMount asChild>
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>
Content
</motion.div>
</Dialog.Content>
)}
</AnimatePresence>
</Dialog.Portal>
All Radix components implement WAI-ARIA keyboard patterns:
| Component | Keys |
|-----------|------|
| Dialog | Esc close, Tab trap focus |
| Dropdown Menu | Arrow navigate, Enter/Space select, Esc close |
| Accordion | Arrow navigate triggers, Enter/Space toggle, Home/End |
| Tabs | Arrow switch tabs, Tab move to panel |
| Select | Arrow navigate, Enter/Space select |
| Slider | Arrow adjust, Home/End min/max |
| Radio Group | Arrow navigate, Space select |
Radix handles automatically:
role, aria-expanded, aria-controls, aria-labelledby)dir prop / DirectionProviderWhat you must provide:
Title and Description for dialogs/alert dialogs (or use VisuallyHidden)aria-label for icon-only triggersPre-styled component library with built-in theming. Uses Radix Primitives internally but ships with styles, a color system, and layout primitives.
npm install @radix-ui/themes
// app/layout.tsx or main entry
import "@radix-ui/themes/styles.css"
import { Theme } from "@radix-ui/themes"
export default function Layout({ children }) {
return (
<Theme accentColor="indigo" grayColor="slate" radius="medium" scaling="100%">
{children}
</Theme>
)
}
<Theme
accentColor="crimson" // brand color (24 options)
grayColor="sand" // neutral tones (6 options)
radius="large" // border radius (none | small | medium | large | full)
scaling="95%" // global size scale
appearance="dark" // light | dark | inherit
panelBackground="translucent" // solid | translucent
>
import { ThemePanel } from "@radix-ui/themes"
<ThemePanel /> // interactive UI for previewing theme changes
See references/themes-colors.md for the full color reference.
Each color has 12 steps from subtle backgrounds to high-contrast text:
| Steps | Purpose | Example variable |
|-------|---------|-----------------|
| 1-2 | Backgrounds | var(--accent-1), var(--accent-2) |
| 3-5 | Interactive states | var(--accent-3) hover, var(--accent-4) active |
| 6-8 | Borders | var(--accent-6) subtle, var(--accent-8) strong |
| 9-10 | Solid fills | var(--accent-9) primary, var(--accent-10) hover |
| 11-12 | Text | var(--accent-11) low contrast, var(--accent-12) high |
Gray, Gold, Bronze, Brown, Yellow, Amber, Orange, Tomato, Red, Ruby, Crimson, Pink, Plum, Purple, Violet, Iris, Indigo, Blue, Cyan, Teal, Jade, Green, Grass, Lime, Mint, Sky.
Gray, Mauve, Slate, Sage, Olive, Sand. Auto-paired with accent but overridable.
var(--accent-surface) - translucent accent for surface backgroundsvar(--accent-indicator) - for selection indicatorsvar(--accent-track) - for slider/progress tracksvar(--accent-contrast) - guaranteed readable text on accent-9var(--color-background) - page backgroundvar(--color-overlay) - overlay/backdrop<Button color="red">Delete</Button> // overrides theme accent for this button
<Badge color="green" highContrast>Active</Badge>
| Category | Components | |----------|------------| | Layout | Box, Flex, Grid, Section, Container | | Typography | Text, Heading, Code, Quote, Em, Strong, Kbd | | Form | Button, IconButton, TextField, TextArea, Select, Checkbox, Radio Group, Switch, Slider, SegmentedControl | | Overlay | Dialog, Alert Dialog, Popover, Hover Card, Tooltip, Context Menu, Dropdown Menu | | Data Display | Table, Avatar, Badge, Callout, Card, Data List, Inset, Separator, Skeleton | | Feedback | Progress, Spinner | | Navigation | Tabs, Tab Nav, Link |
Most Themes components share:
<Button
size="1" | "2" | "3" | "4" // numeric scale
variant="solid" | "soft" | "outline" | "ghost" | "surface"
color="indigo" // override accent
highContrast={true} // enhanced visibility
radius="full" // override theme radius
loading={true} // loading state
asChild // compose onto child
/>
See references/patterns.md for detailed patterns.
VisuallyHidden if not visible)asChild for routing -- compose Trigger onto <Link> or router componentsforceMount needed)[data-state], [data-highlighted] are stable APIDialog.Overlay, Dialog.Portal exist for a reasonasChilddevelopment
Quality gates for multi-reviewer code review pipelines: adversarial verification panel, completeness critic, reviewer pipeline conventions, and the context sharing pattern for parallel reviewers. TRIGGER WHEN: running /senior-review:team-review quality gates; running /senior-review:code-review Steps 4b/4c (adversarial verification and completeness check); consolidating or deduplicating findings from multiple parallel reviewers. DO NOT TRIGGER WHEN: single-reviewer style review without a consolidation phase, or generic team coordination (the upstream agent-teams skills cover that).
development
Knowledge base for pure-architecture decisions on when to unify duplicated logic into a shared abstraction versus leave it duplicated. Covers the canonical theory (Rule of Three, DRY/WET/AHA, Wrong Abstraction, Locality of Behaviour, Bounded Contexts, Tidy First options framing, CUPID vs SOLID), 12 essential-duplication patterns that justify unification, 12 wrong-abstraction patterns that justify inlining or decomposition, an operational decision frame, and a verified reading list. TRIGGER WHEN: the user is making an architectural decision about whether to centralize, extract, or remove a layer; reviewing an abstraction for premature generality; auditing scattered cross-cutting concerns; spawned by the abstraction-architect agent during /abstraction-architect:audit or as the Abstraction dimension of /senior-review:team-review or /senior-review:code-review; the user asks "should I extract this into a service" / "is this DRY enough" / "is this wrong abstraction". DO NOT TRIGGER WHEN: the task is code formatting and readability cleanup (use clean-code:clean-code), Python-specific refactoring with metrics (use python-development:python-refactor), generic dead-code removal (use senior-review:cleanup-dead-code), security review (use senior-review:security-auditor), or pure pattern-consistency review without an architecture lens (use senior-review:code-auditor).
development
Unified web frontend knowledge base covering CSS architecture, UX psychology, UI components, distinctive aesthetics, and interface design generation. TRIGGER WHEN: working on web styling, design systems, component decisions, responsive strategy, distinctive frontend aesthetics, or exploring multiple interface designs. DO NOT TRIGGER WHEN: the task is purely backend or unrelated to web frontend.
development
Stripe payments knowledge base - API patterns, checkout optimization, subscription lifecycle, pricing strategies, webhook reliability, Firebase integration, cost analysis, and revenue modeling. Loaded by stripe-integrator and revenue-optimizer agents; also consumable directly when the user asks for Stripe-specific patterns without needing an agent. TRIGGER WHEN: working with Stripe API (Payment Intents, Customers, Subscriptions, Checkout Sessions, Connect, webhooks, tax, usage-based billing), pricing strategy, or revenue modeling. DO NOT TRIGGER WHEN: payment work is non-Stripe (PayPal, Square, crypto) or the task is generic e-commerce unrelated to payments.