Published: December 11, 2025
So, you have a site that you want to build or redesign. Maybe you have a few core colors in mind, and you're thinking about how to quickly implement a theme based on those colors.
You'll need your primary color, but also colors for actions, hover states, errors, and colors for other user interface needs. Then what about light and dark mode options? Suddenly there's lots of colors you'll need, and it can feel overwhelming.
The good news is that when it comes to building a palette relative to the color tokens that define your site and switching between color modes, Baseline features can do a lot of the heavy lifting for you. You can explore some of these techniques in the featured demo, a color themed playlist on the fictional Baseline Radio site.
Build a base with relative colors
If you have an idea for a primary color for your theme, with some basic color theory and the CSS relative color syntax, you can quickly start generating a palette of colors to use in your theme.
Say your base color is a shade of teal, which you can first define in your preferred color format. Then you can use any color function to create new colors relative to your base color:
html {
--base-color: oklch(43.7% 0.075 224);
}
The --base-color custom property is made using the oklch() color function. OkLCh is the cylindrical form of the Oklab color space, and defines values for three channels: L (lightness), C (chroma), H (hue), plus an optional alpha channel to control transparency.
OkLCh is a good format for this type of color manipulation, as it's designed to provide perceptual uniformity. For example, if you adjust just the hue of a color, the resulting color should have a similar perceived lightness and chroma to the original color. This is especially useful in avoiding unexpected contrast issues.
Keeping the same lightness and chroma from your --base-color, you could adjust the hue by 120 degrees in both directions for a triadic palette.
html {
/* ... */
--triadic-color-primary: oklch(from var(--base-color) l c calc(h + 120));
--triadic-color-secondary: oklch(from var(--base-color) l c calc(h - 120));
}
As shown here, the relative color syntax uses a color function that references an origin color (--base-color in this example) with the from keyword, and adjusts the color space's respective channels based on the chosen output color, which in this case will also be OkLCh.
The resulting output gives you a dark pink for the --accent-color, and a shade of gold to use for the --highlight-color, both with the same lightness and chroma as the original --base-color.
html {
/* ... */
--accent-color: var(--triadic-color-primary);
--highlight-color: var(--triadic-color-secondary);
}
html {
/* Input color in the rgb color space*/
--base-color: teal;
/* Output color in oklch. Computes to oklch(0.543123 0.0927099 314.769) */
--triadic-color-primary: oklch(from var(--base-color) l c calc(h + 120));
}
A complementary color would add 180 degrees to the hue angle.
html {
/* ... */
--complement-color: oklch(from var(--base-color) l c calc(h + 180));
--border-highlight: var(--complement-color);
}
For a hover state in your UI, you might want to output a lighter version of a particular color. This would mean increasing the value of the lightness channel. For an active state, you might want to add transparency by adjusting the alpha channel, or darkening it by decreasing the value of the lightness channel.
html {
/* Darken the --base-color by 15% */
--base-color-darkened: oklch(from var(--base-color) calc(l * 0.85) c h);
/* Assign this color a meaningful variable name */
--action-color: var(--base-color-darkened);
/* Lighten the --action-color by 15% */
--action-color-light: oklch(from var(--action-color) calc(l * 1.15) c h);
/* Darken the --action-color by 10% */
--action-color-dark: oklch(from var(--action-color) calc(l * 0.9) c h);
}
Here, we are deriving the --action-color from the --base-color, and using it for buttons and links. The --action-color has two variants—lighter and darker—that would still apply even if the --action-color was changed to be relative to another color different from the --base-color.
You can adjust channels by using a math function like calc() or replacing the channel entirely with a new value. Unchanged channels are represented by their respective letters (for example, l for an unchanged lightness value).
Mix colors with color-mix()
For other color variants, you could take a similar approach and adjust other channels of the --base-color custom property. Or use color-mix() to add hints of the base color to other aspects of your design.
The --border-color is a mix of the base color and the named color grey, interpolated in the oklab color space. When used as a color interpolation method, this provides perceptually uniform results.
html {
--base-mix-grey-50: color-mix(in oklab, var(--base-color), grey);
--border-color: var(--base-mix-grey-50);
}
By default, this would be 50% of each color, but you can make either color more or less prominent by adjusting its percentage weight.
html {
--background-mix-base-80: color-mix(in oklab,
var(--background-color) 80%,
var(--base-color));
--surface-light: var(--background-mix-base-80);
}
An alternative to adding more color to an element would be to adjust its chroma channel using the relative color syntax. The border of the text inputs in the contact form have a slightly more colorful border when in focus.
[data-input*="text"] {
--focus-ring: transparent;
/* ... */
&:focus {
--focus-ring: oklch(from var(--border-color) l calc(c + 0.1) h);
}
}
Opt in to light and dark modes
Once you have a set of colors to work with, you'll want an efficient way to apply different colors for light and dark modes.
Signal support for light and dark themes with the color-scheme property
You can instantly tell the browser that your site can be viewed in "light", "dark", or both modes with the color-scheme property. This property tells the browser in which color schemes an element can be comfortably rendered.
html {
color-scheme: light dark;
}
Setting color-scheme: light dark on the :root pseudo-element or the html element:
- Tells the browser that your page supports being viewed in light or dark mode.
- Changes the default colors of the browser user interface to match the respective operating system setting.
To give user agents earlier notice that your page supports light and dark modes, you can also signal support for color scheme switching by adding a <meta> element in the <head> of the document.