Tokens
Every visual value in the component library is a token, and every token is a CSS custom-property reference with a neutral fallback:
import { tokens } from "@nimblebrain/synapse/ui";
tokens.bg; // "var(--color-background-primary, #ffffff)"tokens.accent; // "var(--color-text-accent, #2563eb)"Components never hold a hex value. They style with these var() references, so
the pixels are decided by whatever fills the custom properties.
How the host fills them
Section titled “How the host fills them”Under the MCP ext-apps protocol, the host sends its CSS variables as
hostContext.styles.variables, and the SDK applies them to your app’s :root.
Every var(--token, …) in the library re-resolves against them:
:root { --color-background-primary: #0b0b0f; --color-text-primary: #f4f4f5; --font-sans: "Your Brand Sans", system-ui, sans-serif; --border-radius-md: 0.5rem;}styles.variables is a closed enum. A NimbleBrain host sends the mode-varying
variables it has no key for (--color-text-accent and the processing pair) in
hostContext["ai.nimblebrain/styles"].variables, and the SDK applies them the
same way; see Theming.
Because resolution happens in CSS, a theme change (including a light-to-dark
flip) is just the host swapping those variables. Every var() re-resolves and
the UI repaints with no React re-render.
Standalone, the library still renders. With no host to inject anything, color
tokens resolve through the synapse-defaults cascade layer, so they stay
theme-correct in dark as well as light — a var() fallback is a static literal and
cannot do that. That layer is installed when a theme is applied, so it covers a
connection with no host behind it (the inline adapter, or a host that ships no
tokens) but not a render that never connects at all, such as a Storybook story or a
unit test; there, and for everything the layer never covers — fonts, radii, shadows —
tokens fall back to their second argument: system fonts and plain geometry. The
library stays usable and unbranded rather than blank. See
Theming for the full resolution order.
Surfaces
Section titled “Surfaces”Background layers, lightest to most recessed.
| Token | CSS variable | Neutral fallback |
|---|---|---|
bg |
--color-background-primary |
#ffffff |
bgRaised |
--color-background-secondary |
#fafafa |
bgSubtle |
--color-background-tertiary |
#f3f4f6 |
Foreground tones plus the accent pair.
| Token | CSS variable | Neutral fallback |
|---|---|---|
fg |
--color-text-primary |
#111827 |
fgMuted |
--color-text-secondary |
#6b7280 |
fgFaint |
--color-text-tertiary |
#9ca3af |
accent |
--color-text-accent |
#2563eb |
accentFg |
--color-text-inverse |
#ffffff |
accentFg is the foreground that sits on top of an accent fill: the label
color for a primary button, for instance. The spec names no “on-accent” colour,
so it reads inverse text, the text for a surface of the opposite polarity.
Border and ring
Section titled “Border and ring”| Token | CSS variable | Neutral fallback |
|---|---|---|
border |
--color-border-primary |
#e5e7eb |
borderStrong |
--color-border-secondary |
#d1d5db |
ring |
--color-ring-primary |
#2563eb |
ring is the focus-ring color; borderWidth (below) sets its width.
Status semantics
Section titled “Status semantics”Semantic colors for state. Each …Light pairs a saturated hue with a tinted
background for badges, banners, and status rows.
| Token | CSS variable | Neutral fallback |
|---|---|---|
danger |
--color-text-danger |
#dc2626 |
dangerFg |
--color-text-inverse |
#ffffff |
success |
--color-text-success |
#059669 |
warning |
--color-text-warning |
#f59e0b |
processing |
--nb-color-processing |
#7c3aed |
processingLight |
--nb-color-processing-light |
#f3eeff |
infoLight |
--color-background-info |
#eef4ff |
The spec has no processing hue, so processing and processingLight read the
kit’s own variables. A NimbleBrain host sends them in its ai.nimblebrain/styles
host-context field; on other hosts the neutral defaults fill them, and an app can
set them on its own :root.
Fonts and weights
Section titled “Fonts and weights”Three faces and four weights. The face tokens only name a font — the library ships no font files, so these resolve to the web-safe fallbacks below unless the host supplies the faces. See Host fonts.
| Token | CSS variable | Neutral fallback |
|---|---|---|
fontSans |
--font-sans |
system-ui, -apple-system, BlinkMacSystemFont, sans-serif |
fontMono |
--font-mono |
ui-monospace, SFMono-Regular, Menlo, monospace |
fontHeading |
--nb-font-heading, then --font-sans |
system-ui, -apple-system, BlinkMacSystemFont, sans-serif |
weightNormal |
--font-weight-normal |
400 |
weightMedium |
--font-weight-medium |
500 |
weightSemibold |
--font-weight-semibold |
600 |
weightBold |
--font-weight-bold |
700 |
The spec names one family, --font-sans, and headings follow it on any host.
Set --nb-font-heading to give headings a family of their own.
Type scale
Section titled “Type scale”Every level is a size and a paired line-height. Body text has four sizes,
consumed by Text; headings have three, consumed by
Heading. The helpers textStyle(size) and
headingStyle(size) return the matching fontSize / lineHeight pair.
Text sizes
Section titled “Text sizes”| Size | Size variable | Size fallback | Line-height variable | Line-height fallback |
|---|---|---|---|---|
xs |
--font-text-xs-size |
0.75rem |
--font-text-xs-line-height |
1rem |
sm |
--font-text-sm-size |
0.875rem |
--font-text-sm-line-height |
1.25rem |
base |
--font-text-base-size |
1rem |
--font-text-base-line-height |
1.5rem |
lg |
--font-text-lg-size |
1.125rem |
--font-text-lg-line-height |
1.75rem |
Heading sizes
Section titled “Heading sizes”| Size | Size variable | Size fallback | Line-height variable | Line-height fallback |
|---|---|---|---|---|
sm |
--font-heading-sm-size |
1.25rem |
--font-heading-sm-line-height |
1.75rem |
md |
--font-heading-md-size |
1.5rem |
--font-heading-md-line-height |
2rem |
lg |
--font-heading-lg-size |
2rem |
--font-heading-lg-line-height |
2.5rem |
| Token | CSS variable | Neutral fallback |
|---|---|---|
gutter |
--nb-gutter |
1.5rem, or 1rem in a frame 640px or narrower |
The inset between the frame’s edge and content. The kit’s layouts draw it; see the gutter.
Corner rounding, extra-small to extra-large.
| Token | CSS variable | Neutral fallback |
|---|---|---|
radiusXs |
--border-radius-xs |
0.25rem |
radiusSm |
--border-radius-sm |
0.5rem |
radiusMd |
--border-radius-md |
0.75rem |
radiusLg |
--border-radius-lg |
1rem |
radiusXl |
--border-radius-xl |
1.5rem |
Border width
Section titled “Border width”| Token | CSS variable | Neutral fallback |
|---|---|---|
borderWidth |
--border-width-regular |
1px |
Shadows
Section titled “Shadows”Elevation from a flat hairline outline up to a lifted overlay.
| Token | CSS variable | Neutral fallback |
|---|---|---|
shadowHairline |
--shadow-hairline |
0 0 0 1px rgba(0,0,0,0.06) |
shadowSm |
--shadow-sm |
0 1px 2px rgba(0,0,0,0.05) |
shadowMd |
--shadow-md |
0 4px 6px -1px rgba(0,0,0,0.1) |
shadowLg |
--shadow-lg |
0 10px 15px -3px rgba(0,0,0,0.1) |