Skip to content

Colors ​

Ooko exposes a theme-aware color palette through CSS custom properties. Components select a color family through their typed color prop and consume semantic shades instead of raw color values.

src/styles/tokens/primitives.scss is the single source of truth for palette values.

Color families ​

ColorIntended use
primaryPrimary brand actions and emphasis.
secondarySecondary brand accents and supporting emphasis.
infoInformational feedback and neutral notices.
successSuccessful actions and positive feedback.
warningCautionary feedback requiring attention.
dangerDestructive actions, errors, and critical states.
neutralInk: text, borders, and default actions.

neutral is a slate ink scale whose pale shades carry a faint blue tint. Its 800 shade (#163144) is the default text-strong and action color in light mode; the dark theme reverses the scale, so the same semantic shade becomes the pale end (#e3e8ed). OButton uses it by default, and primary stays the accent for focus, links, and selection.

Each family exposes primitive shades from 50 to 900:

css
color: var(--o-color-primary-700);
background: var(--o-color-success-100);

Prefer semantic tokens in components and application UI. Use primitive tokens only when no semantic role describes the intended use.

Halo ​

--o-halo-1 to --o-halo-4 are the four stops of the ambient backdrop: mint, cream, lavender, and sky in light mode, and deep versions of the same full-page wash in dark mode. Put the o-halo utility class on the app shell (or body) to paint it, and lay glass surfaces on top:

html
<body class="o-halo"></body>

Glass ​

Surfaces and controls are glass by default, so a halo shows through them:

TokenUse
--o-background-glass-strongCards, popups, dialogs, drawers, and form controls with text
--o-background-glassThe lighter glass variants, for sparse panels
--o-border-glassThe light hairline edge of a glass surface
--o-backdrop-filter-glassThe blur behind a glass surface

Without backdrop-filter, browsers show the translucent fill without blur.

Borders stay discreet and translucent, so they pick up the surface behind them: --o-border-color-control (form controls, triggers, checkboxes) and --o-border-color-subtle (dividers, popup and table edges). High contrast turns both back into the solid --o-border-neutral-default.

Themes ​

Light mode maps 50 to the lightest palette value and 900 to the darkest. Dark mode reverses that mapping, so semantic shade names preserve their relative emphasis without component-level theme overrides.

Apply a theme with data-ooko-theme:

html
<section data-ooko-theme="dark">
  <!-- Ooko components -->
</section>

data-ooko-theme="light" explicitly selects the light palette. Without an attribute, Ooko uses the browser or operating-system preference through prefers-color-scheme. Use useOokoTheme from ooko-ui/theme when an application needs a persistent override; see Theme.

Semantic shades ​

Semantic shades replace numeric palette indexes with names that communicate relative visual emphasis.

Semantic shadePrimitive shadeRelative emphasis
minimal50Lowest
subtle100Very low
lighter200Low
light300Soft
default400Default
dark500Moderate
darker600Strong
deep700Very strong
deeper800High contrast
deepest900Highest contrast

Every color family exposes background, border, and text semantic tokens:

css
.notice {
  background: var(--o-background-info-subtle);
  border-color: var(--o-border-info-default);
  color: var(--o-text-info-deep);
}

The available patterns are:

text
--o-background-{color}-{semantic-shade}
--o-border-{color}-{semantic-shade}
--o-text-{color}-{semantic-shade}

--o-background-overlay is a translucent black surface in both themes. It is intended for blocking overlays and deliberately does not reverse with the palette.

Overriding the palette ​

Load a plain unlayered CSS file after Ooko's stylesheet. Define the replacement palette once, then map it in normal order for light mode and reverse order for dark mode:

css
:root {
  --app-primary-50: #f0fdfa;
  --app-primary-100: #ccfbf1;
  --app-primary-200: #99f6e4;
  --app-primary-300: #5eead4;
  --app-primary-400: #2dd4bf;
  --app-primary-500: #14b8a6;
  --app-primary-600: #0d9488;
  --app-primary-700: #0f766e;
  --app-primary-800: #115e59;
  --app-primary-900: #134e4a;
}

:root,
[data-ooko-theme="light"] {
  --o-color-primary-50: var(--app-primary-50);
  --o-color-primary-100: var(--app-primary-100);
  --o-color-primary-200: var(--app-primary-200);
  --o-color-primary-300: var(--app-primary-300);
  --o-color-primary-400: var(--app-primary-400);
  --o-color-primary-500: var(--app-primary-500);
  --o-color-primary-600: var(--app-primary-600);
  --o-color-primary-700: var(--app-primary-700);
  --o-color-primary-800: var(--app-primary-800);
  --o-color-primary-900: var(--app-primary-900);
}

[data-ooko-theme="dark"] {
  --o-color-primary-50: var(--app-primary-900);
  --o-color-primary-100: var(--app-primary-800);
  --o-color-primary-200: var(--app-primary-700);
  --o-color-primary-300: var(--app-primary-600);
  --o-color-primary-400: var(--app-primary-500);
  --o-color-primary-500: var(--app-primary-400);
  --o-color-primary-600: var(--app-primary-300);
  --o-color-primary-700: var(--app-primary-200);
  --o-color-primary-800: var(--app-primary-100);
  --o-color-primary-900: var(--app-primary-50);
}

The --app-* properties belong to the consumer and may use any prefix. Override the complete 50–900 scale to keep primitive and semantic relationships coherent. Keep the reversed dark mapping so semantic shades retain the same theme behavior as the built-in palette. Adapt the theme selectors to a container when only part of an application should use the custom palette.

Consumer overrides must use the public --o-* contract. Do not override internal component color channels or copy component selectors solely to change a palette family.