Colors
How the Daybridge color system works, from first principles
Why OKLCH?
Most digital color systems use RGB or HSL, but these have a fundamental problem: they don't match how humans actually see color. In HSL, a yellow and a blue at the same "lightness" value will look completely different to your eyes — the yellow appears far brighter. This makes it impossible to build a consistent color palette where every hue feels equally prominent.
OKLCH solves this. It's a perceptually uniform color space, meaning equal numerical differences correspond to equal perceived differences. It separates color into three independent axes:
- L (Lightness) — how bright or dark a color appears, from 0% (black) to 100% (white)
- C (Chroma) — how vivid or saturated a color is, from 0 (gray) upward
- H (Hue) — the color itself, as a degree on the color wheel (0–360)
Because these axes are independent, you can change a color's hue without affecting its perceived brightness, or adjust brightness without shifting the hue.
This matters for Daybridge because users can pick any color for their calendar events. With OKLCH, a red event and a blue event at the same shade will look equally prominent — same brightness, same vibrancy, just a different color. No event accidentally stands out or fades into the background because of its hue.
Luminance
The first step in building a color palette is choosing a set of lightness values — the L axis. We call these luminance stops, and they form the backbone of every color in the system. Whether a color is gray, purple, or green, it uses the same set of luminance values. This guarantees visual consistency across the entire palette.
Why not just space them evenly?
In a UI, most of the visual real estate lives at the extremes. In light mode, backgrounds, cards, and surfaces are all very light — they need to be subtly different from each other, but they're all clustered near white. In dark mode, the same is true near black. The middle of the range handles text and accents, where you don't need as many fine distinctions.
If you space luminance stops evenly, you waste stops in the middle where you don't need them and don't have enough at the extremes where you do. Instead, we compress stops at both ends of the scale so there are many subtle shades near white and near black, with a more relaxed, linear spacing through the mid-range.
How we generate the curve
We split the luminance range into three zones:
- Light zone — shades near white, compressed together using a gamma curve so that light mode has plenty of subtle surface variations
- Mid zone — shades in the middle, spaced linearly
- Dark zone — shades near black, compressed together so that dark mode has the same level of surface subtlety
Both light and dark mode share this single palette. Light mode's surfaces draw from the top of the scale, dark mode's surfaces draw from the bottom, and the mid-range serves both modes for text, accents, and decorative elements.
Note that we don't extend all the way to pure black (0%). At the dark end, going too close to black makes the UI feel oppressively dark — shades become hard to distinguish and text becomes difficult to read.
Daybridge luminance values
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 100.0 | 99.9 | 99.0 | 96.8 | 92.3 | 85.0 | 76.0 | 67.0 | 58.0 | 49.0 | 40.0 | 30.0 | 21.9 | 15.9 | 13.0 |
Chroma
With luminance values chosen, the next question is: how vivid should each shade be?
In OKLCH, chroma controls saturation — a chroma of 0 is a neutral gray, and higher values are more vivid. But there's a catch: not every combination of lightness, chroma, and hue can be displayed on a screen. Very light and very dark colors can only support low chroma before they fall outside the screen's color gamut. And at any given lightness, some hues can go much more vivid than others — a bright yellow can be far more saturated than a bright blue at the same lightness.
Uniform chroma
We want every hue to look equally vivid at a given shade. To achieve this, we compute the uniform chroma for each luminance stop — the highest chroma that stays within the Display P3 gamut for every hue at that lightness. We find this by testing all 360° of hue and taking the lowest maximum.
The result is a per-shade chroma value that's safe for any hue. Shades near the extremes (very light or very dark) have low chroma because the gamut narrows there. Mid-range shades can support more vibrancy. On devices that don't support P3, the browser maps these colors to the nearest sRGB equivalent.
Daybridge chroma values
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Shade | |||||||||||||||
| Chroma | 0.000 | 0.000 | 0.005 | 0.017 | 0.040 | 0.081 | 0.133 | 0.153 | 0.132 | 0.112 | 0.091 | 0.068 | 0.050 | 0.036 | 0.030 |
| Example |
Hue
With luminance and chroma defined, the final axis is hue — the actual color. Daybridge uses four color modes, each determining how the hue is applied.
Neutral
Most of the UI is neutral — backgrounds, surfaces, text, borders, and dividers. Neutral colors use near-zero chroma, making them essentially gray with only a very subtle tint. We've chosen a cool tint for our neutrals.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
State
State colors communicate meaning — errors, warnings, success messages, and informational hints. Each has a fixed hue chosen for its conventional association.
At a uniform chroma (multiplier of 1.0), state colors can look a little washed out. But for states like error or warning, being noticeable is the whole point. So state colors apply a chroma multiplier that boosts vibrancy beyond the uniform safe limit. This makes them more vivid at the cost of some hues clipping to the gamut edge.
| State | Hue | Usage | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| error | 30 | Destructive actions, validation failures | |||||||||||||||
| warning | 60 | Caution, potential issues | |||||||||||||||
| success | 140 | Confirmations, completed actions | |||||||||||||||
| info | 240 | Informational messages, tips |
Brand
The Daybridge brand uses a single fixed hue for primary actions, active controls, and brand-level accents.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Adaptive
Adaptive colors are the heart of the system. Unlike the other modes, the hue isn't fixed — it's chosen by the user. This is how calendar events get their color. Each event can be assigned any hue on the wheel, and the UI generates a full palette for that hue using the same luminance stops and chroma values as everything else.
This is where OKLCH's perceptual uniformity pays off. No matter which hue a user picks, their event will look consistent with every other event — same brightness, same vibrancy, just a different color. Use the hue control below to see how the palette adapts.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Semantic Colors
The luminance stops, chroma values, and hue modes described above are the raw building blocks. To actually use them in the UI, we define semantic colors — named tokens like surface, primary, high-contrast, or divider that describe a color's purpose rather than its literal value.
Each semantic color is composed of two things:
- A shade — which of the luminance stops to use (1–15)
- A transparency — an alpha value (0–1)
Crucially, a semantic color doesn't specify a hue or chroma. Those come from the color mode applied to the element. For example, the semantic color surface might be defined as shade 2 at full opacity. When rendered inside a neutral region, it resolves to a light gray. Inside a brand region, the same surface token resolves to a light purple. Inside an adaptive region with the user's chosen hue, it takes on that hue instead.
This separation is what makes the system composable — the same set of semantic definitions works across all four color modes without any mode-specific overrides.
Semantic colors are defined throughout the rest of these guidelines alongside the components and patterns that use them, such as surfaces, typography, controls, and inputs.