MCPcopy Index your code
hub / github.com/ecklf/tailwindcss-radix

github.com/ecklf/tailwindcss-radix @v4.0.2

Chat with this repo
repository ↗ · DeepWiki ↗ · release v4.0.2 ↗ · + Follow
65 symbols 170 edges 43 files 0 documented · 0% 13 cross-repo links updated 15mo agov4.0.2 · 2025-02-19★ 2,169
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

  <img width="967" height="auto" src="https://raw.githubusercontent.com/ecklf/tailwindcss-radix/main/demo/public/static/og.png" />

Utilities and variants for styling Radix state

tailwindcss v4 ready npm version npm downloads

What is this?

The main purpose of this library is adding classnames for accessing Radix data attributes, which gains you the benefit of auto-completion compared to using data-* variants.

TL;DR It's @headlessui-tailwindcss for Radix.

Installation

# For v3 compatibility
pnpm add tailwindcss-radix@3
# For v4 compatibility
pnpm add tailwindcss-radix

Demo

Click on the banner to check out the demo components. You can find the code inside the demo folder.

Usage

With @plugin directive (recommended)

Default prefix

/* Generates `radix-[state/side/orientation]-*` utilities for `data-[state/side/orientation]="*"` */
@plugin "tailwindcss-radix";

Custom prefix

/* Generates `rdx-[state/side/orientation]-*` utilities for `data-[state/side/orientation]="*"` */
@plugin "tailwindcss-radix" {
  variantPrefix: rdx;
}

With @config directive

Default prefix

module.exports = {
  // --snip --
  plugins: [
    // Generates `radix-[state/side/orientation]-*` utilities for `data-[state/side/orientation]="*"`
    require("tailwindcss-radix")(),
  ],
};

Custom prefix

module.exports = {
  // --snip --
  plugins: [
    // Generates `rdx-[state/side/orientation]-*` utilities for `data-[state/side/orientation]="*"`
    require("tailwindcss-radix")({
      variantPrefix: "rdx",
    }),
  ],
};

Load configuration

@config "../../tailwind.config.js";

Styling state

Basic usage

This plugin works with CSS attribute selectors. Use the variants based on the data-* attribute added by Radix.

import React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";

const App = () => {
  return (
    <DropdownMenuPrimitive.Root>
      <DropdownMenuPrimitive.Trigger className="border-black radix-state-open:border-2">
        Trigger
      </DropdownMenuPrimitive.Trigger>
      <DropdownMenuPrimitive.Content>
        <DropdownMenuPrimitive.Item>Item</DropdownMenuPrimitive.Item>
      </DropdownMenuPrimitive.Content>
    </DropdownMenuPrimitive.Root>
  );
};

export default App;

Accessing parent state

When you need to style an element based on the state of a parent element, mark the parent with the group class and style the target with group-radix-* modifiers.

Example usage of a conditional transform for a Radix Accordion:

import React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDownIcon } from "@radix-ui/react-icons";

const Accordion = () => {
  return (
    <AccordionPrimitive.Root type="multiple">
      <AccordionPrimitive.Item value="item-1">
        <AccordionPrimitive.Header>
          <AccordionPrimitive.Trigger className="group">



              Item 1
              <ChevronDownIcon className="w-5 h-5 ml-2 transform group-radix-state-open:rotate-180" />



          </AccordionPrimitive.Trigger>
        </AccordionPrimitive.Header>
        <AccordionPrimitive.Content>Content 1</AccordionPrimitive.Content>
      </AccordionPrimitive.Item>
      <AccordionPrimitive.Item value="item-2">
        <AccordionPrimitive.Header>
          <AccordionPrimitive.Trigger className="group">



              Item 2
              <ChevronDownIcon className="w-5 h-5 ml-2 transform group-radix-state-open:rotate-180" />



          </AccordionPrimitive.Trigger>
        </AccordionPrimitive.Header>
        <AccordionPrimitive.Content>Content 2</AccordionPrimitive.Content>
      </AccordionPrimitive.Item>
    </AccordionPrimitive.Root>
  );
};

export default App;

Accessing sibling state

When you need to style an element based on the state of a sibling element, mark the sibling with the peer class and style the target with peer-radix-* modifiers.

Example usage of a conditional icon color for a sibling of a Radix Checkbox:

import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { CheckIcon, TargetIcon } from "@radix-ui/react-icons";
import React from "react";

interface Props {}

const App = (props: Props) => {
  return (
    <>
      <CheckboxPrimitive.Root id="c1" defaultChecked className="peer h-5 w-5">
        <CheckboxPrimitive.Indicator>
          <CheckIcon />
        </CheckboxPrimitive.Indicator>
      </CheckboxPrimitive.Root>

      <TargetIcon className="text-red-500 peer-radix-state-checked:text-green-500" />
    </>
  );
};

