Skip to content

ecklf/tailwindcss-base-ui

Repository files navigation

Utilities and variants for styling Base UI state

tailwindcss v4 ready npm version npm downloads

Important

This is still a WIP, most of the code was ported with Cursor from tailwindcss-radix

What is this?

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

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

Installation

pnpm add tailwindcss-base-ui

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 `bui-*` utilities for data attributes */
@plugin "tailwindcss-base-ui";

Custom prefix

/* Generates `ui-*` utilities for data attributes */
@plugin "tailwindcss-base-ui" {
  variantPrefix: ui;
}

Default prefix

module.exports = {
  // --snip --
  plugins: [
    // Generates `bui-*` utilities for data attributes
    require("tailwindcss-base-ui")(),
  ],
};

Custom prefix

module.exports = {
  // --snip --
  plugins: [
    // Generates `ui-*` utilities for data attributes
    require("tailwindcss-base-ui")({
      variantPrefix: "ui",
    }),
  ],
};

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 Base UI.

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

const App = () => {
  return (
    <DropdownMenuPrimitive.Root>
      <DropdownMenuPrimitive.Trigger className="border-black bui-ui-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-bui-* modifiers.

Example usage of a conditional transform for a Base UI Accordion:

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

const Accordion = () => {
  return (
    <AccordionPrimitive.Root type="multiple">
      <AccordionPrimitive.Item value="item-1">
        <AccordionPrimitive.Header>
          <AccordionPrimitive.Trigger className="group">
            <div className="flex items-center">
              Item 1
              <ChevronDownIcon className="w-5 h-5 ml-2 transform group-bui-open:rotate-180" />
            </div>
          </AccordionPrimitive.Trigger>
        </AccordionPrimitive.Header>
        <AccordionPrimitive.Content>Content 1</AccordionPrimitive.Content>
      </AccordionPrimitive.Item>
      <AccordionPrimitive.Item value="item-2">
        <AccordionPrimitive.Header>
          <AccordionPrimitive.Trigger className="group">
            <div className="flex items-center">
              Item 2
              <ChevronDownIcon className="w-5 h-5 ml-2 transform group-bui-state-open:rotate-180" />
            </div>
          </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-bui-* modifiers.

Example usage of a conditional icon color for a sibling of a Base UI Checkbox:

import * as CheckboxPrimitive from "@base-ui-ui/react-checkbox";
import { CheckIcon, TargetIcon } from "@base-ui-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-bui-checked:text-green-500" />
    </>
  );
};

export default App;

Disabled state

Use the generated disabled variant.

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

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

MIT

About

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

 

Packages

No packages published