CORE

Styling

There is no styling solution inside AmoJS and none is coming. class and style are attributes like any other, so dynamic ones are just attribute holes; stylesheets are files that amo build copies verbatim; scoping is a platform feature now.

class and style are attributes

A hole must be the whole attribute value, so build the string in a function hole.

JS
html`<div class="${() => (open.value ? 'panel open' : 'panel')}">…</div>`
html`<div style="${() => `--progress: ${pct.value}%`}">…</div>`
html`<button class="${kind}">…</button>`        // a signal holding 'primary' | 'ghost'

Plain CSS files

Every non-module file under src/ — css, images, fonts — is copied to the same path in dist/; src/public/ goes to the root. Link the stylesheet from your page or layout. In amo dev a css edit hot-swaps without reloading the page or resetting island state.

Scoping with @scope

CSS @scope (Baseline 2024) scopes a block to a subtree, with an optional lower boundary — what CSS-in-JS and SFC scoped styles used to emulate.

CSS
/* src/styles/card.css — copied to dist as-is */
@scope (.card) to (.card-body) {
  :scope { border: 1px solid var(--line); border-radius: 6px; }
  h2 { margin: 0; font-size: 16px; }       /* only h2s between .card and .card-body */
}

Theming

Custom properties on :root, overridden by a data-theme attribute and by prefers-color-scheme. This site does exactly that — the token file is the only place a hex value is allowed.

CSS
:root { --brand: #6553E8; --line: #E5E7E1; }
[data-theme="dark"] { --brand: #8B7BFF; --line: #1E2027; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --brand: #8B7BFF; } }

Why nothing is built in

Scoped styles need either a compiler that rewrites selectors or a runtime that generates class names — the first breaks the "touch nothing but templates" rule the compiler lives by, the second costs bytes on a 2 KB budget and cannot be ejected as code you would have written. The platform's answer is good enough, and it is already in every browser.