From frontend
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 claudepluginhub acaprino/alfio-claude-plugins --plugin frontendThis skill uses the workspace's default tool permissions.
Guidance for building accessible, composable UIs with Radix UI. Covers both Primitives (unstyled, headless) and Themes (pre-styled design system).
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Automates semantic versioning and release workflow for Claude Code plugins: bumps versions in package.json, marketplace.json, plugin.json; verifies builds; creates git tags, GitHub releases, changelogs.
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 | web-designer |
| Page layout composition, grid systems | ui-layout-designer agent |
| Animations, micro-interactions, visual polish | web-designer agent |
| Distinctive visual identity | frontend-design |
| 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 reasonasChild