Three authoring tiers
css-is-awesome ships three ways to author the same components — drop-in classes, SCSS mixins, or zero-class bare tags. All three resolve to the same router mixin per component, so styling stays consistent across an app that mixes them.
One router mixin per component. Three doors into it. That’s the entire architecture. Add a variant to btn(variant) once and every tier picks it up.
Tier 1 — Drop-in CSS + HTML
Two stylesheets, one class per element, ship. No build, no SCSS, no framework. Audience: designers, marketing pages, prototypes, anyone without a build step.
<!-- in your <head> --> <link rel="stylesheet" href="css-is-awesome.min.css"> <link rel="stylesheet" href="theme-sketchbook.css"> <!-- in your <body> --> <a class="cia-btn-primary" href="/start">Get started</a> <a class="cia-btn-outline" href="/docs">Read docs</a> <article class="cia-card">…</article>
One class per element. No BEM, no __element / --modifier chains. The cia- prefix keeps every utility and component class out of your app’s namespace.
Tier 2 — SCSS mixins + HTML
Author your own class names; the mixin handles the styling. Audience: product teams that want their own domain vocabulary in markup (hero-cta, product-card) without giving up a design system.
// app.scss @use 'css-is-awesome/scss/components/buttons' as b; @use 'css-is-awesome/scss/components/data' as d; @use 'css-is-awesome/scss/mixins' as m; .hero-cta { @include b.btn(primary, $px: 6, $r: full); @include m.elevation(2); } .checkout-cancel { @include b.btn(outline); } .product-card { @include d.card-base($shadow: 2); } <!-- in your HTML --> <a class="hero-cta" href="/buy">Buy now</a> <a class="checkout-cancel" href="/cart">Back to cart</a> <article class="product-card">…</article>
Variant is an argument to the mixin, not a class modifier. Every parameter is overridable — the mixin accepts $px, $r, $shadow, and friends to tune one-off components without breaking the system.
Tier 3 — Bare tags (opt-in Pico-mode)
One @use line styles every common bare tag in your site — buttons, tables, inputs, forms, headings, lists. Audience: content-heavy sites, blog posts, READMEs rendered as HTML, anywhere the author doesn’t want to think about classes.
// app.scss — one line styles the whole site @use 'css-is-awesome/scss/recipes/bare-tags'; <!-- in your HTML --> <button>Save</button> <button type="reset">Cancel</button> <table>…</table> <input type="email">
The recipe styles bare tags at specificity 0,0,1 — no @layer, no :where(). Any class-based selector you add wins automatically, so React + CSS Modules + third-party libs all override the recipe without ceremony.
The same button, three ways
All three of these resolve to the same btn(primary) mixin output. Visually identical, conceptually different.
<!-- Tier 1 --> <button class="cia-btn-primary">Save</button> <!-- Tier 2 --> <button class="save-btn">Save</button> // app.scss .save-btn { @include b.btn(primary); } <!-- Tier 3 --> <button>Save</button>
Architecture
- Single source of truth. One mixin per component (
btn,card,input,alert, …) with private internals. - Router pattern.
btn(variant)dispatches to private mixins. Variant is an argument, not a class modifier — that’s why there’s no BEM. - Tier 1 = router output baked into single utility classes (
.cia-btn-primary,.cia-btn-outline, …). - Tier 2 = direct router
@includein author SCSS under custom class names. - Tier 3 = router
@includeapplied to bare-tag selectors via the recipe. - Tier 4 — React. Components in
src/wrap the same mixins through CSS Modules. Same output, framework-aware ergonomics. See the recipes page for live examples.
Change the router, every tier updates. Add a variant once, every tier gets it. That’s the entire payoff.
Picking a tier
| You’re building… | Use |
|---|---|
| A landing page, prototype, or static site with no build | Tier 1 |
| A product app where designers want semantic class names | Tier 2 |
| A content site, blog, or generated HTML where classes are noise | Tier 3 |
| A React or Next.js app | Tier 4 — see install |
Tiers compose. A Tier 2 product can drop in a Tier 3 recipe for its /blog route, and a Tier 1 marketing page can sit next to a Tier 4 app under the same theme file.