
# Theme a React app from one brand colour

By Danylo Pravda, 2026-10-08

Theming usually means a long list of variables: a primary colour, its hover, its pressed state, a focus ring, a text
colour that stays readable on it, chart colours that match. Miss one and a button turns unreadable in light mode.
Beamline derives all of them from one value, and keeps the rest of the look (type, corners, surfaces, motion) in a
separate choice. This guide shows the three choices and what each one changes.

## Choice one: the brand colour

```tsx
import { UIRoot } from "@beamline/runtime";
import { prism } from "@beamline/look-prism";

<UIRoot preset={prism} theme="dark" brand="oklch(0.7 0.15 150)">
  {children}
</UIRoot>
```

From that one colour come the accent, accent text, hover and pressed states, focus rings, selection tints, glows and
the first chart series. Text on a brand fill (a main button, a selected day, a checked box) picks its own ink from the
brand's lightness, so any brand keeps at least 4.5:1 contrast without a second colour. In plain CSS it is one
custom property, `--pui-brand`.

Write it in OKLCH and keep the lightness between 0.55 and 0.75: then the brand also reads as text on both themes.
Six ready accents swap only the brand (`data-accent="neutral|violet|green|amber|orange|rose"`).

## Choice two: light or dark

Dark is the default and the primary design target; `theme="light"` switches. Both themes define every token, so no
component ever branches on the theme, and a region can switch on its own with a `UIRoot` inside another.
[Theme switch](https://beamline.io/components/theme-switch) is a ready control for it.

## Choice three: the look

Every part has a core (size, layout, states, keyboard, motion) and a look on top: the fonts, icon weight, corner radii,
the page behind everything and the paint of every surface. Changing the look on the root restyles the whole app.
There are nine:

| look | what it is |
|---|---|
| Prism (default) | cut crystal in a dark room: parts drawn by their cut edges, the main action white light, colour only where attention is |
| Soft | clean surfaces with a trace of your brand's hue, hairline rims, short shadows |
| Blueprint | the drafting table for dense tools: cyan wireframes, square corners, mono labels |
| Glass | a liquid lens: clear glass with bright rims over a faint grid |
| Metal | black anodised metal with diamond-cut edges, wide headings, monospaced figures |
| Glow | a calm night with light in one place: spectral light under the main buttons |
| Pop | a sticker sheet: a warm page, ink outlines, a hard lip under every control |
| Mono | monochrome: your brand colour appears only in the data it leads |
| Ink | a pen-and-ink drawing in two inks |

A look never changes the size scale (control heights, spacing), so switching it never reflows a layout beyond text
following its fonts. You can set one for the app, one region, or a single part:

```tsx
import { glass } from "@beamline/look-glass";

<UIRoot preset={glass} theme="light">…</UIRoot>         // a region, its menus and dialogs included
<Button variant="accent" look="glow">Upgrade</Button>  // one button
```

Every look is live in the [component library](https://beamline.io/components/foundations/looks): switch it there and every part on the
page follows.

## Smaller changes

For the rest, change a token or a default instead of a component:

```tsx
<UIRoot
  tokens={{ "--pui-radius-control": "12px" }}
  defaults={{ button: { size: "sm" }, table: { density: "compact" } }}
>
  {children}
</UIRoot>
```

- **Tokens** cover corner radii, surfaces, the three ink levels, chart series, motion timing and type faces; the
  [customise docs](https://beamline.io/docs/customize) list them. Trading direction colours (`--pui-up`, `--pui-down`) are separate from
  success and danger, so a market where red means up can swap them.
- **Defaults** set a component's design props for a whole region (a smaller button, a compact table); a prop on one
  part still wins.
- **Your own parts** can wear the same paint with the look's role classes (`m-control`, `m-surface`, `m-field` and the
  rest), so a custom widget matches whichever look is on.

## What stays out of the theme

Nothing in a theme changes behaviour: keyboard models, focus order, data handling and motion rules belong to the core,
and reduced motion is honoured in every look. Changing the brand or the look is safe to do late, even the day before a
launch.

## Ask your agent for it

With Beamline connected ([setup for your agent](https://beamline.io/connect)): "use our brand colour #2F66D6 and the Soft look, light
theme". The agent sets the brand and look on the root once and leaves every component alone. The full set of options
is in [Customise](https://beamline.io/docs/customize) and the [design language](https://beamline.io/docs/design-language).
