home / skills / yonatangross / orchestkit / radix-primitives

radix-primitives skill

/plugins/ork/skills/radix-primitives

This skill helps you build accessible, polymorphic UI with Radix Primitives, enabling asChild composition and robust patterns for dialogs, menus, and forms.

npx playbooks add skill yonatangross/orchestkit --skill radix-primitives

Review the files below or copy the command above to add this skill to your agents.

Files (10)
SKILL.md
4.3 KB
---
name: radix-primitives
description: Radix UI unstyled accessible primitives for dialogs, popovers, dropdowns, and more. Use when building custom accessible components, understanding shadcn internals, or needing polymorphic composition.
context: fork
agent: frontend-ui-developer
version: 1.0.0
tags: [radix, ui, primitives, accessibility, dialog, popover, dropdown, aschild, a11y]
user-invocable: false
---

# Radix Primitives

Unstyled, accessible React components for building high-quality design systems.

## Overview

- Building custom styled components with full accessibility
- Understanding how shadcn/ui works under the hood
- Need polymorphic composition without wrapper divs
- Implementing complex UI patterns (modals, menus, tooltips)

## Primitives Catalog

### Overlay Components
| Primitive | Use Case |
|-----------|----------|
| **Dialog** | Modal dialogs, forms, confirmations |
| **AlertDialog** | Destructive action confirmations |
| **Sheet** | Side panels, mobile drawers |

### Popover Components
| Primitive | Use Case |
|-----------|----------|
| **Popover** | Rich content on trigger |
| **Tooltip** | Simple text hints |
| **HoverCard** | Preview cards on hover |
| **ContextMenu** | Right-click menus |

### Menu Components
| Primitive | Use Case |
|-----------|----------|
| **DropdownMenu** | Action menus |
| **Menubar** | Application menubars |
| **NavigationMenu** | Site navigation |

### Form Components
| Primitive | Use Case |
|-----------|----------|
| **Select** | Custom select dropdowns |
| **RadioGroup** | Single selection groups |
| **Checkbox** | Boolean toggles |
| **Switch** | On/off toggles |
| **Slider** | Range selection |

### Disclosure Components
| Primitive | Use Case |
|-----------|----------|
| **Accordion** | Expandable sections |
| **Collapsible** | Single toggle content |
| **Tabs** | Tabbed interfaces |

## Core Pattern: asChild

The `asChild` prop enables polymorphic rendering without wrapper divs:

```tsx
// Without asChild - creates nested elements
<Button>
  <Link href="/about">About</Link>
</Button>

// With asChild - merges into single element
<Button asChild>
  <Link href="/about">About</Link>
</Button>
```

**How it works:**
- Uses Radix's internal `Slot` component
- Merges props from parent to child
- Forwards refs correctly
- Combines event handlers (both fire)
- Merges classNames

## Built-in Accessibility

Every primitive includes:
- **Keyboard navigation**: Arrow keys, Escape, Enter, Tab
- **Focus management**: Trap, return, visible focus rings
- **ARIA attributes**: Roles, states, properties
- **Screen reader**: Proper announcements

## Styling with Data Attributes

Radix exposes state via data attributes:

```css
/* Style based on state */
[data-state="open"] { /* open styles */ }
[data-state="closed"] { /* closed styles */ }
[data-disabled] { /* disabled styles */ }
[data-highlighted] { /* keyboard focus */ }
```

```tsx
// Tailwind arbitrary variants
<Dialog.Content className="data-[state=open]:animate-in data-[state=closed]:animate-out">
```

## Quick Reference

```tsx
import { Dialog, DropdownMenu, Tooltip } from 'radix-ui'

// Basic Dialog
<Dialog.Root>
  <Dialog.Trigger>Open</Dialog.Trigger>
  <Dialog.Portal>
    <Dialog.Overlay />
    <Dialog.Content>
      <Dialog.Title>Title</Dialog.Title>
      <Dialog.Description>Description</Dialog.Description>
      <Dialog.Close>Close</Dialog.Close>
    </Dialog.Content>
  </Dialog.Portal>
</Dialog.Root>
```

## Key Decisions

| Decision | Recommendation |
|----------|----------------|
| Styling approach | Data attributes + Tailwind arbitrary variants |
| Composition | Use `asChild` to avoid wrapper divs |
| Animation | CSS-only with data-state selectors |
| Form components | Combine with react-hook-form |

## Related Skills

- `shadcn-patterns` - Pre-styled Radix components
- `focus-management` - Accessibility patterns
- `design-system-starter` - Design system foundation

## References

- [asChild Composition](references/aschild-composition.md) - Polymorphic rendering
- [Dialog Patterns](references/dialog-modal-patterns.md) - Dialog and AlertDialog
- [Dropdown Patterns](references/dropdown-menu-patterns.md) - Menus and Select
- [Popover Patterns](references/popover-tooltip-patterns.md) - Popover and Tooltip
- [Focus Management](references/focus-management.md) - Keyboard and focus

Overview

This skill provides unstyled, accessible Radix UI primitives for building dialogs, popovers, dropdowns, forms, and other interactive components in TypeScript React apps. It’s ideal when you need low-level building blocks to create custom-styled, accessible components or to understand how higher-level libraries like shadcn/ui compose Radix under the hood. The primitives emphasize polymorphic composition, keyboard behavior, and ARIA-compliant patterns.

How this skill works

The primitives expose small composable components (Dialog, Popover, DropdownMenu, Select, Tabs, etc.) that implement accessibility, focus management, and keyboard navigation out of the box. The asChild pattern lets you render a primitive into an existing element without extra wrappers by merging props, forwarding refs, combining event handlers, and merging class names. State is surfaced via data attributes so styling and animations can be driven with CSS or Tailwind arbitrary variants.

When to use it

  • Building a custom design system or component library with accessible base components
  • Replacing pre-styled components to apply your own visual language (Tailwind, CSS-in-JS, or plain CSS)
  • Creating polymorphic components that must avoid wrapper DOM nodes (asChild)
  • Implementing complex UI patterns: modals, context menus, tooltips, dropdowns, side sheets
  • Integrating with form libraries like react-hook-form while keeping full accessibility

Best practices

  • Prefer data-state and data-attributes for styling and CSS-only animations instead of JS-driven classes
  • Use asChild to avoid redundant wrapper elements and ensure semantic DOM structure
  • Combine Radix primitives with react-hook-form for form controls (Select, Checkbox, Radio) to keep state and validation simple
  • Rely on built-in focus trapping and return behavior for overlays; avoid reimplementing focus logic
  • Keep interactions accessible: ensure triggers are keyboard reachable and provide clear ARIA labels when content is dynamic

Example use cases

  • Modal form with trapped focus and Tailwind-driven open/closed animations using data-[state] selectors
  • Custom dropdown menu rendered as a native button element via asChild for semantic correctness
  • Tooltip and hover-card previews with keyboard accessible focus and proper ARIA descriptions
  • Mobile sheet component for off-canvas navigation with slide animations based on data-state
  • Complex select component integrated with react-hook-form and custom item rendering

FAQ

What does asChild actually do?

asChild lets a Radix primitive render into an existing element instead of creating a new wrapper. It merges props, forwards refs, combines handlers, and preserves semantics without extra DOM nodes.

How do I style open/closed states?

Use the data attributes Radix exposes (e.g., data-state="open") and target them in CSS or Tailwind arbitrary variants to apply animations and visual changes.