Colors
Six OKLCH ramps sharing one lightness/chroma spine — only the hue changes. Primary 257.4°, danger 25°, warning 85°, success 145°, info 210°; neutral is the primary hue with chroma flattened.
primary
--color-primary-*
50
100
200
300
400
500
600
700
800
900
950
danger
--color-danger-*
50
100
200
300
400
500
600
700
800
900
950
warning
--color-warning-*
50
100
200
300
400
500
600
700
800
900
950
success
--color-success-*
50
100
200
300
400
500
600
700
800
900
950
info
--color-info-*
50
100
200
300
400
500
600
700
800
900
950
neutral
--color-neutral-*
50
100
200
300
400
500
600
700
800
900
950
Categorical palette
off-spine by designTwelve hues whose only job is to be told apart. They are not semantic: a semantic colour means something (danger is bad), a categorical colour only says "not the same as that one". Tags, scheduler job categories and the per-module sidebar accent draw from here.
This is the one documented exception to the spine invariant. Each hue is pulled to the spine's 500-level weight and then gamut-mapped, so lightness drifts 0.551–0.560 and chroma varies by hue. Yellow and amber cannot hold C 0.196 at L 0.55 on any real display — forcing them onto the nominal spine would push them out of gamut and let the browser clip them somewhere unpredictable. Every 500 step carries ≥ 4.3:1 against white; the closest pair is ΔE2000 7.7 apart.
cat-red
cat-orange
cat-amber
cat-yellow
cat-lime
cat-green
cat-teal
cat-cyan
cat-blue
cat-indigo
cat-purple
cat-magenta
The .cat-* classes are tint carriers, not components —
each only sets --cat-tint and
--cat-ink. Put one on a chip, a calendar event or a
legend swatch. The chip recipe (100 tint background, 500 text) is structurally the legacy
statusBox, so status chips migrate almost invisibly.
Module accents
--color-module-accent
The legacy 41px icon rail is tinted per module, so "which module am I in" is answered by colour
before it is answered by a label — users who live in two or three modules navigate by that tint.
Set data-bms-module on any ancestor and everything beneath
it that reads the token picks up the accent. Default is neutral, so an app that sets nothing is
unchanged.
crm
cat-red
sales
cat-orange
billing
cat-amber
service
cat-green
stock
cat-blue
debtors
cat-purple
admin
neutral
Semantic tokens
flip with dark mode--color-canvas
--color-surface
--color-surface-sunken
--color-border
--color-border-strong
--color-accent
--color-accent-hover
--color-accent-subtle
--color-text
--color-text-muted
--color-text-subtle
--color-focus-ring
Components reference these, never the ramps directly. Toggle dark mode in the topbar and watch this page re-skin itself without a single component change.