Migrating from Bootstrap
If you know Bootstrap, you mostly know css-is-awesome — here's what changes and why.
Why migrate
Bootstrap is a great starting point for a lot of projects, and if yours is humming along, you don't have to move. But the cost of staying shows up in a few familiar places: long theme recompiles, utility markup sprawl, and a JavaScript bundle you may not need. css-is-awesome rethinks those trade-offs.
- Token-driven theming. One theme file (a flat list of CSS custom properties) reskins the entire system. No Sass recompile, no build step — swap the
<link>and reload. - Mixin-first API. You compose styles at the component level with SCSS mixins instead of stringing together a dozen utility classes in your markup.
- Smaller install footprint. Import only the parts you use. The utility layer is optional; the core ships as tokens + mixins.
- Zero JavaScript by default. cia's CSS is framework-agnostic. React components exist for the interactive pieces (Modal, Tabs, Dropdown) but they're opt-in.
Philosophy differences
The mental model shifts in four places. Everything else is close enough that muscle memory carries over.
| Concern | Bootstrap | css-is-awesome |
|---|---|---|
| Theming | $primary Sass variable + full recompile | --ai CSS custom property + hot swap |
| JS components | jQuery-era bundle + data-bs-* attributes | React components (or none — plain HTML works) |
| Utilities | .mt-3 .px-4 .d-flex — unprefixed, global | .cia-* namespaced, mixin-first |
| Grid | 12-column .row .col-md-6 | Native CSS Grid + flex mixins (@include m.grid(2)) |
Equivalents table
Day-to-day, these are the substitutions you'll reach for. When both a React component and a mixin are listed, pick whichever layer your project already lives on.
| Bootstrap | css-is-awesome |
|---|---|
.btn .btn-primary | <Button variant="primary"> or @include m.button-primary |
.card | <Card title="..."> (SCSS component styles live in scss/components/_cards.scss) |
.container, .container-fluid | @include m.container (default xl) / @include m.container(full) |
.row .col-md-6 | @include m.grid(2) or display: grid; grid-template-columns: 1fr 1fr |
.d-flex .justify-content-center .align-items-center | .cia-flex-center (or @include m.flex-center) |
.mt-3 .px-4 | .cia-mt-md .cia-px-md (t-shirt scale — see utilities) |
.text-primary .bg-light | style={{ color: "var(--ai)", background: "var(--surface-subtle)" }} — or a semantic utility |
.alert .alert-success | <Alert status="success"> |
.badge .bg-success | <Badge status="success"> |
.modal + data-bs-toggle | <Modal> (React, controlled) |
.nav .nav-tabs | <Tabs> (React) |
.form-control, .form-label | <Input> + <Label>, or <FormField> for the combined pattern |
Side-by-side: button
Same output, two markup styles. Bootstrap first.
<!-- Bootstrap --> <button type="button" class="btn btn-primary"> Save changes </button>
{/* css-is-awesome (React) */} <Button variant="primary">Save changes</Button> /* css-is-awesome (SCSS) */ .save-btn { @include m.button-primary; }
Side-by-side: two-column layout
Bootstrap leans on a 12-column abstraction. cia uses CSS Grid directly — the mixin is a shortcut, not a different system.
<!-- Bootstrap --> <div class="row"> <div class="col-md-6">Left</div> <div class="col-md-6">Right</div> </div>
/* css-is-awesome */ .two-col { @include m.grid(2); } {/* or inline */} <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1rem" }}> <div>Left</div> <div>Right</div> </div>
Step-by-step migration
Treat this as a gradual rollout, not a rewrite. Bootstrap and cia can coexist in the same page while you convert.
- Install cia alongside Bootstrap. Don't rip Bootstrap out on day one. Add cia's theme + base stylesheets after Bootstrap's so cia wins specificity ties. See /docs/install.
- Pick a theme file. One
<link>swap gives you cia's entire design language. Preview themes before committing — see /docs/tokens. - Convert one component type at a time. Start with buttons — highest visibility, lowest risk, and the fastest way to sanity-check your theme. Alerts, badges, and cards are natural next stops.
- Replace Bootstrap utilities as you touch files.
.mt-3becomes.cia-mt-md,.d-flexbecomes.cia-flex. A regex codemod handles the bulk of this — don't do it by hand. - Swap Bootstrap JS for React (or nothing). Modals, tabs, dropdowns, tooltips — every Bootstrap JS plugin has a cia React equivalent. Forms and buttons often need no JS at all.
- Remove Bootstrap last. Delete the Bootstrap
<link>, dropbootstrapfrompackage.json, and runnpm run validate-themesto confirm nothing still references Bootstrap-only variables.
Gotchas
- No bundled reset. Bootstrap ships Reboot (a
normalize.cssfork) as part of its base. cia does not. If you were relying on Bootstrap's cross-browser reset, addnormalize.css(or your own) before cia. .d-noneis not.cia-hidden. The classes look equivalent, but their responsive variants differ — Bootstrap uses.d-md-none, cia uses.cia-md:hidden. Check breakpoint prefixes when you codemod.- No 12-column grid. cia never ships a
.row/.col-*system. Use CSS Grid withgrid-template-columns, or the@include m.grid(n)mixin for common column counts. - Color tokens are named, not numbered. cia uses
--ai(indigo accent),--shu(vermilion),--ink,--paper— not$primary,$danger,$secondary. Semantic aliases like--success-textand--error-subtleexist for status colors. - Icons are separate. Bootstrap Icons are not part of cia. Bring your own icon system (Lucide, Phosphor, or SVG sprites). See /docs/tokens for the sizing tokens icons should respect.
Further reading
- /docs/tokens — the full token vocabulary, and how to build a theme file.
- /docs/mixins — every mixin with a signature and a live example.
- /docs/utilities — the
.cia-*class reference, including responsive variants.