Most CSS frameworks today ask you to memorize a class vocabulary (btn btn-primary btn-lg) or install a compiler (Tailwind's JIT, PostCSS pipelines). Weblin makes neither ask. Here's why, decision by decision.

Attributes instead of classes

<button primary> instead of <button class="btn btn-primary">.

Classes describe how — a bag of utility rules composed at author time. Attributes describe what — a semantic role the browser and the framework both understand. This isn't cosmetic: attributes are queryable by CSS ([primary]), by JS (element.hasAttribute('primary')), and by assistive tech in ways ad-hoc class names never are. It also means variants can't silently drift — there's exactly one way to mark a button primary, not .btn-primary, .is-primary, and .button--primary scattered across a codebase.

The cost: attribute selectors are marginally more verbose in CSS than a single class. We accepted that cost because it buys long-term consistency across a project with no linter enforcing naming conventions.

A restricted palette, on purpose

Warm light-grey page, white cards, near-black text, three accent families in two tiers each. Not a design-token explosion with fifty shades.

Design systems that expose unlimited tokens end up used inconsistently — every developer picks a "close enough" value. A restricted palette removes that decision entirely. If you need a shade that isn't there, that's a signal to reconsider the design, not to add a token.

Native HTML5 as the substrate, not an afterthought

<dialog>, popover, commandfor/command, view-transitions, :has() — these aren't polyfilled or reimplemented. Weblin styles what the browser already does.

This is the actual bet the project makes: the platform has quietly shipped most of what a component library used to hand-roll in JS (modals, popovers, accordions via <details>, tabs via :has()). Every year a framework doesn't lean on these is a year of JS someone will eventually have to maintain, debug, and ship to the client for free. Weblin's job shrinks every time the browser ships a new primitive — that's the design goal, not a side effect.

Zero build, on principle, not just for convenience

<link rel="stylesheet" href="css.css"> is the entire install. No npm install, no postcss.config.js, no watch process.

This matters more than it sounds. A build step is a second system a project has to keep alive — Node version pinning, lockfile drift, a CI step that can silently break. For a solo developer or a small team maintaining a project for years, removing that system removes an entire category of maintenance burden. Weblin is opinionated that this trade — slightly more manual CSS authoring, zero pipeline — is worth it for most projects that aren't building a large product with a dedicated frontend team.

Adaptive performance is opt-in, not baked in

Obs.js (device/connection detection) is a separate, optional pairing, not bundled. obs.css costs nothing if Obs.js is absent.

This follows the same logic as everything else: the framework's job is to do one thing (styling) well and compose cleanly with other single-purpose tools, rather than growing into a platform that tries to own performance, state, and routing all at once.

What this buys you, and what it costs

You get: a framework you can read start to finish in an afternoon, that never breaks on a Node upgrade, and that ages with the browser instead of against it.

You give up: the ecosystem gravity of Tailwind/React — fewer pre-built examples, and fewer Stack Overflow answers.