Skip to the page

MCSS-Lite 0.4.1

GitHub

Build UI from declared parts

MCSS-Lite is a pure-CSS design system where every class, state and token is written down in a contract. People and AI agents read the same contract, and mcss-lite validate rejects any part that isn't in it.

An agent guesses c-button--warning

error Unknown class "c-button--warning". Valid modifiers of c-button: --primary, --secondary, --ghost, --danger, --sm, --lg. Only classes from the MCSS-Lite contracts exist.

See the piece that doesn't fit

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/gabrielcpule/mcss-lite@v0.4.1/dist/mcss-lite.min.css">

Or install it from GitHub: npm install github:gabrielcpule/mcss-lite#v0.4.1. MCSS-Lite is not on the npm registry.

Step 1: Lay the baseplate

Every class says what it is: l- for layouts, c- for components, u- for utilities. Layouts own all spacing and components never set their own outer margin, so any part fits anywhere. Each dashed outline below is one layout.

New parts (× is how many times a part is used in this build)

  • l-section, used 1 time in this buildModifiers --sm
  • l-container, used 1 time in this buildModifiers --narrow
  • l-stack, used 2 times in this buildModifiers --sm --lg
  • l-center, used 1 time in this build
  • l-sidebar, used 1 time in this buildElements __sidebar __content
  • l-switcher, used 1 time in this build

Also in this build c-button, used 2 times in this build l-grid, used 1 time in this build c-card, used 5 times in this build u-text-center, used 1 time in this build

Projects

Side by side on wide screens
Stacked on narrow screens

Centered at reading width.

Step 2: Seat the buttons in a cluster

l-cluster lines parts up in a row that wraps. Primary goes last; destructive actions get the danger brick.

New parts (× is how many times a part is used in this build)

  • l-cluster, used 4 times in this build
  • c-button, used 11 times in this buildModifiers --primary --secondary --ghost --danger --sm --lgStates data-state="disabled" data-state="loading"

Also in this build l-stack, used 1 time in this build u-margin-0, used 1 time in this build

Add a publish date to schedule the post.

Step 3: Snap on the badges

Short status labels. The text carries the meaning; the color only reinforces it.

New parts (× is how many times a part is used in this build)

  • c-badge, used 6 times in this buildModifiers --primary --success --warning --error --info

Also in this build l-cluster, used 1 time in this build

Draft New Active Expiring Failed Beta

Step 4: Place cards on the grid

l-grid sets the gap. Cards group related content; an interactive card is one real link. A utility such as u-margin-0 is the last resort, for the one override no part covers.

New parts (× is how many times a part is used in this build)

  • l-grid, used 1 time in this buildModifiers --2-col --3-col --4-col --responsive
  • c-card, used 3 times in this buildModifiers --elevated --bordered --interactiveElements __header __title __body __footer
  • u-*, used 3 times in this buildUsed here u-margin-014 more on the utilities sheet

Also in this build l-cluster, used 2 times in this build c-badge, used 1 time in this build c-button, used 1 time in this build

Team plan

Active

12 of 20 seats used. Renews on 1 March.

Updated 2 hours ago

Invoices go to finance@example.com on the 1st of each month.

Step 5: Build the form

Each field wraps its label, control, help and error. States live in data-state, paired with ARIA.

New parts (× is how many times a part is used in this build)

  • c-form-field, used 3 times in this buildElements __label __help __errorStates data-state="error"
  • c-input, used 3 times in this buildStates data-state="error" data-state="success" data-state="disabled"

Also in this build l-stack, used 1 time in this build l-cluster, used 1 time in this build c-button, used 1 time in this build

We'll send the invite here.

Use at least 12 characters.

Step 6: Fit the checks and switches

Checkboxes pick any number, radios pick exactly one, a toggle applies straight away. All three keep the native input, so every state is a real attribute, not a class.

New parts (× is how many times a part is used in this build)

  • c-checkbox, used 2 times in this buildElements __input __label __hintStates :checked :indeterminate :disabled [aria-invalid="true"]
  • c-radio, used 2 times in this buildElements __input __label __hintStates :checked :disabled
  • c-toggle, used 1 time in this buildElements __input __label __hintStates :checked :disabled data-state="loading"

