Skip to content

Lesson 3 of 6

Styling

Style every part with Tailwind utility classes and let state attributes drive what changes.

Style the element that exists

Headless means comp0 does not choose your colors, spacing, or borders. Pass className with Tailwind utilities to the part that actually renders an element, exactly as you would style native HTML. Plain CSS works too, but every example in these docs uses Tailwind so you can copy classes straight into your app.

Add thistsx
<SelectTrigger className="w-full rounded-lg border border-zinc-950/10 bg-white px-3 py-2 text-sm">
  <SelectValue />
</SelectTrigger>

Style state with data variants

Components add attributes such as data-open only while that state is true. When a popover closes, data-open is removed instead of becoming data-open="false". Tailwind's data variants match exactly that presence, so data-open: and data-selected: utilities switch on and off with the state.

Add thistsx
<MenuTrigger className="rounded-lg px-3 py-2 text-sm data-open:bg-zinc-100">
  Actions
</MenuTrigger>

<SelectOption className="px-3 py-2 text-sm data-selected:bg-teal-100 data-selected:font-medium" value="small">
  Small
</SelectOption>

Show focus clearly

Keyboard users need to see where they are before they press a key. Keep a strong focus-visible: outline or replace it with an equally obvious style. Also style disabled and invalid states, with data-disabled: and data-invalid:, so a control does not silently change meaning.

Add thistsx
<Button className="rounded-lg bg-teal-700 px-3 py-2 text-sm text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600 data-disabled:opacity-50">
  Save
</Button>