Radix UI primitive patterns. Use when building accessible, unstyled UI components like dialogs, dropdowns, tooltips, tabs, and selects. Covers Tailwind styling, keyboard navigation, animations, and portal management.
Radix UI primitive patterns. Use when building accessible, unstyled UI components like dialogs, dropdowns, tooltips, tabs, and selects. Covers Tailwind styling, keyboard navigation, animations, and portal management.
Radix UI primitive patterns. Use when building accessible, unstyled UI components like dialogs, dropdowns, tooltips, tabs, and selects. Covers Tailwind styling, keyboard navigation, animations, and portal management.
Radix UI
Platform: Web only. For mobile modals/sheets, see the expo-sdk and react-native-patterns skills.
Use Context7 MCP (resolve-library-id then query-docs) for full API reference, all component props, and additional examples.
Overview
Unstyled, accessible UI primitives for React with built-in keyboard navigation, focus management, and ARIA attributes. Designed to be composed with Tailwind CSS and Framer Motion.
Version: Latest (individual packages) or radix-ui unified package
// Uncontrolled — component manages its own state
<Dialog.Root defaultOpen={false}>
// Controlled — parent manages state (preferred for complex UIs)const [open, setOpen] = useState(false);
<Dialog.Rootopen={open}onOpenChange={setOpen}>
Portal Usage
Always portal overlays and dropdowns to avoid z-index conflicts, overflow: hidden clipping, and CSS transform issues:
❌ Using controlled without onOpenChange/onValueChange
❌ Mixing controlled and uncontrolled patterns
❌ Forgetting focus:ring styles (poor keyboard UX)
❌ Not testing with keyboard navigation
Feedback Loops
Accessibility testing:
# Test with keyboard only (no mouse)# Tab through all interactive elements# Esc should close Dialogs, Dropdowns, Selects# Arrow keys should navigate menus and selects
Screen reader testing:
# macOS: VoiceOver (Cmd+F5)# Verify Dialog.Title and Dialog.Description are announced# Check for proper role attributes
Visual regression: Test all states: Closed vs Open, Hover vs Focus, Selected vs Unselected, Disabled, different viewport sizes.
Framer Motion: Use forceMount + AnimatePresence. Test that focus management still works with animations.