Skip to content
OpenPerf
Esc
↑↓navigate↵open⌘Jpreview
On this page

Colors

Neutral greys carry the interface. Cobalt marks the one thing to act on; amber and red mark state.

Token names read as property, role, level: bg-control-hover, fg-muted, accent-emphasis. In code use the utility: bg-* for surfaces, text-* for text and icons. Swatches show light and dark; text tokens show their contrast on the surface they sit on.

Surfaces

import { ColorTokens } from "../../src/ColorTokens";
import { SURFACE_TOKENS } from "../../src/constants";

export default function SurfacesColors() {
  return <ColorTokens tokens={SURFACE_TOKENS} />;
}

Text and icons

import { ColorTokens } from "../../src/ColorTokens";
import { TEXT_TOKENS } from "../../src/constants";

export default function TextColors() {
  return <ColorTokens tokens={TEXT_TOKENS} />;
}

Accent

import { ColorTokens } from "../../src/ColorTokens";
import { ACCENT_TOKENS } from "../../src/constants";

export default function AccentColors() {
  return <ColorTokens tokens={ACCENT_TOKENS} />;
}
  • If two things on screen are blue, one of them is wrong.
  • Links and the New badge use text-accent, a darker shade that holds 4.5:1 on light grounds.

State

import { ColorTokens } from "../../src/ColorTokens";
import { STATE_TOKENS } from "../../src/constants";

export default function StateColors() {
  return <ColorTokens tokens={STATE_TOKENS} />;
}
  • Amber for things that need attention, red for things that are broken. Green only marks a resolved status: fine is the default and needs no colour.
  • State colours mark icons, badges and numbers, never whole rows or panels.

Rings

  • ring-accent is the focus ring of every interactive control.
  • ring-control outlines an unchecked checkbox.
  • ring-ground cuts a marker or a stacked avatar out of what it overlaps.

Charts

import { ColorTokens } from "../../src/ColorTokens";
import { CHART_TOKENS } from "../../src/constants";

export default function ChartColors() {
  return <ColorTokens tokens={CHART_TOKENS} />;
}
  • Grey carries the data. accent-emphasis marks the recent 20% of the story series; warning, danger and critical mark state.
  • Text in legends and tooltips is never drawn in the series colour.

Palettes

import { paletteIds } from "@openperf/deft";

const SAMPLES = paletteIds.flatMap((id) =>
  (["light", "dark"] as const).map((theme) => ({ id, theme })),
);

const SWATCHES = ["bg-inset", "accent-emphasis", "chart-ramp-2", "chart-ramp-3", "chart-ramp-4"];

export default function Palettes() {
  return (
    <div className="grid grid-cols-4 gap-3 font-sans text-body-md">
      {SAMPLES.map((sample) => (
        <div
          className="flex flex-col gap-2 rounded-lg bg-default p-3 text-default shadow-raised"
          data-palette={sample.id}
          data-theme={sample.theme}
          key={`${sample.id}-${sample.theme}`}
        >
          <div className="flex items-baseline justify-between gap-2">
            <code className="font-medium">{sample.id}</code>
            <span className="text-body-sm text-muted">{sample.theme}</span>
          </div>
          <div className="flex gap-1">
            {SWATCHES.map((name) => (
              <span
                className="h-4 flex-1 rounded-xs"
                key={name}
                style={{ background: `var(--${name})` }}
              />
            ))}
          </div>
          <span className="w-fit rounded-sm bg-accent px-2 py-0.5 text-body-sm font-medium text-on-accent">
            Resolve
          </span>
        </div>
      ))}
    </div>
  );
}
  • A palette rewrites every colour token, with a light and a dark scheme. data-palette picks the palette and data-theme (light, dark or system) picks the scheme. Both work on any element, usually <html>. Without data-palette, the base colours on this page apply.
  • Tokens resolve with light-dark(), so system follows the OS without a script.
  • Palettes live in packages/deft/src/libs/palettes/palettes.ts. palettes.gen.css is a file snapshot of them: after changing a value, run bun run test --project @openperf/deft -u to regenerate it. The test fails while the two disagree.

Was this page helpful?