[µ*]Mode CSSDocs
v1.0.0 · ~3 KB gzipped

Write HTML.
Not class soup.

mode.css styles semantic HTML out of the box, then lets you compose everything with an exact family of µ-* attributes — no classes, no build step, no runtime.

Get started View on GitHub
Overview

Introduction

One stylesheet turns plain, semantic markup into a polished, responsive, themeable page. You never touch a class attribute.

Most CSS frameworks ask you to decorate every element with a string of utility classes. mode.css takes the opposite bet: your HTML should describe meaning, and the cascade should handle appearance. The entire library is built around a single principle — a strong attribute cascade.

Instead ofYou write
<div class="grid grid-cols-3 gap-6"> <section µ-grid="3-col">
<div class="card border rounded shadow p-4"> <article>
<h3 class="text-xl font-bold mb-2"> <h3>
Setup

Installation

Add one <link>. There is no build step and nothing to configure.

From the CDN

<link rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/@microdom/mode.css@1/dist/mode.min.css">

From npm

npm install @microdom/mode.css
import "@microdom/mode.css"      // flat, readable build
import "@microdom/mode.css/min"  // minified build

Local file

<link rel="stylesheet" href="dist/mode.css">

Your first page

This is a complete, responsive page — sticky header with a no-JS hamburger menu, a centered reading column and a footer:

Hello, cascade

Native elements are styled automatically — no classes, no setup.

<body>
  <input type="checkbox" id="menu-toggle" hidden>
  <header>
    <div>
      <b>My Site</b>
      <label for="menu-toggle"><span></span></label>
      <nav><ul><li><a href="#">Home</a></li></ul></nav>
    </div>
  </header>

  <main>
    <section>
      <h1>Hello, cascade</h1>
      <p>Native elements are styled automatically.</p>
      <button>A native button</button>
    </section>
  </main>

  <footer><div><p>&copy; 2026</p></div></footer>
</body>
Concepts

The mental model

Everything you style lives in one of three attribute layers. Master these and you know the whole library.

LayerRoleExample
1 · Structure A bare attribute that establishes a layout. <section µ-grid>
2 · Modifier Space-separated tokens that mutate the structure. µ-grid="3-col cards"
3 · Semantic A domain attribute describing what the element is. µ-pricing

The one naming rule

Attribute values name the thing, never a CSS property. If a value reads like a Tailwind utility, it's wrong.

ExampleWhy
✓ doµ-grid="pricing"Describes the block.
✗ don'tµ-grid="centered"That's a property, not a thing.
✗ don'tµ-text-blueUtility class in disguise.
Layout

The page skeleton

mode.css homologates three pillars — header, main and footer — so content always lines up on the same lateral gutter.

Direct children of <body> share --screen-padding, and the real content centers inside --max-width. A <section> spans the full width, so its background can bleed edge-to-edge while its text stays aligned.

<body>
  <header><div> … brand + nav … </div></header>
  <main>
    <section> … reading content … </section>
  </main>
  <footer><div></div></footer>
</body>
Layout

Grids

A bare µ-grid is auto-responsive. Add a column token to take control.

Auto grid (default)

With no value, the grid auto-fits as many 280px+ columns as will fit.

Auto

Fits to space.

Fit

No breakpoints to manage.

Flow

Wraps when it needs to.

<section µ-grid>
  <article><header><h3>Auto</h3></header><p>Fits to space.</p></article>
  <article></article>
</section>

Explicit columns

Tokens 2-col through 6-col set a fixed track count that collapses gracefully on smaller screens (1 → 2 → full).

One

3-col on desktop.

Two

2-col on tablet.

Three

1-col on mobile.

<section µ-grid="3-col">
  <article></article>
  <article></article>
  <article></article>
</section>
TokenDesktop columns
µ-gridauto-fit (min 280px)
2-col6-col2 – 6 fixed tracks
sidebar-leftnarrow rail + wide content
sidebar-rightwide content + narrow rail
Components

Native elements

Headings, text, links, buttons and form fields are styled with zero attributes. These are the building blocks every layout inherits.

Heading 1

Heading 2

Heading 3

Body text with a link, plus a small note.

<h1>Heading 1</h1>
<h2>Heading 2</h2>
<p>Body text with a <a href="#">link</a>.</p>
<button>Primary action</button>
Components

Content elements

Quotes, code, keys, rules, figures and description lists — the prose furniture — are styled with zero attributes, in the same tokens as everything else.

“mode.css is an exact promise: semantic HTML looks finished. If an element ships with the browser, it ships styled.”

Inline code reads as code, and shortcuts like Ctrl + K read as keys.


<!-- One attribute, one mutation -->
<div µ-table="collapsed">
  <table>…</table>
</div>
<blockquote>
  <p>“mode.css is an exact promise: semantic HTML looks finished.”</p>
</blockquote>

