Tailwind CSS
helix-ui is not a Tailwind replacement. The two solve different problems, and a lot of teams want both:
- helix-ui ships components, tokens, an a11y baseline, and a theme engine.
- Tailwind ships layout utilities (
flex,grid,gap-4,md:px-6) that are awkward to express any other way.
This page shows how to use them together without fighting either.
Install both
npm install @helix-ui/core @helix-ui/tokens @helix-ui/icons tailwindcss postcss autoprefixernpx tailwindcss init -pMirror helix-ui tokens into Tailwind’s theme
This is the key step, and @helix-ui/tokens does it for you. It ships a
Tailwind preset so you never hand-copy var() mappings. Utilities
like bg-brand-500, p-4, rounded-lg, shadow-md, and text-primary
resolve to the exact same helix-ui token variables the components use —
which means they follow DNA themes and dark mode automatically, because
every value is a live var(--helix-ui-…) reference, not a baked-in hex.
Tailwind v3
Add the preset. It only sets theme.extend (pure data, no plugins), so
it composes cleanly with the rest of your config:
/** @type {import('tailwindcss').Config} */module.exports = { presets: [require('@helix-ui/tokens/tailwind')], content: [ './index.html', './src/**/*.{ts,tsx,js,jsx}', // Match helix-ui's own component source so Tailwind doesn't purge // utilities that exist inside @helix-ui/core's CSS files. './node_modules/@helix-ui/core/dist/**/*.{js,mjs,css}', ], // Tailwind's reset clashes with helix-ui's component CSS — see below. corePlugins: { preflight: false },};Tailwind v4
There’s no JS config in v4 — import the generated @theme stylesheet
instead. It maps helix-ui tokens onto Tailwind’s theme variables:
@import '@helix-ui/tokens/css';@import '@helix-ui/tokens/tailwind.css';@import 'tailwindcss';Order matters: @helix-ui/tokens/css declares the --helix-ui-*
variables that the @theme block references, so it comes first.
What the preset gives you
Both entry points wire up the same helix-ui token families:
| Tailwind theme | helix-ui tokens | Example utilities |
|---|---|---|
colors | brand / neutral / danger / success / warning ramps | bg-brand-500, text-neutral-700, border-danger-500 |
backgroundColor | semantic surfaces + action fills | bg-surface, bg-surface-subtle, bg-brand, hover:bg-brand-hover |
textColor | text roles + on-fill + brand text | text-primary, text-muted, text-on-brand |
borderColor | border roles | border (default), border-focus, border-strong |
spacing | space scale (density-aware) | p-4, gap-6, mt-2 |
borderRadius | radius scale | rounded, rounded-lg, rounded-full |
fontFamily / fontSize | type tokens | font-sans, text-lg |
boxShadow | elevation tokens | shadow, shadow-md |
The semantic names in the v4 @theme block are prefixed for v4’s single
flat color namespace: bg-surface, text-fg-muted, border-border-focus.
The corePlugins.preflight: false is the one non-obvious bit —
Tailwind’s CSS reset (@tailwind base) nukes some defaults helix-ui
relies on. If you’re moving an existing Tailwind project to helix-ui,
this line prevents a wave of subtle visual breakage.
Import both stylesheets
On Tailwind v3, pull in helix-ui’s stylesheets alongside the Tailwind
layers (v4 users already did this above, plus @helix-ui/tokens/tailwind.css):
@import '@helix-ui/tokens/css';@import '@helix-ui/core/styles.css';
@tailwind components;@tailwind utilities;helix-ui’s stylesheet ships first so Tailwind utilities can override.
Use them in tandem
import { Button, Card, Stack } from '@helix-ui/core';
function PaymentRow({ amount, status }: Props) { return ( // helix-ui Card + Stack handle the design system. Tailwind handles // the page layout. <Card className="md:col-span-2"> <Stack direction="row" align="center" justify="between" className="gap-4"> <div className="text-brand">{amount}</div> <Button>Pay</Button> </Stack> </Card> );}Rule of thumb:
| Use helix-ui for | Use Tailwind for |
|---|---|
| Buttons, inputs, cards, dialogs, popovers | Grid spans, responsive breakpoints |
| Color, radius, typography, motion | Margins, padding overrides at breakpoints |
| Anything that should be themable via DNA | Anything that’s “just layout” |
When the cascade fights
If you find yourself writing !bg-brand-500 to override a helix-ui
component’s background, that’s a smell — the right move is to pass
helix-ui’s own variant / tone prop. Tailwind override should be for
layout properties, not visual ones.
// ❌ Fighting the cascade<Button className="!bg-purple-600">Save</Button>
// ✅ Use helix-ui's API<Button tone="brand" variant="solid">Save</Button>
// ✅ Tailwind for layout<Button className="w-full md:w-auto">Save</Button>What you give up
- Two stylesheets to ship. Together they’re ~30 KB gzip. Tradeoff for the breadth.
- Two mental models for color. helix-ui’s tokens are the source of truth; the Tailwind names mirror them. Don’t author colors in two places.
Migrating from Tailwind-only
A typical path:
- Install helix-ui.
- Set
corePlugins.preflight: false. - Replace your hand-rolled
<button className="bg-blue-600 px-4 py-2 ...">s with<Button>. - Keep your layout utilities (
grid,flex,md:*) — they’re still useful. - Move your colors into helix-ui tokens so DNA themes Just Work.
The migration is incremental. You don’t have to do it all at once.