Migrating from Tailwind
Tailwind gives you atomic utilities; css-is-awesome gives you mixin-first composition and token-driven themes — fewer classes in your markup, one data-theme swap for the whole app.
Why migrate
Tailwind is a great atomic engine, and plenty of teams ship great products with it. If that's you, stay. The reasons to move off are almost always the same four: themes that need a rebuild, markup that drifts toward class soup, a consumer-side build pipeline, and JavaScript you didn't ask for.
- Token-driven theming. One
data-themeflip reskins everything — no rebuild, no JIT pass, no PostCSS. Tailwind's equivalent is editingtailwind.config.jsand recompiling the entire stylesheet. - Mixin-first composition. You compose styles in SCSS with
@include, keeping markup semantic (<Button variant="primary">) instead of stacking a dozen atomic classes on every element. - Smaller runtime footprint. Consumers ship plain CSS. No JIT, no PostCSS, no content-scanning build step — cia is valid CSS on delivery.
- Zero JavaScript by default. Tailwind leans on Headless UI, Radix, or Alpine for interactivity. cia ships its own React components (Modal, Tabs, Dropdown) and they're opt-in — plain HTML + CSS works on its own.
Philosophy differences
The mental model shifts in four places. Layouts, sizing, and color semantics carry over once you remap them.
| Concern | Tailwind | css-is-awesome |
|---|---|---|
| Theming | tailwind.config.js + rebuild | data-theme attribute + CSS custom properties |
| Composition | Atomic classes stacked on every element | Mixin @include in SCSS, or a small set of .cia-* utilities |
| Build pipeline | JIT / PurgeCSS scans templates at build time | No build step on the consumer side — cia ships CSS |
| Dark mode | dark: variant on every affected class | data-theme="graphite" swaps one attribute for the whole app |
| Directive | @apply btn-class text-sm (Tailwind class list) | @include m.button-primary (mixin name) |
Equivalents table
These are the substitutions you'll reach for daily. When a row lists both a utility and a mixin, pick whichever layer your project lives on — markup or SCSS.
| Tailwind | css-is-awesome |
|---|---|
class="flex items-center justify-center" | class="cia-flex-center" or @include m.flex-center |
class="grid grid-cols-2 gap-4" | class="cia-grid cia-grid-cols-2 cia-gap-md" or @include m.grid(2) |
class="p-4" / class="px-6 py-3" | class="cia-p-md" / class="cia-px-lg cia-py-sm" (t-shirt scale — see utilities) |
class="text-gray-700" | style={{ color: "var(--ink-soft)" }} — or .cia-text-text-secondary for the closest semantic utility |
class="bg-blue-500" | style={{ background: "var(--ai)" }} (cia's indigo accent token) |
class="rounded-lg shadow-md" | style={{ borderRadius: "var(--radius-lg)", boxShadow: "var(--shadow-md)" }} or class="cia-rounded-lg cia-shadow-md" |
class="hidden md:block" | class="cia-hidden cia-md:block" (cia responsive prefix is cia-<bp>:, breakpoint keys are sm md lg xl 2xl) |
@apply flex items-center gap-2 | @include m.inline(2) — or any other mixin. @include takes a mixin name, not a class list. |
Headless UI <Menu> | <Dropdown> from cia |
Headless UI <Dialog> | <Modal> from cia |
Component mapping
Tailwind leaves behavior to you; most teams pull in Headless UI, Radix, or Alpine. cia ships the React layer in-core, so you can delete a dependency as you migrate.
- Tailwind + Headless UI
<Menu>→<Dropdown>from cia. Controlled or uncontrolled, keyboard-accessible out of the box. - Tailwind + Headless UI
<Dialog>→<Modal>from cia. Focus trap, escape to close, and overlay styling are built in. - Tailwind
<Transition>wrappers → built into cia overlays. Motion tokens (--motion-duration-*,--motion-ease-*) drive enter/exit; no wrapper component needed. - Tailwind plugin ecosystem (
@tailwindcss/forms,@tailwindcss/typography) → cia ships forms and typography in-core. Use<Input>,<FormField>, and the@include m.type(heading-1)scale directly.
Before & after
Same output, two markup styles. Tailwind first, then cia.
Button
<!-- Tailwind --> <button type="button" class="bg-blue-500 hover:bg-blue-600 text-white font-medium px-4 py-2 rounded-md"> Save changes </button>
{/* css-is-awesome (React) */} <Button variant="primary">Save changes</Button> /* css-is-awesome (SCSS) */ .save-btn { @include m.button-primary; }
Card
<!-- Tailwind --> <div class="bg-white rounded-lg shadow-md p-6 border border-gray-200"> <h3 class="text-lg font-semibold text-gray-900 mb-2">Title</h3> <p class="text-gray-700">Body copy here.</p> </div>
{/* css-is-awesome (React) */} <Card title="Title"> Body copy here. </Card> /* css-is-awesome (SCSS) */ .profile-card { @include m.card-base; }
Step-by-step migration
Tailwind and cia can coexist in the same page while you convert. Don't do a big-bang rewrite.
- Install cia alongside Tailwind. Keep Tailwind where it is and add cia's theme + base stylesheets. See /docs/install.
- Pick a theme file.
<html data-theme="press">reskins the whole app without a rebuild. Preview themes before committing — see /docs/tokens. - Convert one component type at a time. Start with buttons — highest visibility, lowest risk. Replace
<button class="bg-blue-500 px-4 py-2 rounded">with<Button variant="primary">. - Replace stacked utilities in common layouts.
flex items-center justify-centerbecomescia-flex-center(markup) or@include m.flex-center(SCSS). A regex codemod handles the bulk. - Delete Tailwind plugin imports for features cia handles —
@tailwindcss/formsand@tailwindcss/typographyboth map to core cia. - Remove Tailwind + PostCSS config. Delete
tailwind.config.js, droptailwindfrompackage.json, remove@tailwinddirectives from your root CSS, and runnpm run validate-themesto confirm nothing references Tailwind-only variables.
Gotchas
- Tailwind's
dark:variant doesn't map 1:1. cia uses thedata-themeattribute for any theme switch —data-theme="graphite"is cia's dark mode, but it's just another theme and the mechanism is the same as brand themes. @applyand@includeare close, not identical.@applytakes a list of Tailwind classes;@includetakes a mixin name and (optional) arguments. You can't drop-in replace class lists with mixins — pick the mixin that matches the intent.- Tailwind's JIT purges unused classes. cia's utility set is intentionally small and always shipped — there's no content scan, and the full
.cia-*surface is in the bundle. If size matters, lean on mixins instead of utilities. - Tailwind color scale → cia semantic tokens. There's no 1:1 between
gray-500and a cia token. Choose by role:--inkfor foreground,--text-secondaryfor muted copy,--aifor the indigo accent,--shufor vermilion. Think in roles, not shades. - Breakpoints differ — verify pixel values. cia's breakpoints are
sm 640px,md 768px,lg 1024px,xl 1280px,2xl 1536px. Tailwind defaults match these today, but if you customized your Tailwindscreens, re-check the mapping before assuming a codemod is safe. See /docs/utilities for the source list.
Further reading
- /docs/mixins — every mixin with a signature and a live example.
- /docs/tokens — the full token vocabulary, and how to build a theme file.
- /docs/utilities — the
.cia-*class reference, including responsive variants.