export default App;

Disabled state

Use the generated disabled variant.

import React from "react";
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";

const ContextMenu = () => {
  return (
    // --snip--
    <ContextMenuPrimitive.Item
      disabled
      className="radix-disabled:opacity-50 radix-disabled:cursor-not-allowed"
    >
      Item
    </ContextMenuPrimitive.Item>
    // --snip--
  );
};

CSS Variable Utilities

Origin position

.origin-radix-context-menu {
  transform-origin: var(--radix-context-menu-content-transform-origin);
}
.origin-radix-dropdown-menu {
  transform-origin: var(--radix-dropdown-menu-content-transform-origin);
}
.origin-radix-hover-card {
  transform-origin: var(--radix-hover-card-content-transform-origin);
}
.origin-radix-menubar {
  transform-origin: var(--radix-menubar-content-transform-origin);
}
.origin-radix-popover {
  transform-origin: var(--radix-popover-content-transform-origin);
}
.origin-radix-select {
  transform-origin: var(--radix-select-content-transform-origin);
}
.origin-radix-tooltip {
  transform-origin: var(--radix-tooltip-content-transform-origin);
}

Content / Viewport Width / Height

.w-radix-accordion-content {
  width: var(--radix-accordion-content-width);
}
.h-radix-accordion-content {
  height: var(--radix-accordion-content-height);
}
.w-radix-collapsible-content {
  width: var(--radix-collapsible-content-width);
}
.h-radix-collapsible-content {
  height: var(--radix-collapsible-content-height);
}
.w-radix-navigation-menu-viewport {
  width: var(--radix-navigation-menu-viewport-width);
}
.h-radix-navigation-menu-viewport {
  height: var(--radix-navigation-menu-viewport-height);
}

Content Available Width / Height

.w-radix-context-menu-content-available {
  width: var(--radix-context-menu-content-available-width);
}
.max-w-radix-context-menu-content-available {
  max-width: var(--radix-context-menu-content-available-width);
}
.h-radix-context-menu-content-available {
  height: var(--radix-context-menu-content-available-height);
}
.max-h-radix-context-menu-content-available {
  max-height: var(--radix-context-menu-content-available-height);
}
.w-radix-dropdown-menu-content-available {
  width: var(--radix-dropdown-menu-content-available-width);
}
.max-w-radix-dropdown-menu-content-available {
  max-width: var(--radix-dropdown-menu-content-available-width);
}
.h-radix-dropdown-menu-content-available {
  height: var(--radix-dropdown-menu-content-available-height);
}
.max-h-radix-dropdown-menu-content-available {
  max-height: var(--radix-dropdown-menu-content-available-height);
}
.w-radix-hover-card-content-available {
  width: var(--radix-hover-card-content-available-width);
}
.max-w-radix-hover-card-content-available {
  max-width: var(--radix-hover-card-content-available-width);
}
.h-radix-hover-card-content-available {
  height: var(--radix-hover-card-content-available-height);
}
.max-h-radix-hover-card-content-available {
  max-height: var(--radix-hover-card-content-available-height);
}
.w-radix-menubar-content-available {
  width: var(--radix-menubar-content-available-width);
}
.max-w-radix-menubar-content-available {
  max-width: var(--radix-menubar-content-available-width);
}
.h-radix-menubar-content-available {
  height: var(--radix-menubar-content-available-height);
}
.max-h-radix-menubar-content-available {
  max-height: var(--radix-menubar-content-available-height);
}
.w-radix-popover-content-available {
  width: var(--radix-popover-content-available-width);
}
.max-w-radix-popover-content-available {
  max-width: var(--radix-popover-content-available-width);
}
.h-radix-popover-content-available {
  height: var(--radix-popover-content-available-height);
}
.max-h-radix-popover-content-available {
  max-height: var(--radix-popover-content-available-height);
}
.w-radix-select-content-available {
  width: var(--radix-select-content-available-width);
}
.max-w-radix-select-content-available {
  max-width: var(--radix-select-content-available-width);
}
.h-radix-select-content-available {
  height: var(--radix-select-content-available-height);
}
.max-h-radix-select-content-available {
  max-height: var(--radix-select-content-available-height);
}
.w-radix-tooltip-content-available {
  width: var(--radix-tooltip-content-available-width);
}
.max-w-radix-tooltip-content-available {
  max-width: var(--radix-tooltip-content-available-width);
}
.h-radix-tooltip-content-available {
  height: var(--radix-tooltip-content-available-height);
}
.max-h-radix-tooltip-content-available {
  max-height: var(--radix-tooltip-content-available-height);
}

