Radix UI
02 / 02

Radix UI: Styling, Positioning & the shadcn/ui Pattern

Radix UI: Styling, Positioning & the shadcn/ui Pattern

Styling via Data Attributes

/* Radix has no default visual CSS -- but exposes internal state
   via data attributes any styling approach can target */
.dialog-content {
  background: white;
  border-radius: 8px;
  padding: 24px;
}

/* CSS reacts directly to Radix's open/closed state -- no need for
   JavaScript to conditionally toggle a class name */
.dialog-content[data-state="open"] {
  animation: fadeIn 150ms ease-out;
}
.dialog-content[data-state="closed"] {
  animation: fadeOut 100ms ease-in;
}

Controlled vs. Uncontrolled Usage

// Uncontrolled -- Radix manages open/closed state internally,
// driven purely by Trigger/Close interactions
<Dialog.Root>...</Dialog.Root>

// Controlled -- application code owns the state, useful when opening
// needs to be triggered by logic other than clicking the Trigger
// (e.g. after a successful form submission elsewhere in the app)
const [isOpen, setIsOpen] = useState(false)

<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
  ...
</Dialog.Root>

Automatic Collision-Aware Positioning

import * as Popover from '@radix-ui/react-popover'

// Content automatically flips/shifts to stay within the viewport --
// a Popover near the bottom edge opens upward instead of being cut off
<Popover.Root>
  <Popover.Trigger>Info</Popover.Trigger>
  <Popover.Portal>
    <Popover.Content side="bottom" collisionPadding={8}>
      Helpful details here.
    </Popover.Content>
  </Popover.Portal>
</Popover.Root>

shadcn/ui: Radix + Tailwind, Pre-Styled

shadcn/ui is a popular collection of components built on top of Radix Primitives, styled with Tailwind CSS, and distributed as copy-pasteable source (not an opaque npm dependency) -- combining Radix's accessible behavior with ready-made visual design that's still fully editable in your own codebase. A good starting point for teams that want Radix's correctness without styling every primitive from scratch.

When to Reach for Radix

  • A project with its own distinct design system, where a pre-styled library like Material UI would mean fighting its default styles.

  • Any interactive pattern with real accessibility complexity -- dialogs, dropdown menus, tooltips, select inputs -- rather than reimplementing focus/keyboard/ARIA logic from scratch.

  • Each primitive (@radix-ui/react-dialog, @radix-ui/react-tooltip, etc.) is installed as its own package, so a project pulls in only what it needs.

  • Radix UI Primitives targets React specifically; a separate, independently maintained project exists for Vue.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free