<p>Inline <code>code</code> reads as code, and shortcuts like
  <kbd>Ctrl</kbd> + <kbd>K</kbd> read as keys.</p>

<hr>

<pre><code></code></pre>

Description lists

Tables
Data tables by default, µ-table for the irreducible.
ARIA states
Truthful state attributes, visually rewarded.
Content elements
Quotes, code, keys, figures and lists.
<dl>
  <dt>Tables</dt>
  <dd>Data tables by default, µ-table for the irreducible.</dd>
  <dt>ARIA states</dt>
  <dd>Truthful state attributes, visually rewarded.</dd>
</dl>

Figures

Placeholder illustration
A figure with its figcaption, muted and small by default.
<figure>
  <img src="…" alt="Placeholder illustration">
  <figcaption>A figure with its figcaption.</figcaption>
</figure>
Components

Cards

Any <article> inside a µ-grid becomes a card — a flex column with border, padding and radius. Use <header>, <p> and <footer> for consistent rhythm.

Starter

Everything you need to ship a small site.

Pro

For teams that need more room to grow.

<section µ-grid="2-col">
  <article>
    <header><h3>Starter</h3></header>
    <p>Everything you need to ship a small site.</p>
    <footer><button>Choose</button></footer>
  </article>
  <article></article>
</section>
The <footer> button stretches to the card width automatically.
Components

Tables

A bare <table> is a styled data table. Wrap it in µ-table to keep wide data scrollable — or collapsed into cards on phones.

Captions, header and footer rows, and row-hover feedback all come from the elements themselves. Controls mutate through the cascade alone: a <button> inside a table becomes dense to fit the row, and links pick up the accent color and an underline.

Invoices — Q3 2026, sorted by amount
Invoice Client Status Amount Actions
#1042 Aurora Labs Paid $12,400.00
#1039 Helix & Co. Pending $8,150.00
#1036 Nadir Studio Paid $3,975.50
#1031 Vega Media Overdue $1,200.00
4 invoices $25,725.50
<div µ-table>
  <table>
    <caption>Invoices — Q3 2026, sorted by amount</caption>
    <thead>
      <tr><th aria-sort="descending"><a href="#">Amount</a></th></tr>
    </thead>
    <tbody>
      <tr><td><button>View</button></td> </tr>
      <tr aria-selected="true"></tr>
    </tbody>
    <tfoot><tr><td>4 invoices</td></tr></tfoot>
  </table>
</div>
The “Amount” column declares aria-sort="descending" — truthful because the data is rendered sorted — and one row carries aria-selected="true".

Scroll wrapper

The bare attribute keeps the semantics intact and adds horizontal scroll when the viewport is narrower than the data — zero markup cost on the table itself.

Collapsed variant

Same table language, one mutation: µ-table="collapsed". Below 640px each row becomes a card and every cell shows its data-label — the declared markup cost of this pattern. Resize to see it.

Date Event Location Seats
2026-08-02 Microdom meetup #4 Buenos Aires 120
2026-09-14 Classless CSS workshop Online 500
2026-10-30 Mode v3 launch Córdoba 80
<div µ-table="collapsed">
  <table>
    <thead></thead>
    <tbody>
      <tr>
        <td data-label="Date">2026-08-02</td>
        <td data-label="Event">Microdom meetup #4</td>
        <td data-label="Location">Buenos Aires</td>
        <td data-label="Seats">120</td>
      </tr>
      <tr></tr>
    </tbody>
  </table>
</div>
The <thead> is hidden visually but kept for assistive technology.
Components

Tabs

HTML has no tabs element, so µ-tabs earns its attribute: radios + labels, the same no-JS hack as the hamburger menu.

Pair each <input type="radio"> with a <label for>, then add one <section> per panel — all direct children. The Nth radio shows the Nth panel, up to six tabs (enumerated like the µ-grid column spectrum). Bonus: radio groups already switch with — try it with the keyboard.

Each panel is a plain <section>. The Nth radio shows the Nth panel — up to six tabs, enumerated like the µ-grid column spectrum.

MetricValue
Signups1,284
Churn2.1%
<div µ-tabs>
  <input type="radio" name="demo-tabs" id="tab-overview" checked>
  <label for="tab-overview">Overview</label>
  <input type="radio" name="demo-tabs" id="tab-data">
  <label for="tab-data">Data</label>

  <section> … first panel … </section>
  <section> … second panel … </section>
</div>
Components

Accordion

<details> is the browser's own accordion — the library only dresses it. Exclusivity is the native name attribute: zero CSS, zero JS.

These three share name="faq", so they are exclusive: opening one closes the others.

Why no JavaScript?

Same spirit as the hamburger menu: <details> is the browser’s own accordion. Zero runtime.

Why no classes?

Because the element already says what it is. Styling the element keeps the HTML the single source of truth.

Why the name attribute?

Exclusive groups are a native <details> feature — declare a shared name and the browser handles the rest.

