REFERENCE
Everything the framework exposes, in one place. The seven chapters before this one argue; this one only lists.
HOW TO READ IT
This chapter is the index, not the argument. Every entry links back to the chapter that explains it, and nothing here is a summary of the reasoning — if a row makes you ask why, the chapter it points at is where the answer lives.
It exists because a guide made of specimens answers "how do I do X" very well and "does this thing even have Y" very badly. Both are real questions. Seven chapters of prose could not answer the second one without being read end to end, and a framework whose surface can only be discovered by reading it is a framework people re-implement pieces of.
Tokens are split by who sets them, which is the single most useful distinction in the list and the one the source files do not make on their face. A token you set is part of the API: override it and the framework follows. A token the framework sets is plumbing — it is derived from the palette or from another token, it is rewritten whenever the display changes, and setting it yourself means fighting the code that owns it. Both are documented, because you may well need to read a derived value; only one of them is a place to write.
Nothing here is versioned separately from the framework. If a name appears in this table it is in the build you are looking at, and anything that has been taken away is in DEPRECATED instead.
EVERY CLASS
All of them, grouped the way src/ is. A class marked
needs a child will not render on its own — the companion node is listed, and
EFFECTS shows the assembled frame.
LAYOUT AND UTILITY · src/base/layout.css
| CLASS | WHAT IT DOES | NOTES |
|---|---|---|
| .ac-screen | The full-height frame the simulations paint on. | Once per page. 100svh, overflow: clip. |
| .ac-screen__body | The padded content well inside the frame. | Override with --ac-screen-pad. |
| .ac-stack | Flex column, gap from --ac-gap. | Default gap --ac-space-4. |
| .ac-row | Flex row, gap from --ac-gap. | — |
| .ac-row--wrap | Lets the row wrap. | — |
| .ac-row--center | align-items: center. | — |
| .ac-row--baseline | align-items: baseline. | A big number beside its unit. |
| .ac-row--between | justify-content: space-between. | Justifies every gap — see .ac-spacer. |
| .ac-row--end | justify-content: flex-end. | — |
| .ac-grid | Grid of --ac-cols equal tracks. | Default gap --ac-space-6. |
| .ac-grid--console | The canonical split: operate left, instrument right. | Stacks below 900px. No --ac-cols. |
| .ac-col-2 -3 -4 -6 | Column spans for a grid child. | All reset to auto below 900px. No -5. |
| .ac-grow | Absorbs leftover space, with min-width: 0. | Needed on a row of .ac-btn--pad. |
| .ac-spacer | Empty element that eats the slack at one point. | Use instead of --between in a row of three. |
| .ac-push | margin-top: auto — drop to the bottom. | The one margin in the system. Flex column only. |
| .ac-sr-only | Available to assistive tech, invisible on the panel. | For a <legend> the panel title already states. |
| .ac-root | Scoped reset, for embedding in a page you do not own. | src/base/reset-scoped.css. Use instead of the global reset. |
All of these are demonstrated in TYPE & GEOMETRY · LAYOUT.
CONTROLS · src/components/
| CLASS | WHAT IT DOES | MODIFIERS |
|---|---|---|
| .ac-btn | The key. Outline by default. | --filled (inverse, one per region) --dim --sm --lg --pad --block |
| .ac-tabs | Soft-key strip. Needs role="tablist". | Children: .ac-tab, .ac-tab--active |
| .ac-toggle | Two-position switch with a mandatory ON/OFF word. | --on (state) --input (CSS-only, no JS) · parts __track __thumb __state |
| .ac-check | Status bit. A set bit fills solid; no check glyph. | --disabled |
| .ac-radio | Exclusive mode selection. | --disabled |
| .ac-field | Label plus control wrapper. | --block --invalid · parts __label |
| .ac-input | The recessed well itself. | --block |
| .ac-select | Wrapper that draws the well around a <select>. | --block |
| .ac-keypad | Numeric pad for gloved hands. | --dense (mouse only) · keys .ac-keypad__key, __key--fn (CLR / ENT, read as commands), __key--wide (spans two columns) |
| .ac-dialog | Native <dialog>, flat backdrop, no fade. | --wide · parts __title __body __actions |
| .ac-nav | The menu bar. Goes inside the frame. | --sticky · parts __mark __links __link __meta · the current link inverts on [aria-current="page"], with __link--active as the class equivalent where you cannot write the aria state |
| .ac-setup | The permanently-open control board. | parts __grid __foot |
Live specimens in CONTROLS.
DISPLAY · src/components/
| CLASS | WHAT IT DOES | MODIFIERS |
|---|---|---|
| .ac-panel | A framed region with a legend chip in its border. | --dim (recedes) --bar (inverse title) · parts __title (the chip in the border), __barTitle (the full-width strip --bar draws instead — the one camel-cased name in the framework) |
| .ac-statusbar | Inverse strip — the machine's own voice. | --line (a data row rather than a statement) |
| .ac-readout | Labelled instrument value. | --lg --inline · parts __label (__label--plain drops the inverse-video strip) __value __unit |
| .ac-list | Keyed log with leader dots. A <dl>. | --dense --dim --bright · parts __item __key __fill __value |
| .ac-table | The data grid. | --zebra --dense · parts __num __row--active |
| .ac-table-scroll | Horizontal scroll container for a wide table. | The answer to a table that will not fit a phone. |
| .ac-meter | Stepped bargraph, never a smooth fill. | --lg --dim --alarm · parts __track __bar __scale |
| .ac-badge | Micro tag in the pixel face. | --filled --dim |
| .ac-banner | The loudest element. One per screen. | --filled --dim |
| .ac-spinner | A rotating character, not a circle. | --lg --dim |
| .ac-hr | The only divider, at the 2px stroke. | --bright |
| .ac-cursor | Block cursor after the text. | — |
| .ac-sweep | The PPI radar sweep. Pure CSS. | part __beam (the rotating wedge; the sweep draws nothing without it) — see PERSISTENCE. |
Live specimens in DISPLAY.
SIMULATIONS AND EFFECTS · src/sim/, src/base/blink.css
| CLASS | WHAT IT DOES | NEEDS A CHILD |
|---|---|---|
| .ac-bloom | The plasma panel: hotter glow, a breathing bleed, the cell mesh. | .ac-mesh |
| .ac-crt | The tube: scanline texture, vignette, 11s drift, 5.5s hum, the retrace band. | .ac-retrace |
| .ac-afterglow | Persistence. Rides with .ac-crt on the board. | .ac-persist |
| .ac-scanlines | The static, motion-free texture. For cards, thumbnails and print. | no |
| .ac-mesh | The cell mesh and its shimmer. Companion to bloom. | — |
| .ac-retrace | The sweeping band. Companion to CRT; switchable, and omittable. | — |
| .ac-persist | The layer ghosts and the smear are mounted into. Companion to afterglow. | — |
| .ac-ghost | One decaying copy of a rewritten value. Mounted and removed by effects.js. | — |
| .ac-ghost--fast | The same, on the short tail, where a value rewrites often. | — |
| .ac-blink | Law-2 emphasis. steps(1), 1.1s, never a fade. | — |
All three simulations compose in CSS; the board refuses to run plasma and CRT together, which is a rule of the demo and not of the framework. EFFECTS and PERSISTENCE have the arguments.
CORNER STYLE · src/components/classic.css
| CLASS | WHAT IT DOES | SCOPE |
|---|---|---|
| .ac-classic | Cut corners, extruded key edge, label top-left. The default look. | One region — including back inside a rounded one. |
| .ac-rounded | The way back to the arc. | One region. |
The page-wide version of the same choice is
data-ac-style-classic below. Where corner-shape is unsupported the
corners quantize to a hard 2px instead of cutting, so both positions mean something in every
browser. CONTROLS shows both at once.
EVERY ATTRIBUTE
Two kinds, and they are not interchangeable. State attributes
go on <html> and are read by the CSS — write them yourself and the framework
follows, with or without the JavaScript. Hooks are how the optional module
finds controls; they do nothing on their own.
STATE · on the root element, read by CSS
| ATTRIBUTE | VALUES | WHAT IT SELECTS | DEFAULT |
|---|---|---|---|
| data-ac-tech | plasma crt | What is making light at all. | plasma |
| data-ac-emitter | neon helium argon krypton · p1 p3 p4 p7 p11 p31 p39 | Which gas or phosphor inside that technology. The palette. | neon |
| data-ac-engine | css css+js | Whether the effects module may contribute. | css+js |
| data-ac-style-blink | on off | Law-2 blink, everywhere at once. | on |
| data-ac-style-classic | on off | Cut corners and extruded key edges, page-wide. | on |
| data-ac-style-retrace | on off | The CRT retrace band. | on |
The style flags are always written explicitly, both ways, and that is deliberate: CSS selecting on the absence of an attribute cannot tell "the author has not chosen" apart from "the author chose off", and the defaults above are the only place that distinction is allowed to live. An emitter is only meaningful inside its technology, which is why naming a palette takes two attributes and not one — see COLOR.
HOOKS · read by amber-console.js, inert without it
| ATTRIBUTE | GOES ON | WHAT IT WIRES |
|---|---|---|
| data-ac="tabs" | An .ac-tabs container | The arrow-key tablist model and roving focus. |
| data-ac="toggle" | An .ac-toggle button | Click-to-flip, keeping aria-pressed and the ON/OFF word in sync. |
| data-ac-screen | The frame | Names the element the simulations mount on. Falls back to the first .ac-screen. |
| data-ac-sim | A toggle | "plasma" or "crt" — mounts the classes and the companion child. |
| data-ac-style | A toggle | The flag name: "blink", "classic", "retrace". |
| data-ac-style-note | Any element | Revealed while the named style flag can do nothing. |
| data-ac-engine | A button | The engine switch. On the root it is state, above; on a button it is the control. |
| data-ac-engine-note -off / -on | Any element | The three states of that switch: cannot / could but off / contributing. |
| data-ac-display | A radio | One catalog row. Carries -tech, -emitter, -sims, -peak. |
| data-ac-display-out | Any element | A readout: tech emitter peak mode engine label. |
| data-ac-display-info | Any element | Prose for one technology or one exact tech/emitter pair. |
| data-ac-display-reset | A button | Reset to Preset — simulations, styles and engine together. |
| data-ac-dialog-open | A button | The id of the <dialog> to show. |
| data-ac-dialog-close | A button inside the dialog | Closes the dialog it is in. Takes no value. Only needed where the button is not in a <form method="dialog">, which closes on its own. |
| data-ac-scrolling | The frame | Written by effects.js while the page moves. Read it; do not set it. |
The catalog is markup, not a table
inside the JavaScript. Ship your own [data-ac-display] radios with your
own palettes and the module needs no edit — which is the same reason the technology prose is
one [data-ac-display-info] node per key rather than strings escaped through a
script. SCREEN & BOARD documents the assembled
board.
Element hooks are bound once, on
load. Both optional modules bind themselves at DOMContentLoaded, so a
page that ships its markup in the HTML calls nothing at all. The exception is markup inserted
afterwards: a [data-ac="tabs"] strip or a [data-ac="toggle"]
your own code rendered is not wired, and the fix is one call —
AmberConsole.init(el) over the new subtree, or AmberConsole.init()
for the whole document. It keeps a set of what it has already wired, so calling it twice on
the same element cannot double-bind a listener.
Two hooks need nothing either way.
The dialog pair is delegated from the document, so a
<dialog> appended at runtime opens and closes with no call. And the
persistence module watches the whole tree with a MutationObserver — ghosting and
smear find new nodes on their own, which is why AmberConsoleEffects.init() is
called for you and is not something you normally touch.
THE THREE FUNCTIONS · everything either module exports
| CALL | WHEN YOU NEED IT |
|---|---|
| AmberConsole.init(el?) | Wire markup that arrived after load. Both modules bind themselves at DOMContentLoaded, so a static page never calls this. Idempotent — it remembers what it has already wired. |
| AmberConsoleEffects.afterglow(el) | Leave a decaying copy of an element behind. Call it before you remove the element — a detached node has no rectangle, so there is nowhere to pin the ghost. Text rewrites ghost themselves; this is for a node about to stop existing. |
| AmberConsoleEffects.transition(fn) | Run a change and let the screen it replaces decay per pixel, rather than element by element. Wrap discrete swaps only — a tab switch, a dialog, a panel replacement. |
AmberConsoleEffects.transition(() => { // falls back to a plain call
panel.replaceChildren(nextScreen); // when the API, the simulation or the
}); // engine flag is unavailable
Only discrete changes, and the
reason is mechanical rather than aesthetic. A new view transition cancels the
one already running, so wrapping a per-second text tick would mean P7's three-second snapshot
being thrown away sixty times a second and never completing once — strictly worse than not
doing it. Frequent text is what the ghost mechanism is for; ghosts compose rather than
cancelling. transition() is always safe to call: it declines and simply runs your
function when the API is missing, motion is reduced, the tab is hidden, the persistence
simulation is off, or the page asked for the CSS-only engine.
EVERY TOKEN
Every custom property in the framework begins --ac-. That is not
tidiness: dist/amber-console.layer.css exists so the framework can be embedded in
an app that owns the page, and cascade layers do not protect custom properties
— an unlayered :root { --radius: 12px } in the host beats a layered one. A host
that happened to use --gap or --cols silently re-cornered every
control in the framework. stylelint enforces the prefix now, so the next token
cannot go bare.
COLOR · you set these
| TOKEN | WHAT IT IS |
|---|---|
| --ac-ink | Body text. The 7.0:1 stop. |
| --ac-ink-bright | The live value. 10.5:1. |
| --ac-ink-dim | Secondary drive. 5.2:1 — a cell driven at 70%, not an off state. |
| --ac-ink-faint | Furniture and disabled text. 3.4:1 — below the text floor, which is why it is furniture. |
| --ac-ink-trace | The faintest mark the panel can make — leader dots, unlit scale ticks. 1.63:1, non-text only. |
| --ac-screen | The glass. One of three warm blacks. |
| --ac-screen-well | A recessed surface: inputs, selects. |
| --ac-screen-raised | A surface standing above the glass. |
| --ac-fill | Inverse video: filled keys, status strips. |
| --ac-fill-bright | The hot fill, for a latched or alarmed surface. |
| --ac-on-fill | Text drawn on a fill. |
| --ac-stroke | Every border in the system. |
| --ac-stroke-dim | The receded border. |
| --ac-emit-100 -90 -70 -50 -30 | The emission ramp the five ink levels are solved from. |
| --ac-halo-1 -2 -3 -4 | The scatter ramp — light spread in the glass, as raw r, g, b triples. |
Eleven palettes set all of these, and every stop in every one of them is solved to the same contrast ratio — so what changes between displays is the hue, never the brightness. COLOR shows the derivation. The halo tokens are triples rather than colors so they can be given an alpha at the point of use.
TYPE, SPACE AND GEOMETRY · you set these
| TOKEN | WHAT IT IS |
|---|---|
| --ac-font-terminal | The body face. A bitmap terminal font. |
| --ac-font-micro | The 8px pixel face, for badges and micro labels. |
| --ac-type-display -title -body -small -micro | The five sizes. 18px is the floor for anything anybody reads. |
| --ac-tracking-display -body -micro | Letter-spacing per size band. |
| --ac-leading | Line height. One number for the whole system. |
| --ac-space-1 -2 -3 -4 -6 -8 -12 | The scale, in 4px half-cells: 4 8 12 16 24 32 48. |
| --ac-border-w | 2px. There are no hairlines on this panel. |
| --ac-radius | Keys, panels, dialogs. 2px 4px 8px 4px by default — the four corners are cut to different depths, TL TR BR BL. 8px under .ac-rounded. |
| --ac-radius-sm | Wells and switch housings. 1px 2px 4px 2px — the same diagonal at half depth. 4px under .ac-rounded. |
| --ac-gap | Per element, not on the root: the gap of one row, stack or grid. |
| --ac-cols | Per element: the track count of one .ac-grid. |
| --ac-screen-pad | Per element: the padding of one .ac-screen__body. |
| --ac-meter-value | Per element: 0–100 on an .ac-meter__track. |
| --ac-panel-title-bg | Per element: what the legend chip masks itself against. |
| --ac-backdrop | Per element: what an open .ac-dialog paints behind itself. Defaults to flat 72% black — keep it opaque enough to read as a powered-down panel, and do not blur it. |
| --ac-sweep-size | Per element: the maximum diameter of an .ac-sweep face. Defaults to 260px; the radar demo overrides it. |
| --ac-sweep-period | Per element: seconds per revolution. Defaults to the phosphor's own tail × 1.5, floored at 2.5s — so a long phosphor comes round about as its trail finishes draining, and neon does not strobe. |
| --ac-sweep-arc | Per element: how many degrees of wake trail the beam. Defaults to 250°, which is what the period expression evaluates to. Lower it for an emitter below the persistence floor, or the wake is a thin spinning line. |
| --ac-sweep-line | Per element: the width of the writing beam itself, 3px by default. A constant width at every radius, because a CRT spot is. |
Every row marked per element
is set on your
element rather than on :root — they are per-instance dials, and there is no global
value to override. Two of them, --ac-gap and --ac-cols, are for that
reason the only tokens in the 1.x rename that could not be bridged with an alias: there was no
framework-level value to alias from, so they have to be renamed where you write them.
See DEPRECATED.
GLOW, MOTION AND PERSISTENCE · mostly the framework's
| TOKEN | WHO SETS IT | WHAT IT IS |
|---|---|---|
| --ac-glow-text | you | The halo on a lit value. Never scales with drive — radius is a property of the glass. |
| --ac-glow-text-dim | you | The halo that belongs to --ac-ink-dim. |
| --ac-glow-text-faint | you | The halo that belongs to --ac-ink-faint. |
| --ac-glow-box | you | The halo on a filled or focused surface. |
| --ac-glow-box-dim | you | Its receded twin. |
| --ac-halo-spread | framework | How far the glow reaches, per palette. The only one of these three that is a radius — it multiplies the blur distance, and it is the square root of the scatter below. |
| --ac-halo-scatter | framework | How much light reaches the halo, per palette. An alpha multiplier, not a distance — neon is 1.00 by definition and every other emitter is stated against it. Radius never scales with drive; only alpha does. |
| --ac-halo-flash | framework | An r, g, b triple, not a number: the colour of a two-coating phosphor's fast layer. Declared by P7 alone, and the reason its sweep leads in blue over a yellow-green wake. |
| --ac-persist | framework | The working tail, per palette — capped for use in transitions. |
| --ac-persist-tail | framework | The uncapped tail. 35µs on P11, 3s on P7. What the engine gate reads. |
| --ac-persist-fast | framework | Documentation only — no CSS reads it. The decay of a two-coating phosphor's fast layer (P7, 0.04ms). It records where the hardcoded 4% crossover in ac-ghost-cascade comes from; keyframe offsets cannot read a custom property, so the figure has to be written twice and this is the half that says why. Not the ghost duration — that is .ac-ghost--fast, which is min(--ac-persist-tail, 400ms). |
| --ac-decay --ac-decay-fast --ac-decay-ease | framework | The decay durations and curve. Derived from the tail. |
| --ac-flicker | framework | 0–1, per phosphor. Scales the mains hum and the retrace band, from one number. |
| --ac-drive-dim --ac-drive-faint | framework | The drive fractions the dim and faint tiers are, arithmetically. |
| --ac-mesh-pitch --ac-mesh-wire | framework | The cell mesh: spacing, and the darkness of a barrier rib. The rib figure is a contrast budget. |
| --ac-corner | framework | bevel or round. What the classic flag actually flips. |
| --ac-edge --ac-edge-press | framework | The key's extruded edge, and the same key pressed IN. |
| --ac-edge-3d --ac-edge-3d-in | framework | The classic values those two point at. |
| --ac-lit | framework | 0 or 1 — whether a control is currently lit. A registered @property, which is the whole point: registering it is what makes a custom property animatable, so the afterimage's alpha and radius decay together off this one number instead of needing a box-shadow transition per state. |
| --ac-blink-anim --ac-blink-name --ac-blink-ease | framework | The blink, assembled per palette. |
| --ac-blink-force --ac-blink-force-ease | framework | The root-level override, read first at every blink site. How one rule stops every blink at once. |
| --ac-ghost-anim | framework | The ghost's decay animation. |
| --ac-smear | framework | 0–1, how fast the page is currently scrolling. Written onto the frame by effects.js every frame and read by the blur below. Read it; do not set it. |
| --ac-smear-filter | framework | The scroll smear. Set to none by reduced-motion and by print. |
| --ac-afterimage-display | framework | Switches the static afterimage halo off wholesale. |
The "framework" rows are still
worth knowing — several are the single point where an entire class of effect is
switched off, which is exactly how prefers-reduced-motion and the print stylesheet
do their jobs in a handful of lines rather than a selector list. Read them freely; write them
only if you have read the file that owns them, because the palette rewrites most of these
whenever the display changes.
EVERY STORED KEY
The optional module remembers what the board was set to. All of it is
localStorage, all of it is prefixed ac.sim., and every write is
wrapped — localStorage throws in private mode and in sandboxed
file:// frames, and a panel that cannot persist must still work.
| KEY | VALUES | WRITTEN BY |
|---|---|---|
| ac.sim.tech | plasma crt | Picking a catalog row. |
| ac.sim.emitter | The emitter name | Picking a catalog row. |
| ac.sim.plasma | 1 0 | The PLASMA switch, and any preset that moves it. |
| ac.sim.crt | 1 0 | The CRT switch, and any preset that moves it. |
| ac.sim.engine | 1 0 | The JS Effects switch. Cleared by Reset. |
| ac.sim.style.blink | 1 0 | A blink switch, if the page ships one. Cleared by Reset. |
| ac.sim.style.classic | 1 0 | The Classic Buttons switch. Cleared by Reset. |
| ac.sim.style.retrace | 1 0 | The Retrace Band switch. Cleared by Reset. |
| ac.sim.mod | 1 0 | Whether the board has been moved off its preset. |
localStorage already answers
that question and a second source of truth for it would be a thing to keep in sync.EVERY ENVIRONMENT
The framework renders in four environments it does not control, and it has a considered answer in each. You do not switch any of these on. They are in the stylesheet, they respond to the reader's own settings, and the reason they are listed here rather than argued in a chapter is that the correct thing to know about them is that there is nothing to do.
| ENVIRONMENT | WHAT CHANGES | FILE |
|---|---|---|
| forced-colors (high contrast) |
The whole design is a colour trick, so this needs a real fallback rather than a broken
one. Every simulation overlay is removed; every border is restated in
CanvasText, because the borders are the layout; and every
inverse-video surface is restated in Highlight / HighlightText,
because "the machine is speaking" is information and must not flatten into an outline.
Blink survives — it carries the alarm — but returns to a hard edge. |
src/base/a11y.css |
| @media print | The palette inverts to black on white by re-pointing the semantic tokens, so no
component needs a print rule of its own. The 2px rules stay because they are the layout;
glow, bloom, scanlines and every animation go, because they describe energized hardware
and paper is not. Filled surfaces carry print-color-adjust: exact, without
which a browser silently drops them and a set interlock bit prints as an empty square. |
src/base/print.css |
| prefers-reduced-motion | All three simulations stop. The cell mesh is the one overlay that keeps rendering — it is the shape of the panel rather than an effect playing over it, and only its buzz is motion. Blink stops, the smear stops, ghosts are hidden outright, and every decay transition is blanketed. | src/sim/frame.css |
| No vendored fonts | A build swap rather than a media query: replace the tokens/fonts.css import
with tokens/fonts-cdn.css and rebuild, and the five vendored
woff2 files are dropped in favour of the same two faces from a CDN. Same
tokens, same stack, one fewer thing to host. |
src/tokens/fonts-cdn.css |
Two of the four are gated rather
than trusted. npm run contrast runs the full pair table against all eleven
palettes independently, so a ratio that passes under one emitter and fails under another is a
build failure; and the visual suite captures a forced-colors and a print case for every demo
page. The one thing the suite cannot see is a missing
print-color-adjust — Playwright renders print to a screen surface, which honours
backgrounds regardless — so that list is checked by hand against a real print preview.
There is also a scoped embed
for putting the framework inside an app that owns the page: swap
base/reset.css for base/reset-scoped.css, wrap your markup in
.ac-root, and use dist/amber-console.layer.css so the framework loses
specificity fights against your own rules. The palette attributes may go on
<html> or on the .ac-root wrapper — both resolve correctly — but
do not split them across the two.