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