Example Next.js app using Astryx pre-built dist CSS alongside Tailwind CSS v4. No StyleX build plugin needed: Astryx components are consumed as a regular npm package with a CSS import.
Astryx ships pre-compiled CSS (astryx.css) with all component styles as atomic classes in CSS cascade layers. Tailwind handles layout, spacing, and custom styling via utility classes. Both systems coexist through explicit layer ordering.
The key to coexistence is declaring all layers upfront in globals.css:
@layer reset, theme, base, astryx-base, astryx-theme, components, utilities;This gives the correct priority (lowest → highest):
| Layer | Source | What it does |
|---|---|---|
reset |
Astryx | CSS reset (:where() selectors, zero specificity) |
theme |
Tailwind | Theme variables (colors, fonts, spacing) |
base |
Tailwind | Preflight reset (element-level normalization) |
astryx-base |
Astryx | Component styles (buttons, cards, inputs, etc.) |
astryx-theme |
Astryx | Theme overrides (typography, color mappings) |
components |
Tailwind | Component classes (if any) |
utilities |
Tailwind | Utility classes: wins over all layers |
| (unlayered) | Consumer | Your custom CSS: highest priority |
Without this declaration, Astryx layers are created after Tailwind's declared layers, making Astryx component styles outrank Tailwind utilities. That means className="bg-red-500" on an Astryx component wouldn't work.
<main className="flex min-h-screen items-center p-8">
<Card className="max-w-md">
<VStack gap={4}>
<Heading level={2}>Dashboard</Heading>
<Button label="Save" variant="primary" />
</VStack>
</Card>
</main>Import @astryxdesign/core/tailwind-theme.css to register Astryx tokens as Tailwind theme variables. This maps all Astryx design tokens to native Tailwind utilities, with no var() needed:
// Before (verbose arbitrary values):
<div className="rounded-[var(--radius-container)] bg-[var(--color-background-surface)] p-[var(--spacing-4)]">
<p className="text-[var(--color-text-primary)]">Styled with Astryx tokens</p>
</div>
// After (with tailwind-theme.css):
<div className="rounded-lg bg-surface p-4">
<p className="text-primary">Styled with Astryx tokens</p>
</div>The bridge uses Tailwind v4's @theme inline: it tells Tailwind to generate utilities from Astryx's existing CSS custom properties without emitting duplicate declarations. Theme switching just works.
Available utilities include text-primary, text-secondary, bg-surface, bg-card, border-strong, text-error, bg-success, bg-blue-subtle, text-blue-vivid, border-blue-ring, and 80+ more.
If you need a token the bridge doesn't cover, you can still use Tailwind's bracket syntax:
<div className="bg-[var(--color-background-surface)]">
<p className="text-[var(--color-text-primary)]">Escape hatch</p>
</div>All Astryx components accept className. Tailwind utilities in the utilities layer override Astryx component styles in astryx-base:
<Button label="Custom" variant="primary" className="rounded-full shadow-xl" />
<Text type="body" className="text-blue-600 italic">Custom styled text</Text>Shadcn-style Tailwind components render correctly alongside Astryx. Both resets are nearly identical (shared modern-normalize lineage), so there are no conflicts:
<div className="grid grid-cols-2 gap-6">
<Card>...</Card>
<div className="rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
...
</div>
</div>Astryx reset and Tailwind Preflight overlap ~80%. The meaningful differences:
- Font smoothing: Astryx enables
-webkit-font-smoothing: antialiased; Tailwind doesn't - Color scheme: Astryx maps
data-themetocolor-schemefor light/dark mode - Placeholder color: Astryx uses
var(--color-text-secondary)token; Tailwind usescolor-mix()
Both resets can run together without conflicts. Neither breaks the other's components.
pnpm install
pnpm dev # or: cd apps/example-nextjs-tailwind && npx next dev