Styles — Overview

Design tokens for building new CSS in this codebase. We use Open Props — a set of CSS custom properties (sizes, colors, shadows, easing, and more; 500+ in total) composed directly in your own rules. No utility classes, no build step.

For shared component patterns, see Components.

How to load

Pages link to /ui/ui.css, which @imports the Open Props token layer, the utility-class layer and forms.css. Token sources live under public/open-props/*.css.

Show code <link rel="stylesheet" href="/ui/ui.css"> <link rel="stylesheet" href="/index.css">

/ui/ui.css provides the CSS that several components or plain markup share (.btn, .badge, cards, forms, status animations). CSS that only one component or decorator uses lives in its module and is injected ahead of the page's stylesheets when the component connects or the decorator first runs, so a page rule of equal specificity still overrides it. A page that never imports x-select.mjs never parses its styles.

/index.css defines the project's semantic aliases (--surface-*, --text-*, --brand, --border-color) and dark-mode variants.

Per-category pages

Page & viewport defaults

A few global rules apply to every page at the html / body level (defined in /index.css). They aren't tokens, but they set the defaults for every layout:

Exception — full-bleed app shells. A page that fills the viewport (body { height: 100dvh; overflow: hidden }) and never scrolls at the viewport should opt out with html { scrollbar-gutter: auto } in its page-level <style>, otherwise the reserved scrollbar space remains as an empty strip. Put the stable gutter on the page's actual document scroll region instead. studio.html does this — it keeps a full-width waveform edge-to-edge and reserves the gutter only on its document column.

Core principles