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-accentis the focus ring of every interactive control.ring-controloutlines an unchecked checkbox.ring-groundcuts 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-emphasismarks 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-palettepicks the palette anddata-theme(light,darkorsystem) picks the scheme. Both work on any element, usually<html>. Withoutdata-palette, the base colours on this page apply. - Tokens resolve with
light-dark(), sosystemfollows the OS without a script. - Palettes live in
packages/deft/src/libs/palettes/palettes.ts.palettes.gen.cssis a file snapshot of them: after changing a value, runbun run test --project @openperf/deft -uto regenerate it. The test fails while the two disagree.