Baselayer 3: Colors
Baselayer’s color utility class system is based on using the modern CSS oklch() function and a series of interpolated lightness level variables that enable the lightness level utility classes to work. The lightnsses for color each class are not pre-created, unlike with other CSS frameworks.
In the HTML, you first choose the color utility class, then you choose the lightness level class separately. Handled this way, the stylesheet doesn’t need to be loaded with lighness classes for every color — most of which you would never use.
The oklch() function has been baseline: widely available since March 2023. All “evergreen” browsers (Chrome, Edge, Firefox, Safari) have capability to use it (see Can I use).
In setting up the lightness utility classes, Baselayer’s oklch() formulas use the hue (h) and chroma (c) values out of the theme color variables. The lightness levels *-100 trough *-900 are perceptually uniform.
Demo using background bg-* utility classes:
bluegreenamberredgrayAlso available:
- A built-in dark theme.
black,white,reversi,reversi-flip, andtransparent— see other Baselayer color utilities.
Color and shade utility classes
All colors and lightness levels are declared in variables.css.
Color utility classes (declared in colors.css) are prefixed acording to where the color will be applied — border b-* text t-* or background bg-*. I have named the colors according to their common names (blue, green, amber, red, gray). You can modify the root variables of these colors, and you can add your own by copying and modifying the border, text, and background utilities you see at the start of css/_colors.css — see adding more colors.
Example border, text and background utilities:
<div class="b-heavy b-green"></div>
<div class="t-heavy t-green t-700 dark:t-300"></div>
<div class="t-black bg-green bg-300"></div>
See also dark theme lightness levels and black, white, and reversi.
Example usage:
<div aria-label="Note" class="popout my-3 bl-heavy b-blue b-400 dark:b-600 p-3 t-reversi bg-blue bg-200 dark:bg-800">
☆ Note (information) panel.
</div>
The lighness modifiers *-100 through *-900, if used alone, do not provide color. But if you use them to supplement one of the base colors above, then that color class will provide the color, and the modifier will set its lightness level.
Colors and accessibility
In any color model, color combinations must be chosen with care so that there is sufficient contrast between text and background colors for purposes of assessibility.
In your text and background color combinations, be careful to ensure that the text is readable — there needs to be an adequate contrast. Most organizations should to aim for WCAG level AA for accessibility requirements.
For WCAG level AA conformance, most user interface colors need to be mid-level (i.e. use *-500 up) if the text color is white, or lighter than the mid-level (i.e. use *-400 down) if the text color is black.
Buttons have a built-in hover background-color change: the background color becomes darker for the light theme, and lighter for the dark theme.
<!-- Default button -->
<button type="button" name="button">Button</button>
<!-- Blue button -->
<button class="bg-blue" type="button" name="button">Button</button>
<!-- Amber buttons -->
<button class="bg-amber" type="button" name="button">Button</button>
<button class="t-black bg-amber bg-200 dark:bg-300" type="button" name="button">Button</button>
<!-- Green outline (a.k.a. ghost) button -->
<button class="b-thin b-green bg-transparent t-green t-600 hover:t-white hover:bg-green hover:bg-600" type="button" name="button">Button</button>
Background reading on colors and accessibility:
- Useful blog posts from The Accessibility (A11Y) Project:
- Web Content Accessibility Guidelines (WCAG) 2
- Contrast and Color Accessibility (WEB AIM)
- The Coolors contrast checker
- Web Accessibility: Understanding Colors and Luminance (Mozilla Developer Network Docs)
How Baselayer CSS handles color
Baselayer handles color in a 4-step process:
-
The 5 baselayer colors are first declared as CSS variables:
:root { /* Theme base colors */ --blue: #4166f5; --green: #00a550; --amber: #ffdc00; --red: #e34234; --gray: #838996; } -
The utility class lightness level suffixes go up in hundreds, from
-100to-1000. (The-1000is only used once in Baselayer, for setting the dark theme<body>background. Levels-100to-900are used in the lightness utility classes.):root { --l100: 98%; --l200: 86%; --l300: 74%; --l400: 62%; --l500: 50%; --l600: 42%; --l700: 34%; --l800: 26%; --l900: 18%; --l1000: 10%; } -
In the variables file, Baselayer colors are set up using the formula. For example, in the base text color:
:root { --tc-base: background-color: light-dark( oklch(from var(--gray) var(--l900) c h), oklch(from var(--gray) var(--l100) c h) ); } -
Finally, the variable is used in a style rule:
body { color: var(--tc-base); }
Adding more colors
You can add any colors you want, and in any format you want. But if you want to add colors in a way that integrates with the Baselayer system, do this:
- You need to declare your colors first as CSS variables (preferably in the
:root{}), so that they are available for the lightness utility classes. - You need to insert your colors before the Baselayer
@layer bl-colorsso that your colors can make use of the Baselayer color shades.
Examples:
:root {
--purple: #9400d3;
--teal: #0080A2;
}
/* If you want to create your own utility classes */
/* Border colors */
.b-teal,
.hover\:b-teal:hover {
--bc: var(--teal);
border-color: oklch(from var(--bc) var(--bl, 50%) c h);
}
.b-purple,
.hover\:b-purple:hover {
--bc: var(--purple);
border-color: oklch(from var(--bc) var(--bl, 50%) c h);
}
/* Text colors */
.t-purple,
.hover\:t-purple:hover {
--tc: var(--purple);
color: oklch(from var(--tc) var(--tl, 50%) c h);
}
.t-teal,
.hover\:t-teal:hover {
--tc: var(--teal);
color: oklch(from var(--tc) var(--tl, 50%) c h);
}
/* Background colors */
.bg-purple,
.hover\:bg-purple:hover {
--bgc: var(--purple);
background-color: oklch(from var(--bgc) var(--bgl, 50%) c h);
}
.bg-teal,
.hover\:bg-teal:hover {
--bgc: var(--teal);
background-color: oklch(from var(--bgc) var(--bgl, 50%) c h);
}
The five Baselayer built-in theme colors (blue, green, amber, red, gray) are set using hex # codes). However, if you add your own in all other color system, the oklch( ... ) formulas can handle it.
How the dark theme works
Baselayer has a simple dark theme built in. Since v.3.4.0, the user’s operating system preference for light or dark mode is automatically detected on the HTML tag by the following CSS:
html {
color-scheme: light dark;
}
For the dark theme, HTML elements are generally flipped from light to dark, or dark to light, as required. The colors are shifted as follows:
| Light theme | Dark theme | |
|---|---|---|
| Body background | White | Near black |
| Base text | Dark gray | White |
| Links/ hovered | Mid blue/ Darker blue | Mid blue/ Lighter blue |
| Buttons/ hovered | Mid gray/ Darker gray | Mid gray/ Lighter gray |
| Table borders, horizontal rules, form inputs | Mid-light gray | Mid-dark gray |
| Form elements’ text and background | Same as base text and body in light theme | Same as base text and body in dark theme |
All the theme color variables in Baselayer involve a light-dark() CSS function. For example:
:root {
/* Body tag background color */
--bgc-body: light-dark(
white,
color-mix(in oklch, var(--gray), var(--l950)));
/* Base text color (also set on the body tag) */
--tc-base: light-dark(
color-mix(in OKLCH, var(--gray), var(--l900)),
color-mix(in OKLCH, var(--gray), var(--l100))
);
}
Baselayer does not use @media (prefers-color-scheme: dark) {} anywhere.
Light and dark theme classes
If you wish to give your visitors the option to switch between light and dark modes, you can include a JavaScript that swaps between CSS classes theme-light and theme-dark on the HTML tag. Baselayer has these two tags built in, so that you can implement the style selection:
html {
color-scheme: light dark;
&.theme-light {
color-scheme: light;
}
&.theme-dark {
color-scheme: dark;
}
}
Example implementation: the Baselayer documentation has demo JavaScript switches for light, dark, and auto modes. The auto mode removes theme-light and theme-dark from the <html> tag, so that you simply get the Baselayer default, that is the user’s operating system theme preference setting.
Dark theme lightness levels
There will be many situations in your design where you want your utility color shades darkened or lightened for the dark theme (similar to how the background color is flipped from white to near black, etc.). Since v.3.5, Baselayer has dark: prefixed color lightness utilities for border, text, and background colors. (This is more versatile versalility than the *-dark-invert modifier classes in v.3.4, that are now removed.)
Example using bg-blue:
<div class="bg-blue bg-100"></div>
<div class="bg-blue bg-100 dark:bg-900"></div>
| Lightness utilities | With dark mode |
|---|---|
bg-100 |
dark:bg-900 |
bg-200 |
dark:bg-800 |
bg-300 |
dark:bg-700 |
bg-400 |
dark:bg-600 |
bg-500 |
dark:bg-500 |
bg-600 |
dark:bg-400 |
bg-700 |
dark:bg-300 |
bg-800 |
dark:bg-200 |
bg-900 |
dark:bg-100 |
Notes:
- The middle dark mode
dark:bg-500is the same lightness level asbg-500. - Except for reversi, the colors themselves don't change when switching between light and dark mode.
- Only lightness utilities have a
dark:prefix option.
What if you don’t want a dark theme?
Then you can simply override or replace the HTML tag as follows:
html {
color-scheme: light;
}
And then you won’t need to use any dark: prefixed uilities.
Hover states and color changes
Since Baselayer v.3.8.0, links and buttons have built-in hover states: usually, link text-color darkens on hover, and button background-color darkens on hover. The exceptions are black, white, and reversi — these do not darken or lighten on hover.
However, colors (including black, white, and reversi) have alternate prefixed hover: utilities — so that the color can be implemented on hover (e.g. for creation of ghost buttons, or making a table row background change color on hover). Note: shade utility classes do not have hover states.
The following elements and classes have hover states:
-
<a href="">links have a text-color change on hover: becoming darker for the light theme and lighter for the dark theme. Examples:<a href="">Default (blue) link</a> <a class="t-green" href="">Green link</a> -
Buttons (
<input>buttons,<button>, and pseudo button links<a class="btn" href="">) have a background-color change on hover: becoming darker for the light theme and lighter for the dark theme. -
Named color utilities can “switch on” color in the hover state, if you add the
hover:prefix. Example:<!-- Outline (ghost) button --> <button class="b-thin b-green bg-transparent t-green t-600 hover:t-white hover:bg-green hover:bg-600" type="button" name="button">Button</button> <!-- Adding a hover color to a table row --> <tr class="hover:bg-gray bg-100 dark:bg-900"> ... </tr>
Other Baselayer color utilities
Black, white, and reversi
Black and white are explicitly named colors in baselayer. They do not have lightness levels (use grays).
If you want something black in the light theme but white in the dark theme (and vise versa like reversi game pieces), Baselayer has *-reversi and *-reversi-flip classes that handle this.
*-black/hover:*-black— named color black*-white/hover:*-white— named color white*-reversi/hover:*-reversi— black for light theme; white for dark theme*-reversi-flip/hover:*-reversi-flip— white for light theme; black for dark theme
bg-black
bg-white
bg-reversi
bg-reversi-flip
CurrentColor
- Border-color:
b-currentcolor/ background-color:bg-currentcolor - Also aliases
b-currentolor/bg-currentColor.
Sets the border or background to currentColor — the current text color.
There are no hover states of *-currentColor — but if the color changes on hover, then so will the *-currentColor.
Transparent
- Border-color:
b-transparent/ background-colorbg-transparent
There are no hover states of *-transparent.
Glass (blur) and filter backgrounds
The following color utility modifiers are intended for use in conjunction with background colors and shade (they do not include color).
Use these classes on layers above images. You will see little or no effect from them unless thy are used on a panel in front of a photo or complex pattern.
bg-glass(aliasbg-blur) — mid-opacity, blurred backroundbg-filter— mid-opacity backgroundhover:bg-opaque– removes the blur, and sets the opacity level to 1 (fully opaque)
These modifiers can also be used in conjunction with the background lightness utilities (but not the dark: lightness utilities). When bg-100 ... bg-900 are used with bg-glass, bg-blur, or bg-filter, the refactored to affect the transparency level.
Notes:
- You will see no effect from these classes unless you place them on a panel in front of a photo or complex pattern. Intended for use in conjunction with background colors and shades.
- The black, white, and reversi colors don’t come with a built-in middle shade. Therefore, when being used with
bg-filterorbg-filterthey must always be accompanied with one of the background shadesbg-100thoughbg-900.
Examples:
bg-glass bg-white bg-900– a highly opaque white glassmorphic blur effectbg-filter bg-white bg-200– a lightly transparent black glass effectbg-filter bg-green– a green mid-opacity filter