dark_mode

Atlas Themes

A complete system of color, typopgraphy, and layout, supporting dark mode, Gutenberg, and Storybook


The simplest way to use Atlas Themes, is to configure its Theme object to match an existing theme system. This ensures the Gutenberg editor, and the UI that it generates, fits the theme. You can just stop right there, if you want.

But, you can also elect to take advantage of additional features. You can even build your entire website on this theme system alone. Those special features include:

  • Fully-automated "dark mode."
  • Fully-automated color system, including conforming to accessibility guidelines while also allowing arbitrary color selection.
  • Arbitrary font configurations, with configurable standard typefaces, sizes, etc..
  • Gutenberg editor shows palettes of color and fonts, ensuring theme and brand consistency.
  • Load Google Fonts with the minimum transfer-size, with a single line of code.

In this chapter, we describe the Atlas philosophy and tools concerning color, typography, and layout.

Color System

Graphic designers and web component designers have always needed to work with color tones. The idea is that there are some root colors -- often company brand colors -- with variants needed for things like lighter and darker areas, shading, gradients, or a "dark mode" that keeps the "spirit" of the brand color but tuned for a dark environment.

The naive approach to creating related tones is to adjust the color's "lightness" as defined by the HSL color space. However, if you create lighter and darker colors this way, you'll discover that it is a poor algorithm, especially at the extremes, and especially with certain hues, where the extremes end up just white or black:

"Lightness" swatches

Famously, the Google Material Design team created great color swatches for a handful of common colors, and an online tool for creating your own swatches. Compare theirs with the "lightness" algorithm above:

Material Design swatches
[image]
[image]

Sadly, a few issues remain. First, why is yellow turning orange when it gets darker? Second, it's great to generate swatches when the colors are known, but what about when the marketer selects an arbitrary color in Gutenberg? Or what about when a new color is automatically selected to support dark mode? Third, these are specific, discrete colors in a "level" range of 50 to 900, but shouldn't you be able to use any level you want, e.g. 350 or even 286?

The Atlas swatch system solves all of those issues. Using the same level-numbering system as Material Design, the "main" color is taken to be level 500, but you can generate any level in the swatch. Here are those same colors using the Atlas swatch system, where you'll note the colors are not "blown out" at the extremes, and yellow remains yellow throughout:

Atlas swatches

Here are those Atlas swatches again, comparing with the specific levels from Material Design:

Atlas swatches, with Material Design terminology
0
100
200
300
400
500
600
700
800
900
999
0
100
200
300
400
500
600
700
800
900
999
0
100
200
300
400
500
600
700
800
900
999

Try it for yourself! Pick any color and see what happens:

Atlas swatches, any color
Atlas:
0
100
200
300
400
500
600
700
800
900
999
HSL:
0
100
200
300
400
500
600
700
800
900
999

The Atlas theme and blocks system already uses this internally, but you can access it too. The simplest way is the getSwatchColor() function which you can import from the root of the core module, or from the specific import as shown below. For example, to get the 650 level for dark blue:

import { getSwatchColor } from '@asmartbear/gutenberg-bridge/dist/themes/colors'
function getDarkerBlue() {
return getSwatchColor('#000080', 650)
}

Color: Accessibility

Other color utilities can be found in that same color module, such as isLight() determining whether the color is "light," from an accessiblity standpoint (which is how the text color is selected for the "Custom Swatches" widget above.)

A common color requirement for accessibility, is for foreground (often text) colors to contrast sufficiently against its background color. You've seen the Atlas Theme object utilities for this already; those utilities also use these, but also apply the colors registered in the theme, which makes it even easier! If you want to compute colors by hand, however, you'll find the following useful.

In the simpler of the two utility functions, you provide a test color (i.e. the "background"), for which you want to select a sufficiently-contrasting color. You supply a prioritized list of options, and it returns the first color from that list which sufficiently contrasts. The list might come from a set of standard theme colors, for example, and ordered by which color you'd prefer, if it meets the guidelines.

import { getFirstReadableColor } from '@asmartbear/gutenberg-bridge/dist/themes/colors'
function getMyReadableColor(color:string): string {
return getFirstReadableColor(color, [
'#303F9F', // indigo that you'd really prefer
'#f0f0f0', // close-to-white option
'#222', // close-to-black option
])
}

How much contrast is "sufficient?" There are standards from WCAG, which depend on things like the size of the text and the desired target level of accessiblity. The default in Atlas is a contrast ratio of 3 as defined by WCAG. You can specify a different ratio by specifying that number as an additional, final argument to getFirstReadableColor().

The more complex case, is when there's not only a background color, but a "desired" foreground color, where you'd like to select that foreground color as-given, if the constrast is already sufficient; if it is insufficiently constrasting, you'd like to use a different tone in the same color family, i.e. something from the same Atlas Swatch. Make the color lighter or darker, until it constrasts enough. Finally, it is possible to construct a situation where no swatch color contrasts enough, therefore there needs to be a priority-ordered list of backup options as well. Atlas has a utility for this:

import { getReadableColor } from '@asmartbear/gutenberg-bridge/dist/themes/colors'
function getMyBestColor(backgroundColor:string, desiredColor:string): string {
return getReadableColor(
desiredColor, // color we'd prefer to use
backgroundColor, // background we're constrasting against
[
'#303F9F', // ordered backup colors, as before
'#f0f0f0',
'#222',
],
3 // optional contrast ratio threshold
)
}

