SalesOS

Design tokens

The SalesOS palette, accessible text colours, typography, radii and shadows for light and dark mode — as TypeScript constants, CSS custom properties and a Tailwind preset.

@salesos/design-tokens is the SalesOS design system as data. Use it so an app extension looks like the rest of SalesOS and keeps to the same WCAG 2.1 AA contrast.

npm install @salesos/design-tokens
import { light, dark, palettes, typography, radii, shadows, contrastRatio } from '@salesos/design-tokens';
@import '@salesos/design-tokens/tokens.css';
/* --salesos-* on :root; dark values under .dark, [data-theme="dark"],
   and [data-theme="system"] when the OS is dark */
.card { background: var(--salesos-surface); color: var(--salesos-text); border-radius: var(--salesos-radius-2xl); }
// tailwind.config.js
module.exports = { presets: [require('@salesos/design-tokens/tailwind')] };
// bg-salesos-surface, text-salesos-muted, text-salesos-accent-ink, rounded-salesos-2xl, shadow-salesos-card …

Inside an extension you don't need the stylesheet to follow the user's theme: the bridge sends the active mode and its --salesos-* values as context.theme, and again on every switch.

const apply = (ctx) => {
  for (const [k, v] of Object.entries(ctx.theme.tokens)) document.documentElement.style.setProperty(k, v);
  document.documentElement.dataset.theme = ctx.theme.mode;
};
apply(bridge.getContext());
bridge.onContextChange(apply);

Colours

TokenLightDarkUse
background#F2F1EA#141414Page background
surface#FFFFFF#1E1E1ECards
surfaceHover#F8F8F6#262626Hover, input backgrounds
surfaceMuted#F0EBD8#2E2B22Chart backgrounds, progress tracks
surfaceInverse#1A1A1A#303030Dark cards, primary buttons
text#1A1A1A#EDEDEDHeadings
textBody#666666#BDBDBDBody text
textMuted#6B6B6B#A6A6A6Captions, placeholders
accent#EAD07D#EAD07DFill only — highlights, active states
accentInk#7A5F14#EAD07DThe accent as text
successFill / successText#93C01F / #4F6B0A#93C01F / #9BCB2BProgress and tints / success text
dangerFill / dangerText#DC2626 / #B91C1C#DC2626 / #FCA5A5Solid error backgrounds / error text
disabledFill#4F4F4F#4F4F4FDisabled dark buttons (instead of opacity)

Contrast rules

The package's tests hold every documented pairing to 4.5:1 (3:1 only for text of 24 px, or 18.66 px bold); CONTRAST_PAIRS lists them.

  1. The accent is a fill, not an ink. Gold text on white is 1.5:1. When the accent has to be text on a light surface, use accentInk. On a dark surface the accent itself reads well.
  2. Light hues stay light on dark surfaces: successFill, accent and white at 60% are the legible choices on surfaceInverse; the darkened inks fail there.
  3. Don't dim a card to mean "inactive" — opacity composites every child against the page and drops muted text under 4.5:1. Use a muted background and say "Inactive" in words.
contrastRatio(light.accentInk, light.surface); // 6.04

Typography, radii, shadows

  • Fonts: Plus Jakarta Sans for headings, Inter for body (typography.fontFamily).
  • Radii (px): cards 2xl 32 / xl 24, inputs md 12, buttons and pills full.
  • Shadows: sm for cards with a hairline border, card and soft for raised panels.

On this page