CanonCSS
GitHub

The same card, three ways

Not a takedown - Tailwind is excellent for humans who want control. This is about what happens when a model writes your UI.

Tailwind v4

~620 chars
<div class="max-w-sm rounded-xl border border-gray-200
     bg-gray-50 shadow-sm overflow-hidden">
  <div class="px-6 py-4 border-b border-gray-200 font-medium">
    Pro plan
  </div>
  <div class="p-6">
    <p class="text-gray-500">Everything in Free, plus…</p>
  </div>
  <div class="px-6 py-4 border-t border-gray-200 bg-gray-100
       flex items-center gap-2">
    <button class="inline-flex items-center justify-center
        rounded-lg bg-gray-900 px-4 py-2 font-medium text-white
        hover:bg-gray-800 transition-colors">
      Upgrade
    </button>
  </div>
</div>

Every value is a decision: gray-50 or gray-100? rounded-lg or xl? px-6 or p-6? Each is valid - so each generation chooses differently.

Vanilla CSS

~830 chars + naming things
<!-- HTML -->
<article class="card">
  <div class="card-header">Pro plan</div>
  <div class="card-body">
    <p class="muted">Everything in Free, plus…</p>
  </div>
  <div class="card-footer">
    <button class="btn btn-primary">Upgrade</button>
  </div>
</article>

/* CSS - written, named and maintained by you */
.card { border: 1px solid #e5e7eb; border-radius: 12px;
        background: #f9fafb; box-shadow: 0 1px 2px rgb(0 0 0 / .05);
        overflow: hidden; }
.card-header { padding: 16px 24px; border-bottom: 1px solid #e5e7eb;
               font-weight: 500; }
.card-body { padding: 24px; }
.card-footer { padding: 16px 24px; border-top: 1px solid #e5e7eb;
               background: #f3f4f6; display: flex; gap: 8px; }
.muted { color: #6b7280; }
.btn { /* …and 15 more lines for states */ }

Total control, total responsibility: you invent the class names, write every state, and keep it consistent across the codebase yourself.

Canon

~280 chars
<article data-component="card">
  <div data-slot="header">Pro plan</div>
  <div data-slot="body">
    <p data-tone="subtle">Everything in Free, plus…</p>
  </div>
  <div data-slot="footer">
    <button data-component="button">Upgrade</button>
  </div>
</article>

One canonical form. Ask five times, get the same markup five times - and canon-lint proves it mechanically.

Side by side

Tailwind v4Vanilla CSSCanon
Valid ways to write this cardHundredsUnlimitedOne
Same output across LLM generationsVaries per runVaries per runConsistent
Mechanical validation--npx canon-lint
Build stepYesNoNo
CSS shipped~10kb (purged, varies)Grows with the project3.8kb gzip, fixed
Prompt cost to teach an LLMTrained-in, but unconstrainedN/A~600 tokens
Design freedomVery highTotalDeliberately low

When to use what - honestly

Use Tailwind when…
A designer or design-minded dev is crafting a distinctive, branded product by hand and wants pixel-level control.
Use vanilla CSS when…
You need something no framework expresses - bespoke art direction, unusual layouts, heavy animation.
Use Canon when…
An LLM writes most of your UI and you want every page it produces to look like one coherent product - dashboards, internal tools, MVPs, agent-generated apps.