Colour
Every chromatic token comes in two registers. The plain token is safe as text on any surface. The -pop token is a fill only, always with its accent-ink label and always inside its outline. No token is ever asked to do both jobs, because a saturated colour vivid enough to be a good fill is exactly where text fails WCAG AA.
Surfaces
In Light: cream page, paper card, white lifted object, deeper cream recess. In Night the same four, in indigo, in the same order. The steps between them are deliberately small in both: an object is separated from the page by its outline and its shadow, never by being a paler shade.
--color-canvas#fdf4dd#15162aThe page
--color-surface#fffdf7#1a1b31A card, a panel
--color-elevated#ffffff#1f2038A node, a dialog, a popover
--color-sunken#f6e9cb#101122An input well, a log list
Ink and line
Text, the outline every object wears, and the hard shadow under it. In Light they are one near-black; in Night text, outline and shadow are cream, because a dark line on a dark page is invisible. All three text steps clear WCAG AA on all four surfaces in both themes — including the faintest, which was not true of the Chapter 1 dark palette.
--color-ink#131722#f8f1e2Body text, and the focus ring
--color-muted#525768#b9bcd1Captions, help text, log lines
--color-faint#63687a#a6a9bfPlaceholders, footnotes, digests
--color-line#131722#f8f1e2The outline every object wears, at 2px. Its own name since Phase 27, so the outline can differ from the text
--color-shade#131722#f8f1e2The hard offset shadow. Every shadow-* utility draws in it
--color-scrim#131722#030309Behind a modal, at 25–35%. Darkens the page in both themes
Accent
Grape. The text register is the default — it is what a link and a label use. The pop fill is what a primary button uses, and its label is always accent-ink, the same near-black in both themes.
--color-accent#7f3dc4#d0b4fdLinks, selected labels, the traversed edge
--color-accent-pop#b57cff#a567f0The primary button, the selected tab
--color-accent-ink#131722#131722The label on any pop fill. Never cream, in either theme
--color-spark#02e9e9#139696The second gradient hue, and the run sweep. Never text
Status
The engine's four step outcomes. Status is never carried by colour alone — every use pairs the tone with a word, an icon or a position.
--color-ok#007044#1fdb8cSucceeded
--color-ok-pop#00f199#109a61A success badge or toast
--color-live#00698f#67ccfdRunning
--color-live-pop#5fcbff#1190c1A running badge, the live edge
--color-warn#7b5d00#f6b61bSkipped, or a validation warning
--color-warn-pop#ffd166#ad7f0cA warning panel's marker
--color-bad#c21725#feaba4Failed
--color-bad-pop#ff726b#ef4c4bA destructive button, an error marker
Node categories
Trigger, logic and integration deliberately share a hue with ok, live and warn: a trigger is the “go” of a graph, and one hue meaning one thing is worth more than five more colours. Shape, icon and label carry the distinction.
--color-cat-trigger#007044#1fdb8cTrigger nodes
--color-cat-trigger-pop#00f199#109a61A trigger node's colour band
--color-cat-agent#a82795#fea3eaAgent and LLM nodes
--color-cat-agent-pop#fc73e3#d750c0An agent node's colour band
--color-cat-logic#00698f#67ccfdBranch, loop, delay
--color-cat-logic-pop#5fcbff#1190c1A logic node's colour band
--color-cat-transform#006d6d#1dd6d6Set, assert, log
--color-cat-transform-pop#02e9e9#139696A transform node's colour band
--color-cat-integration#7b5d00#f6b61bHTTP, Discord, Sheets, Gmail
--color-cat-integration-pop#ffd166#ad7f0cAn integration node's colour band
Themes
Light is the default and the reference; Toybox Night and System are choices a reader makes, kept in their own browser. Until Phase 27 one near-black did four jobs. On a cream page it can; on an indigo one it cannot — text must turn light, a label on a bright fill must stay dark, and an outline and a shadow must still show against the page. So each job has its own name.
| Role | Light | Toybox Night |
|---|---|---|
inkBody text, and the focus ring | #131722 | #f8f1e2 |
accent-inkThe label on every pop fill | #131722 | #131722 |
lineThe outline every object wears | #131722 | #f8f1e2 |
shadeThe hard shadow under it | #131722 | #f8f1e2 |
The fills are the one place the numbers decided the design. In Night a single outline must clear 3:1 against both the indigo page and every fill, which leaves the fills a band between “a dark label reads on it” and “a cream outline shows around it” — so Night's fills are a rich mid-tone rather than Light's near-pastels, and its text tones are the bright half instead. The two registers still sit twice apart, the other way round.
Contrast, measured
Every tone in the text register against every surface, graded against the 4.5:1 bar for normal-sized text — not the 3:1 large-text allowance, because the smallest tokens in this system carry 10 and 11px captions. These are the figures the build gate asserts.
| Token | canvas | surface | elevated | sunken |
|---|---|---|---|---|
ink | 16.36 AAA | 17.59 AAA | 17.94 AAA | 14.86 AAA |
muted | 6.52 AA | 7.01 AAA | 7.15 AAA | 5.92 AA |
faint | 5.04 AA | 5.42 AA | 5.52 AA | 4.58 AA |
accent | 5.68 AA | 6.11 AA | 6.23 AA | 5.16 AA |
ok | 5.63 AA | 6.05 AA | 6.17 AA | 5.11 AA |
live | 5.59 AA | 6.01 AA | 6.13 AA | 5.08 AA |
warn | 5.63 AA | 6.06 AA | 6.18 AA | 5.12 AA |
bad | 5.58 AA | 6.00 AA | 6.12 AA | 5.07 AA |
cat-trigger | 5.63 AA | 6.05 AA | 6.17 AA | 5.11 AA |
cat-agent | 5.63 AA | 6.05 AA | 6.17 AA | 5.11 AA |
cat-logic | 5.59 AA | 6.01 AA | 6.13 AA | 5.08 AA |
cat-transform | 5.60 AA | 6.02 AA | 6.14 AA | 5.08 AA |
cat-integration | 5.63 AA | 6.06 AA | 6.18 AA | 5.12 AA |
| Token | canvas | surface | elevated | sunken |
|---|---|---|---|---|
ink | 15.86 AAA | 15.06 AAA | 14.15 AAA | 16.66 AAA |
muted | 9.49 AAA | 9.01 AAA | 8.47 AAA | 9.97 AAA |
faint | 7.69 AAA | 7.30 AAA | 6.85 AA | 8.08 AAA |
accent | 9.84 AAA | 9.35 AAA | 8.78 AAA | 10.34 AAA |
ok | 9.83 AAA | 9.33 AAA | 8.77 AAA | 10.33 AAA |
live | 9.85 AAA | 9.35 AAA | 8.78 AAA | 10.35 AAA |
warn | 9.85 AAA | 9.35 AAA | 8.79 AAA | 10.35 AAA |
bad | 9.82 AAA | 9.33 AAA | 8.76 AAA | 10.32 AAA |
cat-trigger | 9.83 AAA | 9.33 AAA | 8.77 AAA | 10.33 AAA |
cat-agent | 9.85 AAA | 9.35 AAA | 8.78 AAA | 10.35 AAA |
cat-logic | 9.85 AAA | 9.35 AAA | 8.78 AAA | 10.35 AAA |
cat-transform | 9.84 AAA | 9.34 AAA | 8.78 AAA | 10.34 AAA |
cat-integration | 9.85 AAA | 9.35 AAA | 8.79 AAA | 10.35 AAA |
Why the focus ring is ink, not the accent
WCAG 2.2 asks a focus indicator for 3:1 against what surrounds it. The accent fill is 2.63:1 against the cream page — under the bar. Ink is 16.36:1 there, and never below 6.23:1 on any fill in the system. So one ink ring is legal everywhere, and there is exactly one focus ring in the product.
In Toybox Night the ring is still ink — which is cream here. It is 15.86:1 against the indigo page and never below 3.22:1 on any fill, because the fills were fitted so that one ring would still clear WCAG's 3:1 on every one of them. The same single ring, in both themes.
Type
Geist and Geist Mono, self-hosted, variable axis. Three steps below Tailwind's smallest, because a node card, a status badge and a log line are all denser than body text. Mono is load-bearing rather than decorative: node ids, {{ }} references, cron expressions and JSON output are all monospaced.
text-3xs10pxDescribe the automationStatus badges, handle labelstext-2xs11pxDescribe the automationCaptions, log lines, help texttext-xs12pxDescribe the automationDense secondary texttext-ui13pxDescribe the automationForm controls and buttonstext-sm14pxDescribe the automationBody texttext-base16pxDescribe the automationA card or dialog headingtext-xl20pxDescribe the automationA section headingtext-3xl30pxDescribe the automationA page headingShape and elevation
A hard offset shadow, down-right, no blur and no spread — ink in Light, cream in Night, drawn in the shade token either way. Blur would read as a drop shadow; the hard edge is what makes an object read as a solid thing sitting on the page. The build gate refuses a shadow with a blur radius in either theme.
2pxshadow-flatA checkbox, a switch — something small and fixed
3pxshadow-cardA button, a card, a badge
4pxshadow-nodeA node card, a dialog, a toast
5pxshadow-liftThe hover state of anything with shadow-card
6pxshadow-drawerA panel that slides over the page
1pxshadow-pressThe pressed state — the object has moved into the page
Corner radius
rounded-smrounded-mdrounded-lgrounded-xlrounded-2xlrounded-3xlPrimitives
The real components, not pictures of them. Every outlined object here presses into the page when clicked — it travels down-right by exactly the shadow offset it loses, so the shadow's far corner stays put and the object appears to move. Try them with a keyboard: every one is operable and shows a visible ink focus ring.
Toggles
A region of the page — a settings section, a list item. Paper fill.
An object on the page — a node, a popover, a dialog. White fill, deeper shadow.
A category header strip is the one place a pop fill covers a wide area — and where a node card carries its category.
A notice is anchored to the thing it is about and stays until that changes; a toast reports the result of something the user just did and clears itself. Both take their fill, their glyph and — importantly — their WORD from one table, so the same failure never announces itself two different ways.
Success: Key verified against the provider and stored, encrypted.
Warning: Built and saved — but no node can do these parts yet
- Read the attachment on the incoming email
- Wait for a human to approve it
Error: The model returned a graph that does not validate
- Edge e2 leaves an output handle that core.branch does not declare
Note: This workflow has no trigger yet, so it cannot run.
The failure carries role="alert" and a single short shake. Deliberately small: it says “look here”, not “your work is gone”.
Dialog
<dialog>: focus is trapped, Escape closes, the background is inert, and there is no z-index anywhere.Toasts
A failure is role="alert" and stays up until dismissed; everything else is polite and clears itself. The live region is rendered always, empty — a region created together with its first message announces nothing, which is the commonest way a toast system is silently broken.
Menu and tooltip
Motion
Eight named states, and nothing outside the list. Two curves: one that settles, one that overshoots. Everything here is decoration over a state that is also stated in text, which is what makes the blanket prefers-reduced-motion rule safe.
press120msAn object moves down-right into the page and its shadow shrinks. Every button, every tab.
hover-lift120msThe same object rises up-left and its shadow grows.
enter380msSomething arriving: up, with a small overshoot.
appear200msSomething appearing in place: a dialog, a tooltip, a menu.
success380msA settled overshoot. The toy landing in its slot.
failure420msA tight, short shake. “Look here”, not “your work is gone”.
waiting1.25s loopThree dots bobbing in sequence. The agent is thinking.
idle3.4s loopThe mascot's bob. Decoration, and the only infinite motion that is not a status.
The two shadow states — press and hover-lift — have no keyframes, because they are transitions on an interaction. They are in the Primitives section above, where they can be felt rather than watched.
Illustration and character
Sparky is a forge sprite with three moods, and the third one is the careful one: a mascot must not read as flippant when someone's workflow has just failed. Concerned is attentive rather than sad — level mouth, inner brow ends raised, no tears — it is rendered small in error contexts, and it never replaces the error text.
The thinking indicator
Used while an agent node is reasoning. The state is announced as a sentence; a bobbing dot is not information.
No workflows yet
Describe what you want in plain language and AgentForge will build the first one.
This canvas is empty
Drag a node in from the palette, or let an agent lay the whole graph out for you.
Nothing has run yet
Run this workflow once and every step's status and logs will stream in here.
Something went wrong
The page could not finish loading. Reloading usually fixes it, and nothing you saved was lost.
Accessibility
The constraints the system is built inside, rather than a list of things checked afterwards.
One focus ring, and it is ink
Near-black in Light, cream in Night. 2.5px, offset 3px, keyboard only. The offset leaves a gap of page colour between an object's own outline and the ring, which is what stops the two reading as one thicker border. No control anywhere sets outline-none.
Never colour alone
Every status pairs its tone with a word, an icon or a position. A pressed tab is darker AND moved. A failed toast is red AND says “Error” to a screen reader AND shakes.
An object is never drawn without its outline
Some fills sit as little as 1.3:1 off the cream page; in Night it is the cards that sit flat on the indigo. Either way the outline carries the separation, not the lightness — the build gate asserts both halves of that, in both themes.
Reduced motion is honoured, and the press is not motion
prefers-reduced-motion collapses every duration to zero. The 2px press displacement stays: that request asks for less animation, not for a control that stops responding.
Native elements where they are better
The select is a real <select>, the dialog a real <dialog>, the switch a real checkbox. Typeahead, focus trapping, Escape, the inert background and a phone's own picker all come free and are all commonly broken when hand-rolled.
The quiet register exists
A cartoon look earns goodwill on a landing page and gets in the way in a settings form. Form controls keep the outline and the radius and drop the shadow and the squish — a field is a hole in the page, not an object on it.