Also in this build l-grid, used 1 time in this build c-form-field, used 2 times in this build l-stack, used 3 times in this build

Plan

Up to 20 seats, billed monthly.

Add-ons

Choose at least one add-on for the Business plan.

Step 7: Raise the signals

Alerts are printed notices, not bricks: paper and a keyline, no shadow. Severity shows three ways at once, as a shaped icon, a hidden word and a heavier bottom edge, so it never rests on color.

New parts (× is how many times a part is used in this build)

  • c-alert, used 4 times in this buildModifiers --info --success --warning --errorElements __icon __body __title __actions __close
  • c-icon, used 5 times in this buildModifiers --sm --lg

Also in this build l-stack, used 1 time in this build u-sr-only, used 4 times in this build l-cluster, used 1 time in this build c-button, used 1 time in this build

Information: Maintenance on 3 October 2026

Sign-in is paused from 22:00 to 23:00 UTC.

Success: Invoice sent

Acme Inc. will get it by email in a few minutes.

Warning: Your trial ends on 12 October 2026

Choose a plan to keep your 4 projects.

Error: The changes weren't saved

The connection dropped. Check your network and save again.

Step 8: Fit the dialog

Prefer a native dialog opened with showModal(). The preview below is contained; the button opens the real thing.

New parts (× is how many times a part is used in this build)

  • c-modal, used 1 time in this buildElements __backdrop __container __header __title __close __body __footerStates data-state="closed"

Also in this build u-margin-0, used 1 time in this build c-button, used 2 times in this build

This piece doesn't fit

An agent wrote this sign-up form from memory. npx mcss-lite validate found 4 problems, and the rebuilt form passes with 0 issues. Both results come from the real validator, run when this page was built.

A brick tilted off its baseplate next to a brick seated flush

What the agent wrote

<form class="c-form">
  <input class="c-input" type="email" placeholder="Work email">
  <p style="color: #d93025">We never share your email.</p>
  <button class="c-button c-button--primary">Submit</button>
</form>

validate: 1 error, 3 warnings

  1. Line 1 error Unknown class "c-form". Only classes from the MCSS-Lite contracts exist. [unknown-class]
  2. Line 2 warning This <input> has no label. Add <label for="its-id"> (c-form-field__label). The placeholder "Work email" disappears as soon as someone types, so it can't be the label. [label-missing]
  3. Line 3 warning Inline style uses a raw color. Use a token, e.g. var(--color-text-default). [raw-color]
  4. Line 4 warning "Submit" says nothing out of context. Name the action and its object, e.g. "Save changes" or "Download the invoice". [content-vague]

The piece that fits

We never share your email.

<form class="l-stack">
  <div class="c-form-field">
    <label class="c-form-field__label" for="signup-email">Work email</label>
    <input class="c-input" type="email" id="signup-email" aria-describedby="signup-email-help">
    <p class="c-form-field__help" id="signup-email-help">We never share your email.</p>
  </div>
  <div class="l-cluster">
    <button type="submit" class="c-button c-button--primary">Create account</button>
  </div>
</form>

validate: 0 issues

Take the parts with you

Point your agent at AGENTS.md before it writes markup, and run npx mcss-lite validate after every edit.

The agent's form: 4 problems caught by validate, 0 in the rebuild.

Every build is checked: text and controls meet WCAG AA contrast in light and dark, every token name from 0.1.0 still exists, and every example on these pages passes the validator.

Designed and built by Gabriel Pule.

Is this the right kit?

Good fit

  • UI written by coding agents: the contracts tell them what exists, and mcss-lite validate checks what they wrote.
  • Server-rendered apps (Rails, Django, Laravel, htmx, Astro): plain classes work in any template, with no runtime JavaScript.
  • Forms, settings and CRUD screens, internal tools and prototypes.

Pick something else

  • Complex widgets such as comboboxes, date pickers and data grids: GitLab Pajamas, Primer or shadcn/ui ship them.
  • A React component API with typed props: shadcn/ui or Primer React.
  • Dense data products, or a brand that must not look like building bricks.

Delete project?

Borealis and its 4 pages will be deleted. This can't be undone.