Manual Style

Style

elements man style Read as markdown

The Elements design system, shipped as @elements/style. A set of CSS files that give an Elements app a complete look: semantic design tokens layered over OKLCH color ramps, bare HTML elements that style themselves, a small family of component classes, atomic utilities, dark mode, and a monochrome default that brands with a single accent override.

@elements/style is CSS only: you import CSS and write HTML with classes. To make a component interactive, such as a tab that switches panels or a menu that opens, toggle its is- state classes from the event handlers and directives in your Elements HTML template. The package carries no JavaScript so that behavior lives in one place, the template, next to the state it reads; there is nothing to import into a .ts file and no runtime or build step of its own. Everything documented here is styling you get from importing CSS and applying classes.

This page is the quick reference. Every section links to a chapter that covers it in full.

Getting Started

A scaffolded app is already wired: app/shared/styles/page.css imports the package and every page's style.css imports that baseline. That chain is what puts the package in a page's bundle. A page's CSS is every stylesheet reachable by import from its <html> template, the template's own imports then each child template's imports, depth first. A rule written in another page's style.css is not in that set, so markup copied from one page renders unstyled on the next until the rule moves somewhere both of them import. See html/templates.

In an existing app, install and import it once:

Terminal
elements install @elements/style
app/shared/styles/page.css
@import "@elements/style";

That one import loads the tokens, the reset, base element styling, typography, and every component and utility. From there you write HTML:

Example
<main class="page-shell"> <div class="stack is-lg"> <h1>Account</h1> <p class="lede">Manage your profile and billing.</p> <div class="card"> <div class="card-head"> <h2>Profile</h2> </div> <div class="card-body"> <form class="is-grid"> <div class="field"> <label>First name</label> <input> </div> <div class="field"> <label>Last name</label> <input> </div> <div class="field is-full"> <label>Email</label> <input type="email"> <p class="hint">We'll never share it.</p> </div> </form> </div> <div class="card-foot"> <button class="is-ghost">Cancel</button> <button class="is-primary">Save</button> </div> </div> </div> </main>

See install for the subpath export map and how a scaffolded app is wired.

The Naming Rule

One rule runs through every class name in the system:

Bare nouns are things. is- adjectives are how they're rendered.

  • Elements and components are bare nouns, the thing itself: <button>, <input>, .card, .callout, .pill, .field, .tab, .terminal.

  • Component parts carry the family prefix, a part named after its parent: .card-head, .card-body, .callout-title, .pill-dot, .tab-list, .tab-panel.

  • Modifiers and state use is-, an adjective describing how the noun renders, meaning the same thing everywhere: is-primary, is-ghost, is-danger, is-sm, is-lg, is-block, is-active, is-error, is-ok, is-loading, is-disabled.

  • Atomic utilities are axis-prefixed and never take is-: .text-sm, .bg-soft, .font-mono, .gap-4, .hidden.

Each is- modifier owns one axis, so they combine freely and order never matters:

Example
<button class="is-primary is-lg is-block">Sign up</button> <input class="is-sm is-error"> <div class="callout is-warning">Heads up</div>