Try it for yourself!

Background Color
Target Foreground Color
Best Constrasting Color
#ff7373ff
Example When Composed:
The slow uncolored fox slept.

Color: Palettes

The Theme.settings.palette object holds the "palette" for your theme, which are pre-defined colors plus related colors. While it's always possible to use arbitrary colors in block properties, defining a standard palette makes it possible to show that palette in Gutenberg, and also tweak the values of those colors later, without having to edit all the existing blocks.

Your theme's palette is placed in Theme.settings.palette.brand. Each object therein contains a color in a field named main, then lighter and darker variations in additional fields. Finally, a text field is the color of text that is overlayed on that color. To make it easier to supply all these colors, you can use StandardThemeGenerator.addBrandColor() to generate those fields automatically. For example:

const themeGen = new StandardThemeGenerator();
themeGen.addBrandColor("tiffany", "#0ecad4"); // Named brand colors
themeGen.addBrandColor("mirage", "#002838"); // for WP Engine
themeGen.addBrandColor("seafoam", "#50e3c2");
themeGen.addBrandColor("royal", "#7e5cef");
themeGen.addBrandColor("sunset", "#ff6c29");
const theme = themeGen.getTheme();

In addition to your brand colors, there are a few other standard fields on the palette object:

lightText
darkText
Standard text colors. Besides creating consistency in the theme, this allows the system to automatically select text colors that contrast best with an arbitrary background color, while staying consistent. This is especially useful with arbitrary backgrounds set in Gutenberg, or in dark mode.
darkModeShift
Controls "how much darker" dark-mode becomes. Used to shift color tones both more light and dark (as well as less saturated) in dark-mode. A standard value is 250, with 0 meaning "no color change for dark mode." The easiest way to understand this value, is to just play with it, and see what it does to your site.

Dark mode: Theory & Automation

The Atlas theme system can provide dark mode automatically. How does this work?

Atlas adopts the Google Material Design philosophy on dark mode principles. It's not necessary that you learn those principles, but they explain the "why" behind the choices we've made, particularly with regard to color.

To create a dark mode theme, pass true for the second parameter of new StandardThemeGenerator(). This sets you up with defaults for standard colors, for example using the Material Design recommendation of #121212 for the main background (which still leaves room for "true black" and for shadows).

This also automatically adjusts the brand palette colors that you register. The adjustment might be surprising at first, because sometimes the color is lightened (and also desaturated), while other times it is darkened. This is due to our dark mode philosophy, which includes observations such as: Dark hues against any dark color are not only difficult to see, but tend to "vibrate," and therefore it is better to use a low-saturation pastel version of the color.

The color function getSwatchColor() accepts a second parameter which is a "dark mode shift." This is a number in units of Material Design levels that determines "how much" to shift colors (whether higher or lower) for dark mode (although note that the algorithm uses the level only as a guide, because some hues require more or less shifting, and there are additional adjustments for saturation). This is how the Atlas blocks automatically support dark made, and you can use it directly as well.

Example of dark mode in action:

[image]
[image]

Note how the heading text becomes near-white to contrast against near-black, but in the case of the box of text, the text remains near-white, because it still contrasts sufficiently. Also notice how the already-dark text box becomes lighter but desaturated, while retaining the character of the original color.

Images are also adjusted automatically. Images that were not created by Atlas (i.e. not from Gutenberg, but from some other URL) are adjusted with a standard algorithm, but images managed by Atlas in Gutenberg are adjusted using additional intelligence. In particular, the goal is to make images a specific target darkness, but for colors to still be visible. The brighter the image, the more we need to darken it to achieve a standard darkness level. But we also adjust contrast and saturation, so that colors don't get completely washed to gray. With images managed by Atlas, we pre-compute values like the average color hue and lightness, which then become inputs to the algorithm.

While the inputs to the algorithm are computed in Javascript, the image adjustments are performed in CSS. This means there aren't any CPU-intensive image calculations, nor delay while images are regeneraed, nor generated images that need to be stored or cached.

To see this in action, note how the first example is an image that is already dark, so it is barely changed at all, whereas the second image is bright and colorful is significantly adjusted, but colors are increased in contrast to prevent them from becoming muddy.

Comparing light-mode images (left) with image-specific dark-mode (right)
[image]
[image]
[image]
[image]

All images share the same average brightness in dark mode, which gives the interface a consistency, with nothing "jumping out" at the user who might be viewing the page in a darkened room.

Typefaces: Fonts and the typeface palette

Typesets: Full definitions, sizes, small screens, and presets

Google Fonts

  • Getting Started with the Gutenberg / React Bridge
    Display blocks from Gutenberg, and creating a WordPress Theme for WYSIWYG editing inside Gutenberg.
  • Atlas Blocks
    The built-in Atlas blocks, in React and in Gutenberg.
  • Atlas Layouts
    Custom Layout blocks: Layouts with controls
  • Atlas Custom Blocks
    How to create new React components that automatically generate Storybook documentation and rich, composable Gutenberg blocks.
  • Atlas Slabs
    Mocking up and filling sections within a React page, using sections within Gutenberg.
  • Atlas Theming
    A complete system of color, typopgraphy, and layout, supporting dark mode, Gutenberg, and Storybook
  • Storybook Integration
    How to automate Storybook stories for everything - custom, WordPress Core, and Atlas blocks.
  • Storybook Reference
    Storybook-based interactive documentation for Atlas Core and Gutenberg Core blocks.