Colours are named by their job. Pick the role that matches what you mean, and the right colour follows, in light and dark mode.
Mostly warm greys, one deep blue for actions, and a small set of colours that each mean one thing. The blue is deliberately not COHO's brand pink, so an app never looks like part of COHO's own navigation.
Why not use my brand colours?
Inside COHO, managers rely on colour to mean the same thing everywhere: blue is an action, red is a problem. Your brand can appear in your logo and illustrations, but not in buttons, links or status.
How tokens are named
Every colour token follows one pattern: property, then role, then emphasis. --at-bg-danger-subtle is a background, for danger, in the subtle strength. Once you know the pattern you can guess any token, and so can an AI tool.
| Part | Options | Example |
|---|---|---|
| Property | text, bg, border, surface, shadow |
--at-text-... |
| Role | primary, neutral, info, success, warning, danger, discovery, inverse, accent-<hue> |
--at-text-danger |
| Emphasis | subtle or bold (left out for the usual strength) |
--at-bg-danger-subtle |
Roles
| Role | Use for | Never for |
|---|---|---|
| primary | The main action: primary buttons, links, the selected tab | Status or decoration |
| neutral | Everything without a meaning: text, borders, secondary buttons, table headers | |
| info | Neutral news, or something in progress | Errors or success |
| success | Done and fine: paid, saved, connected | Anything still to do |
| warning | Needs attention soon, nothing has failed | Errors |
| danger | Errors, overdue, and actions that delete or disconnect | Emphasis or branding |
| discovery | Something new: onboarding, a new feature, AI-drafted content | Status |
| inverse | Text on bold backgrounds and primary buttons | Text on the page |
| accent | Categories with no meaning, such as property types or charts | Anything with a meaning: use a status role |
The accent test
If you could swap one accent colour for another and the screen would mean exactly the same, use an accent. If the colour says something (good, bad, new), use a role instead.
Text
Text
--at-text
Body text and headings
--at-text-subtle
Hints, secondary text, table headers
--at-text-primary
Links and link buttons
--at-text-info
Information text
--at-text-success
Paid, done, connected
--at-text-warning
Due soon, needs attention
--at-text-danger
Errors, overdue, destructive actions
--at-text-discovery
New things, onboarding, AI
Backgrounds
Neutral and primary
--at-bg-primary
Primary buttons
--at-bg-neutral
Table headers, skeletons, muted cards
--at-bg-neutral-pressed
Secondary button pressed
Subtle: alerts, tags, highlights
--at-bg-info-subtle
Information alert and tag
--at-bg-success-subtle
Success alert and tag
--at-bg-warning-subtle
Warning alert and tag
--at-bg-danger-subtle
Error alert and tag, danger button hover
--at-bg-discovery-subtle
Discovery alert and tag, AI content
Bold: the one thing that most needs attention (pair with --at-text-inverse)
--at-bg-info-bold
Bold information tag
--at-bg-success-bold
Bold success tag
--at-bg-warning-bold
Bold warning tag
--at-bg-danger-bold
Bold danger tag (Overdue, Failed)
--at-bg-discovery-bold
Bold discovery tag (New)
Borders
Borders
--at-border
Borders and dividers
--at-border-bold
Hovered inputs
--at-border-focus
Focus outline
--at-border-danger
Error fields, danger buttons, error alert edge
Accent hues
Six hues with no meaning, each with a background and a text token: --at-bg-accent-<hue> and --at-text-accent-<hue>. Use them through the accent tags or for chart series.
Example
Example
Rules
- Pick tokens by meaning, never by the colour you want. A green you like is not a reason to use
success. - Use bold for one thing per view at most, such as an Overdue tag in a list of otherwise subtle tags.
- Every text and background pair here passes WCAG AA (4.5:1) in light and dark mode. Mixing roles (danger text on a success background) is not checked: do not do it.
Dark mode
Every colour token has a dark value, applied inside [data-color-mode="dark"]. If your app only uses the tokens, dark mode works with no changes. Use the Light / Dark switch on any example to see it.