<details name="faq" open>
  <summary>Why no JavaScript?</summary>
  <p>Same spirit as the hamburger menu. Zero runtime.</p>
</details>
<details name="faq">
  <summary>Why no classes?</summary>
  <p>The element already says what it is.</p>
</details>
Components

Forms

A <form> is a grid; wrap each field in a <label> and everything aligns with shared focus styles.

<form>
  <label>Full name <input type="text"></label>
  <label>Plan
    <select><option>Starter</option></select>
  </label>
  <label>Message <textarea></textarea></label>
  <button type="submit">Send</button>
</form>

Fieldsets & options

Group related controls in a <fieldset>; the <legend> acts as the group's label. When a <label> wraps a checkbox or radio it flips from the column layout to an aligned row, and the controls pick up the accent color — no extra markup on any child.

Notification settings
<form>
  <fieldset>
    <legend>Notification settings</legend>
    <label><input type="checkbox" checked> Release announcements</label>
    <label><input type="radio" name="freq" checked> Weekly digest</label>
    <label><input type="radio" name="freq"> Real-time</label>
  </fieldset>
  <button type="submit">Save preferences</button>
</form>

Invalid fields

A field that declares aria-invalid="true" gets a danger border. The color reads from the optional --color-danger token and falls back to a default red when undeclared.

Declares aria-invalid="true". Override the fallback red with --color-danger.
<label>
  Email
  <input type="email" value="not-an-email" aria-invalid="true">
</label>
Components

ARIA states

Mode rewards accessible HTML: these looks come from truthful state attributes, never from classes.

State the truth in the markup and the styling follows. A sorted column declares aria-sort (truthful even without JS when the server renders the order), the current nav item declares aria-current="page" — replacing the classic .active class — and a region waiting for data declares aria-busy="true".

Busy region

This block declares aria-busy="true" while its data loads. It fades and ignores pointer events.

Invalid field

Declares aria-invalid="true". Override the fallback red with --color-danger.
<div aria-busy="true"></div>
<button disabled>Native disabled</button>
<a role="button" aria-disabled="true">ARIA disabled</a>
<input type="email" aria-invalid="true">
<a href="/docs" aria-current="page">Docs</a>
AttributeVisual reward
aria-sort="ascending / descending"Sort arrow on the <th>, pointer cursor
aria-selected="true"Accent-tinted table row
aria-busy="true"Region fades and ignores pointer events
aria-disabled="true" / :disabledUnified dimmed, not-allowed look
aria-current="page"Bold accent nav link
aria-invalid="true"Danger border (--color-danger)
Customize

Tokens & theming

Every visual decision is a CSS custom property. Override them in :root — no recompilation, no config file.

:root {
  --color-bg:       #ffffff;
  --color-text:     #1f2937;
  --color-accent:   #111827;
  --spacing:        1.5rem;   /* master rhythm unit */
  --radius:         6px;
  --max-width:      1200px;   /* reading column     */
  --screen-padding: 1.5rem;   /* shared gutter       */
}
TokenControls
--font-mainBase font stack
--font-monoMonospace stack (code, kbd, samp)
--size-h1 … --size-smallHarmonic type scale
--color-bg / text / muted / border / accentColor system
--color-dangerInvalid-field border (optional — has a built-in fallback)
--spacingMaster spacing (scales up at breakpoints)
--radiusGlobal corner radius
--max-widthReading-column width
--screen-paddingLateral gutter for the three pillars
Customize

Dark mode

A checkbox and one CSS rule — no JavaScript. (This very page uses it; try the ◐ / ◑ toggle in the header.)

<!-- in your markup -->
<input type="checkbox" id="theme-toggle" hidden>
<label for="theme-toggle">Toggle theme</label>

/* in your theme.css */
body:has(#theme-toggle:checked) {
  --color-bg:   #0b0d12;
  --color-text: #c9d1d9;
  --color-accent: #f3f6fb;
}

Because every component reads from the same tokens, re-painting the whole site is just a matter of swapping a handful of variables.

Customize

Migrating to mode

Refactoring class-heavy markup is mostly deletion. Flatten the divs, drop the classes, and let the cascade do the work.

Before — utility soup

<div class="grid grid-cols-3 gap-6 bg-gray-100 p-8 rounded-lg">
  <div class="border p-4 rounded bg-white shadow">
    <h3 class="text-xl font-bold text-blue-600 mb-2">Client</h3>
    <p class="text-sm text-gray-600">Description…</p>
  </div>
</div>

After — mode.css

<section µ-grid="3-col cards">
  <article>
    <h3>Client</h3>
    <p>Description…</p>
  </article>
</section>

Aesthetics that the defaults don't cover (a brand color, a special card treatment) live in your theme layer, keyed off the Layer-3 attribute:

[µ-grid~="cards"] article {
  box-shadow: var(--shadow-sm);
  border-radius: var(--radius);
}