Install

One <link> tag is the whole thing, and it is the wrong thing to ship: it carries all 71 components, which are 24,808 lines of CSS between them, and a page uses a handful. Both paths are below, with the one caveat that comes before either of them.

There is no npm package yet

npm install mostlycss does not work, and the reason is not a network problem: nothing called mostlycss has been published. The registry and jsdelivr both answer 404 — measured on 14 August 2026, when this site was still telling people to paste a CDN address that had never resolved.

So this site serves the built package itself, at https://ui.bodge.tools/dist/, in the same layout the documentation names everywhere else: dist/css/button.css is https://ui.bodge.tools/dist/css/button.css. That is what the tags on this page point at. The day the library is published under a real version number, those addresses become the CDN's and nothing else about this page changes.

No build step, no package manager, no configuration. You get the reset, the full token set, every skin, light and dark, and all 71 components.

It is .min.css deliberately. The readable mostlycss.css is served beside it and is worth opening — the library's arguments are written in it, at length — but it carries every one of those comments over the wire, and it is five times the file. Read one, link the other.

Or only what you use

Every component ships as its own file, and the package's exports map names each one, so a bundler takes the files a page needs and nothing else. This is what the eight sites built on the library do; none of them imports the bundle.

/* The foundation: tokens, modes, the default skin, the reset. */
import 'mostlycss/css/spacing';
import 'mostlycss/css/typography';
import 'mostlycss/css/motion';
import 'mostlycss/css/elevation';
import 'mostlycss/css/shape';
import 'mostlycss/css/materials';
import 'mostlycss/css/sizing';
import 'mostlycss/css/modes';
import 'mostlycss/css/default';
import 'mostlycss/css/reset';

/* Then one line per component you actually put on the page. */
import 'mostlycss/css/button';

The first 10 lines are not optional. They are every file the bundle imports before its first component: the token groups a component reads its custom properties from, the two files that set color-scheme and the level 1 colours, and the reset the components are drawn on top of. Import a component without them and the markup is right, the classes match and the page looks like nothing was applied — which is the failure mode of this whole approach: nothing warns you. A class with no stylesheet behind it is silent.

The order does not matter. Every file — component, token group, utility — opens by restating the layer order, so the cascade is the same however your bundler ends up concatenating them. That is what makes cherry-picking safe rather than a thing that works until it does not.

Without a bundler the same thing is one tag per file:

<link rel="stylesheet" href="https://ui.bodge.tools/dist/css/spacing.min.css">
<!-- …the rest of the foundation… -->
<link rel="stylesheet" href="https://ui.bodge.tools/dist/css/button.min.css">

The 22 components that need a script

49 of the 71 ship no JavaScript at all: they are native elements with a class on them, and the states you can see — checked, disabled, invalid, open — are the browser's. The remaining 22 register a behaviour, and each one's page says what you lose without it. Import the module by its own specifier and opt the markup in with its data-ui attribute.

import 'mostlycss/js/components/range';
<div class="range" data-ui="range">…</div>

That specifier, and not the package root. import 'mostlycss' is a bundle carrying a second copy of the registry, so a page that holds both initialises every element twice — two listeners on one control, and the second undoing the first.

The icons are a file on your server

The library draws no icon until the sprite is somewhere your site serves it. There is no CSS switch and no fallback glyph: an <svg> whose <use> points at a missing file renders empty, silently. One command, once.

mostlycss sprite --out public   # writes public/sprite.svg
mostlycss sprite --inline       # prints it, to paste into a template
<svg class="icon" aria-hidden="true"><use href="/sprite.svg#icon-check" /></svg>

The href is a URL on your own site, not a package path. /sprite.svg assumes the sprite sits at the web root, which is where --out public puts it; serving it from anywhere else means adjusting the href, and the command prints the right one.

mostlycss here is the package's own bin, which means npx cannot fetch it for the same reason npm install cannot: it would be resolved from a registry that has nothing under that name. Until it is published, the command is the CLI in a checkout of the library — node cli/index.js sprite --out public — or the binary npm links into node_modules/.bin for a workspace that has the library as a dependency, which is how the eight sites run it.

Or take the source and own it

A component you need to change is not a dependency. The CLI copies its source into your repository, and from that moment it is your file: no override layer, no !important, no upgrade that undoes your edit.

mostlycss add button                # into mostlycss/
mostlycss add button --dir src/ui      # somewhere else
mostlycss add button --js              # compiled JS, for a project with no build step
mostlycss list                       # what there is to copy

The landing page explains why you usually do not need this: everything the library ships is in @layer, and unlayered CSS — which is what you write — beats a layer at any specificity. Overriding a component is an ordinary rule in your own stylesheet.