Skill

Build Accessible Design Systems with Radix UI

Guide to building accessible, unstyled design systems with Radix UI primitives - theming, composition, and troubleshooting.

Works with githubreact

79
Spark score
out of 100
Updated 3 days ago
Version 15.5.1

Add to Favorites

Why it matters

Leverage Radix UI primitives to construct production-ready, accessible design systems. This asset guides you in building scalable component libraries with a focus on accessibility, theming, and composable patterns.

Outcomes

What it gets done

01

Implement accessible UI components using Radix primitives.

02

Set up theming strategies with CSS variables or Tailwind.

03

Build complex interactive components like Dialogs and Tabs.

04

Ensure WCAG 2.1 AA/AAA compliance in your design system.

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/ag-radix-ui-design-system | bash

Overview

Radix UI Design System

Guides building accessible design systems on Radix UI's unstyled primitives, covering theming strategies, compound component patterns, an accessibility checklist, and common troubleshooting. Use when building a custom, accessible React component library or design system, not for pre-styled component needs or non-React projects.

What it does

A guide to building production-ready, accessible design systems on Radix UI's unstyled primitives - headless components that provide behavior (keyboard navigation, focus management, ARIA attributes) while leaving all styling decisions to the consumer. Three core principles anchor it: accessibility first (never override Radix's built-in keyboard nav, screen-reader support, or focus trapping - enhance, don't replace), headless architecture (Radix supplies behavior, you supply appearance via className on Trigger and Content), and composition over configuration (complex components like Tabs are built by composing Root, List, Trigger, and Content primitives rather than configuring one monolithic component). It covers installation per primitive and the universal Root, Trigger, Portal, Content shape every primitive follows. Three theming strategies are compared: CSS variables (framework-agnostic, SSR-friendly, using data-theme overrides), Tailwind plus class-variance-authority for variant-heavy components with a typed VariantProps interface, and Stitches CSS-in-JS for runtime theming with scoped styles. Four component patterns are covered in depth: compound components sharing state via context, such as a Select built from Root, Trigger, Value, Portal, Content, Viewport, and Item; the asChild prop for rendering a primitive as a different element, such as a Next.js Link, without losing Radix's behavior or breaking the accessibility tree with nested interactive elements; controlled versus uncontrolled state, using defaultValue for Radix-managed state versus value and onValueChange for syncing with external state like a URL or Redux; and animating with Framer Motion using forceMount plus AnimatePresence so exit animations can run before the portal unmounts. A reference section gives the exact primitive shape for Dialog, Dropdown Menu including nested Sub menus, Tabs, Tooltip, and Popover. An accessibility checklist requires visible focus indicators, full keyboard support, meaningful ARIA labels, WCAG AA color contrast, and proper aria-invalid and aria-busy states, plus Dialog-specific requirements such as a Dialog.Title for screen readers and focus returned to the trigger, and Dropdown-specific requirements like arrow-key navigation and type-ahead search. Best practices contrast four do's - always use asChild to avoid wrapper divs, provide semantic HTML, use CSS variables for theming, compose primitives for complex components like a command palette - against four don'ts, including skipping Dialog.Title or Description, fighting internal behavior by stopping propagation, mixing controlled and uncontrolled props on the same component, and disabling keyboard handling. Three worked examples show a Cmd+K command palette built on Dialog plus cmdk, an icon-triggered dropdown actions menu, and a Radix Select wired into React Hook Form via Controller. Troubleshooting covers four named problems with causes and fixes: Escape not closing a dialog, dropdown positioning breaking under an overflow-hidden ancestor (fixed by rendering through Portal), animations not running (needs forceMount plus AnimatePresence), and asChild TypeScript inference issues. Performance guidance covers lazy-loading heavy primitives, reusing a single Tooltip.Provider portal container across multiple tooltips, and memoizing expensive list-rendering functions. It also positions Radix relative to shadcn/ui, a copy-paste component collection built on Radix plus Tailwind for quick, standard-design prototyping, and Radix Themes, the official pre-styled system, versus raw Radix primitives for full customization of a unique design.

When to use - and when NOT to

Use when creating a custom design system from scratch, building an accessible interactive component library, implementing complex components like Dialog, Dropdown, or Tabs, migrating from a pre-styled library to unstyled primitives, setting up CSS-variable or Tailwind theming, or building an application that needs WCAG 2.1 AA/AAA compliance. Not for projects that want pre-styled components out of the box, where shadcn/ui or Mantine fit better, simple static pages with no interactivity, projects on React below 16.8 since Radix requires hooks, or non-React frameworks.

Inputs and outputs

Input is the target UI component (Dialog, Dropdown, Tabs, Tooltip, Popover, Select, and so on) and the chosen theming strategy. Output is a composed, accessible component built from Radix primitives with custom styling, following the Root, Trigger, Portal, Content shape common to every primitive.

<Primitive.Root>
  <Primitive.Trigger />
  <Primitive.Portal>
    <Primitive.Content />
  </Primitive.Portal>
</Primitive.Root>

Integrations

Built on individual @radix-ui/react-* packages, commonly paired with clsx, tailwind-merge, and class-variance-authority for styling, Framer Motion for animation, cmdk for command palettes, React Hook Form for form integration, and positioned relative to shadcn/ui and the official Radix Themes package. Related sibling skills include tailwind-design-system, react-patterns, frontend-design, and accessibility-compliance.

Who it's for

Frontend engineers and design-system teams building a fully custom, accessible component library in React who need Radix's specific patterns - compound components, asChild, controlled and uncontrolled state, theming strategies - rather than adopting a pre-styled library.

Source README

Radix UI provides unstyled, accessible components (primitives) that you can customize to match any design system. This skill guides you through building scalable component libraries with Radix UI, focusing on accessibility-first design, theming architecture, and composable patterns.

Key Strengths:

  • Headless by design: Full styling control without fighting defaults
  • Accessibility built-in: WAI-ARIA compliant, keyboard navigation, screen reader support
  • Composable primitives: Build complex components from simple building blocks
  • Framework agnostic: Works with React, but styles work anywhere

FAQ

Common questions

Discussion

Questions & comments ยท 0

Sign In Sign in to leave a comment.