Theming guide

Brand an entity with one color.

One color fills the whole brand ramp, so buttons, focus rings, selection, tints, and dark mode all follow it. Set it in one CSS block or on DgaProvider.

The 12-step ramp from one color, --ddga-brand-foreground, and the contrast warning need @dev-dga/react, @dev-dga/css, and @dev-dga/tokens 1.6.0 or later.

Brand an entity in one CSS block

Set your entity color once and derive the 12 --ddga-brand-* steps from it with color-mix(). The color is step 600. Lighter steps mix it with white and darker steps with black, in the same proportions as the built-in palettes. No JavaScript runs.

/* app.css: after @dev-dga/css, outside any @layer */:root {  --entity: #0a5c8a;  --ddga-brand-25: color-mix(in oklab, var(--entity) 3%, white);  --ddga-brand-50: color-mix(in oklab, var(--entity) 6%, white);  --ddga-brand-100: color-mix(in oklab, var(--entity) 14%, white);  --ddga-brand-200: color-mix(in oklab, var(--entity) 28%, white);  --ddga-brand-300: color-mix(in oklab, var(--entity) 46%, white);  --ddga-brand-400: color-mix(in oklab, var(--entity) 64%, white);  --ddga-brand-500: color-mix(in oklab, var(--entity) 88%, white);  --ddga-brand-600: var(--entity);  --ddga-brand-700: color-mix(in oklab, var(--entity) 86%, black);  --ddga-brand-800: color-mix(in oklab, var(--entity) 74%, black);  --ddga-brand-900: color-mix(in oklab, var(--entity) 64%, black);  --ddga-brand-950: color-mix(in oklab, var(--entity) 48%, black);}

Put the block after the @dev-dga/css import and outside any @layer. The library declares its variables in the ddga-base layer, and unlayered styles win over layered ones. Don’t also pass theme to DgaProvider: its inline values win over the block.

Light mode: primary is step 600

Dark mode: primary is step 400

Which step each role reads

Components read role variables, never the steps themselves. Each role points at a step of the brand ramp, and dark mode moves it to a lighter step so it stays legible on dark surfaces.

RoleLight modeDark mode
--ddga-color-primary--ddga-brand-600--ddga-brand-400
--ddga-color-primary-hover--ddga-brand-700--ddga-brand-300
--ddga-color-primary-active--ddga-brand-900--ddga-brand-500
--ddga-color-ring--ddga-brand-600--ddga-brand-400
--ddga-color-primary-foreground--ddga-brand-foreground--ddga-gray-950

Without --ddga-brand-foreground, text on primary is white in light mode. The roles resolve at :root and on every element with data-theme. DgaProvider sets data-theme on its root, so dark mode reads the same ramp.

Check the text on primary buttons

In light mode, primary buttons show white text on your color. If white doesn’t reach 4.5:1 on it, as with a yellow or a light green, set --ddga-brand-foreground to dark text. Dark mode always uses gray-950 text on step 400.

:root {  --entity: #f5bd02;  /* … the 12 steps above … */  --ddga-brand-foreground: var(--ddga-gray-950); /* dark text on a light brand color */}

In development, DgaProvider warns when a theme color fails 4.5:1, but a CSS block never reaches the provider. Check your color in a contrast checker before you ship.

Set the color on DgaProvider

theme.primary takes the same color and builds the same 12 steps, or takes a built-in palette name. Use it when the color comes from data, such as a per-entity setting.

import { DgaProvider } from '@dev-dga/react';// A color string: step 600 of a 12-step ramp, the same mix as the CSS block<DgaProvider dir="rtl" locale="ar" theme={{ primary: '#0a5c8a' }}>  <App /></DgaProvider>// A built-in palette: saGreen, lavender, gold, info, success, warning, error, gray<DgaProvider theme={{ primary: 'lavender' }}>  <App /></DgaProvider>

For server rendering or a scoped area, buildTheme returns the same variables as a plain object. Spread it on an element that carries data-theme.

import { buildTheme } from '@dev-dga/tokens';const vars = buildTheme({ primary: '#0a5c8a' });// { '--ddga-brand-25': 'color-mix(in oklab, #0a5c8a 3%, white)', …, '--ddga-brand-600': '#0a5c8a', … }// Spread it on an element that carries data-theme; a plain <section> keeps the old brand.<div data-theme="light" style={vars}>…</div>

To tune tokens beyond the brand ramp, use the Theme studio

Upgrading from 1.5

Apps that already pass a color to theme.primary see these changes:

  • Tints, focus rings, selection, and dark mode follow your color instead of SA Green.
  • Hover is step 700 and pressed is step 900, slightly different from the old color-mix(in srgb) values.
  • Dark mode shows gray-950 text on primary fills instead of white, for palettes and custom colors.
  • In light mode, the warning and success palettes switch to gray-950 text.
  • buildTheme with a color returns only --ddga-brand-* values; spread it on an element with data-theme.
  • The explicit { base, hover, active, foreground } triplet is unchanged.