Frequently asked questions
Short answers to the questions people actually ask — philosophy, theming, integration, versioning, and how to get involved.
Why not just use Tailwind?
Tailwind and css-is-awesome optimise for different things. Tailwind ships atomic utilities you compose in markup; the tokens live in a config you recompile. cia ships tokens + mixins: the design language is a small set of CSS custom properties, and the primary API is SCSS mixins you compose in stylesheets. That means you get runtime re-skinning via one data-theme attribute and semantic class names instead of a utility stack.
The tradeoff: Tailwind's atomic classes don't re-skin at runtime, and the markup carries every design decision. cia's mixins keep markup semantic but require SCSS at author time. Neither is a knock on the other — pick based on whether your project wants its design system in HTML or in CSS. See /docs/migration-tailwind for a deeper comparison.
Why not just use Bootstrap?
Bootstrap is component-first: you get .btn, .card, .alert, and a Sass variable layer beneath them. css-is-awesome is token-first: components are assembled from the same token vocabulary, and theming is a single attribute swap instead of a Sass recompile.
If you already live in Bootstrap and just want a reskin, a Sass override works fine. If you want the design language to be the primitive and components to fall out of it, cia is the smaller, flatter system. See /docs/migration-bootstrap for side-by-side equivalents.
Why SCSS over Tailwind / plain CSS / CSS Modules?
SCSS gives us mixins, which are the primary API. Plain CSS custom properties alone aren't composable — you can't @include button(primary) in vanilla CSS. Tailwind is atomic — the composition unit is a class in markup, not a mixin in a stylesheet. CSS Modules scope styles per file but give you no vocabulary to compose across files.
SCSS plus tokens plus mixins is the durable combination: tokens are the runtime contract, mixins are the authoring contract. The React layer uses CSS Modules on top for component-scoped styles, so you get file-local scoping and a system-wide vocabulary at the same time.
What does "mixin-first" mean exactly?
It means the recommended way to style a thing is @include m.button(primary), not class="btn btn-primary" in markup. You compose styles semantically in SCSS instead of stacking utility classes in HTML.
/* mixin-first (recommended) — author your own class, variant is a mixin arg */ .save-btn { @include b.btn(primary); } <!-- utilities exist as escape hatches, not the default --> <button class="cia-btn-primary">Save</button>
The .cia-* utility classes exist as escape hatches for prototypes, one-offs, and projects that can't consume SCSS. They're not the recommended default.
How does the one-file theme swap actually work?
Every theme is a single CSS file that declares the same 127 required custom properties with theme-specific values, under :root, :root[data-theme="name"]. Because of the bare :root, dropping one file in as your theme.css reskins the page with no markup change at all — the data-theme attribute is optional in that case.
The attribute matters for the bundle: the shipped public/theme.css carries all 24 themes in one file, with the bare :root stripped, so data-theme on <html> is the only thing choosing between them. Flip it and the whole application reskins.
Persistence is a cookie read server-side before first paint, so the correct theme is baked into the initial HTML — no flash of unthemed content, no rebuild, no bundler step.
Can I make my own theme?
Yes. Every theme declares the same 127 required tokens (plus up to 36 optional ones) documented in the /docs/authoring/themes contract. Fork the contract, set values for every required slot, run the validator (node scripts/theme-validator.js), and drop the file in. If you skip a token the validator tells you which one.
Spacing is part of that: themes own the numbered scale --space-0…--space-9, so a theme can re-proportion the page and not just recolour it.
How many themes ship?
24 theme files, in eight families: Sketchbook (default warm paper), Boilerplate (neutral starter), Press (editorial newsprint), Prism (modern-app zinc), Graphite (dark aluminium), Glass (visionOS-style frosted), Cupertino (macOS / Apple HIG), and Terminal (CRT phosphor).
Each family ships three files: the base name auto-switches light and dark via light-dark(), while the -light and -dark variants pin a single mode. Preview the families in the Themes gallery.
How do I use my brand colors?
Override the relevant tokens in your own theme file. --ai drives primary actions (links, primary buttons, focus rings); --shu drives emphasis and danger; --ochre drives marginalia and accents. Replace those three and most of the visible brand moves with you.
If you want more than a colour swap, fork a shipped theme whose feel is closest to yours and change the palette. See /docs/tokens for the full vocabulary.
Does it work with SSR / Next.js / Remix?
Yes. The default pattern is a static export. The data-theme attribute is set server-side (or via an inline script that reads a cookie before first paint), which means the initial HTML already carries the right theme and there is no hydration mismatch.
For Next.js App Router specifically, see /docs/install#react-next.
What does it cost my bundle?
For the CSS-only consumer: one theme.css plus one base stylesheet. No runtime JavaScript. The consolidated theme.css that ships all 24 built-in themes is about 162 KB uncompressed / 18 KB gzipped — that is the price of carrying every theme at once.
Almost nobody needs that. If you only need one theme, use the per-theme file at public/themes/<name>/theme.css — roughly 6.5 KB uncompressed, 2.4 KB gzipped — and because it emits a bare :root it needs no data-theme attribute to take effect.
Is JavaScript required?
Not for styling. Plain HTML plus the two stylesheets renders a fully themed page with working buttons, cards, forms, tables, and the whole utility layer.
Not for interaction either. Accordion, modal, tooltip, dropdown and tabs ride native primitives — <details name>, <dialog>, [popover] — so the browser manages open/close state, Escape and light-dismiss. The same goes for the mobile toolkit (hamburger, drawer, sheet, dock): see /docs/mobile. A zero-JS page works great.
Does it have TypeScript types?
Yes. Every React component in the companion library ships with its prop types. Token names as a TypeScript union are on the roadmap — see the intro for status.
How do you handle breaking changes?
As of 1.0 we follow SemVer strictly: breaking changes only ever land in a MAJOR release. Every breaking change lands with a loud entry in CHANGELOG.md and, where we can, a codemod.
Post-1.0 we follow SemVer strictly. Breaking changes require a MAJOR bump, full stop. See VERSIONING.md at the repo root for the exact policy.
What's the license?
MIT. Use it, fork it, ship it in commercial work.
Is this production ready?
Yes. 1.0 has shipped and the package is published on npm (npm install css-is-awesome). The mixin API is stable under SemVer, and the token contract is locked (127 required slots plus 36 optional, validated on every theme).
We also eat our own dogfood: every pixel on this docs site — the buttons, cards, type scale, the theme picker — is built with cia. See the roadmap for what's next.
Can I contribute?
Yes. See CONTRIBUTING.md at the repo root for setup, style, and the PR checklist. Themes, bug fixes, and docs improvements are especially welcome.
I found a bug. What now?
Open an issue using the Bug Report template at .github/ISSUE_TEMPLATE/bug_report.yml. Include a minimal reproduction, the theme you were on, and the browser — the template asks for those anyway.
For security issues, don't file a public issue. Email jhansenportfolio@gmail.com directly.