Trigger Available Width / Height

.w-radix-context-menu-trigger {
  width: var(--radix-context-menu-trigger-width);
}
.h-radix-context-menu-trigger {
  height: var(--radix-context-menu-trigger-height);
}
.w-radix-dropdown-menu-trigger {
  width: var(--radix-dropdown-menu-trigger-width);
}
.h-radix-dropdown-menu-trigger {
  height: var(--radix-dropdown-menu-trigger-height);
}
.w-radix-hover-card-trigger {
  width: var(--radix-hover-card-trigger-width);
}
.h-radix-hover-card-trigger {
  height: var(--radix-hover-card-trigger-height);
}
.w-radix-menubar-trigger {
  width: var(--radix-menubar-trigger-width);
}
.h-radix-menubar-trigger {
  height: var(--radix-menubar-trigger-height);
}
.w-radix-popover-trigger {
  width: var(--radix-popover-trigger-width);
}
.h-radix-popover-trigger {
  height: var(--radix-popover-trigger-height);
}
.w-radix-select-trigger {
  width: var(--radix-select-trigger-width);
}
.h-radix-select-trigger {
  height: var(--radix-select-trigger-height);
}
.w-radix-tooltip-trigger {
  width: var(--radix-tooltip-trigger-width);
}
.h-radix-tooltip-trigger {
  height: var(--radix-tooltip-trigger-height);
}

Toast swiping

.translate-x-radix-toast-swipe-end-x {
  transform: translateX(var(--radix-toast-swipe-end-x));
}
.translate-y-radix-toast-swipe-end-y {
  transform: translateY(var(--radix-toast-swipe-end-y));
}
.translate-x-radix-toast-swipe-move-x {
  transform: translateX(var(--radix-toast-swipe-move-x));
}
.translate-y-radix-toast-swipe-move-y {
  transform: translateY(var(--radix-toast-swipe-move-y));
}

Migrate from v1

To prevent a possible future name clashing the skipAttributeNames option has been removed. In case you used this option, please update the class names accordingly.

Migrate from v2

In case you use content-available utilities:

  • Add -content-available to the width-based classnames
  • Remove width and height from content-available-[width|height]

View diff

```diff -w-radix-context-menu +w-radix-context-menu-content-available

-h-radix-context-menu-content-available-height +h-radix-context-menu-content-available

-max-w-radix-context-menu-content-available-width +max-w-radix-context-menu-content-available

-max-h-radix-context-menu-content-available-height +max-h-radix-context-menu-content-available

-w-radix-dropdown-menu +w-radix-dropdown-menu-content-available

-h-radix-dropdown-menu-content-available-height +h-radix-dropdown-menu-content-available

-max-w-radix-dropdown-menu-content-available-width +max-w-radix-dropdown-menu-content-available

-max-h-radix-dropdown-menu-content-available-height +max-h-radix-dropdown-menu-content-available

-w-radix-hover-card +w-radix-hover-card-content-available

-h-radix-hover-card-content-available-height +h-radix-hover-card-content-available

-max-w-radix-hover-card-content-available-width +max-w-radix-hover-card-content-available

-max-h-radix-hover-card-content-available-height +max-h-radix-hover-card-content-available

-w-radix-menubar +w-radix-menubar-content-available

-h-radix-menubar-content-available-height +h-radix-menubar-content-available

-max-w-radix-menubar-content-available-width +max-w-radix-menubar-content-available

-max-h-radix-menubar-content-available-height +max-h-radix-menubar-content-available

-w-radix-popover +w-radix-popover-content-available

-h-radix-popover-content-available-height +h-radix-popover-content-available

-max-w-radix-popover-content-available-width +max

Extension points exported contracts — how you extend this code

Core symbols most depended-on inside this repo

Shape

Function 48
Interface 11
Class 2
Enum 2
Method 2

Languages

TypeScript100%

Modules by API surface

demo/components/menubar.tsx7 symbols
demo/components/shared/command-menu.tsx5 symbols
demo/pages/_document.tsx4 symbols
demo/pages/index.tsx3 symbols
demo/components/dropdown-menu.tsx3 symbols
demo/components/context-menu.tsx3 symbols
demo/hooks/use-media-query.ts2 symbols
demo/components/toolbar.tsx2 symbols
demo/components/toggle-group.tsx2 symbols
demo/components/tabs.tsx2 symbols
demo/components/shared/theme-switcher.tsx2 symbols
demo/components/shared/demo-card.tsx2 symbols

For agents

$ claude mcp add tailwindcss-radix \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page