Theming

Stacks provides a robust theming API to handle theming in various contexts.

Light, dark, and high contrast modes

Section titled Light, dark, and high contrast modes

Stacks supports light, dark, and high contrast modes. By using Stacks, you will get each of these modes for free. By using a Stacks component, an atomic color class, or CSS variable directly, your theme will switch appropriately based on the following methods:

  1. You can apply .theme-system to the body element. This will change colors based on the prefers-color-scheme media query, which is ultimately powered by the user’s system or browser settings. This can be preferable for folks who have their system turn to dark mode based on ambient light or time of day.
  2. Alternatively, you can set a dark mode that is not system dependent by attaching .theme-dark to the body element.
  3. Adding .theme-highcontrast to the body element will boost colors to WCAG Level AAA contrast ratios in as many places as possible. This mode stacks on top of both light and dark modes. The only exception is branded themed colors remain untouched by high contrast mode.

There are also conditional classes that can be applied to override assumed dark mode colors, force light mode, or to force dark mode. Forcing modes can be good for previews in admin-only situations.

Like light and dark modes, Stacks has baked-in theming available as primary and secondary CSS color variables. These can be overridden in various scopes. By default, the Stacks primary and secondary variables are Stack Overflow’s branded orange and blue, respectively, at a global scope, affecting every element on the page. These can be overridden in the context of your product, e.g. Teams, a Collective, or Stack Exchange Site. Both primary and secondary colors are available in the full spectrum of atomic classes and variables.

At its core, our theming is powered by CSS variables that are split into primary and secondary H, S, L, and R, G, B values. For example:

--theme-base-primary-color-h: 313;
--theme-base-primary-color-s: 76%;
--theme-base-primary-color-l: 54%;
--theme-base-primary-color-r: 227;
--theme-base-primary-color-g: 49;
--theme-base-primary-color-b: 188;

--theme-base-secondary-color-h: 313;
--theme-base-secondary-color-s: 76%;
--theme-base-secondary-color-l: 54%;
--theme-base-secondary-color-r: 227;
--theme-base-secondary-color-g: 49;
--theme-base-secondary-color-b: 188;

These then get combined to generate the full set of color stops. Overriding them is as simple as redefining those individual theming variables.

By default, we take those single primary and secondary colors and generate an appropriate dark mode equivalent. However, what if your branding color is dark purple—or black? You can pass an optional dark mode override:

--theme-dark-primary-color-h: 288;
--theme-dark-primary-color-s: 45%;
--theme-dark-primary-color-l: 60%;
--theme-dark-primary-color-r: 180;
--theme-dark-primary-color-g: 105;
--theme-dark-primary-color-b: 199;

--theme-dark-secondary-color-h: 313;
--theme-dark-secondary-color-s: 76%;
--theme-dark-secondary-color-l: 54%;
--theme-dark-secondary-color-r: 227;
--theme-dark-secondary-color-g: 49;
--theme-dark-secondary-color-b: 188;

Stacks allows for further theming various portions of a page. You can simply pair the .themed class with an atomic color stop, and a new theming scope. For this example, we’re using a class name of .theme-team-[xxx] with a unique ID appended.

<div class="bg-theme-primary-700"></div>
<div class="themed theme-team-001 bg-theme-primary-500"></div>
<div class="themed theme-team-002 bg-theme-primary-500"></div>
<div class="themed theme-team-003 bg-theme-primary-500"></div>

<style>
.theme-team-001 {
--theme-base-primary-color-h: 349;
--theme-base-primary-color-s: 81%;
--theme-base-primary-color-l: 58%;
--theme-base-primary-color-r: 235;
--theme-base-primary-color-g: 59;
--theme-base-primary-color-b: 90;

--theme-base-secondary-color-h: 349;
--theme-base-secondary-color-s: 81%;
--theme-base-secondary-color-l: 58%;
--theme-base-secondary-color-r: 235;
--theme-base-secondary-color-g: 59;
--theme-base-secondary-color-b: 90;
}

.theme-team-002 {
--theme-base-primary-color-h: 41;
--theme-base-primary-color-s: 93%;
--theme-base-primary-color-l: 58%;
--theme-base-primary-color-r: 247;
--theme-base-primary-color-g: 183;
--theme-base-primary-color-b: 49;

--theme-base-secondary-color-h: 41;
--theme-base-secondary-color-s: 93%;
--theme-base-secondary-color-l: 58%;
--theme-base-secondary-color-r: 247;
--theme-base-secondary-color-g: 183;
--theme-base-secondary-color-b: 49;
}

.theme-team-003 {
--theme-base-primary-color-h: 288;
--theme-base-primary-color-s: 76%;
--theme-base-primary-color-l: 38%;
--theme-base-primary-color-r: 141;
--theme-base-primary-color-g: 23;
--theme-base-primary-color-b: 170;

--theme-base-secondary-color-h: 288;
--theme-base-secondary-color-s: 76%;
--theme-base-secondary-color-l: 38%;
--theme-base-secondary-color-r: 141;
--theme-base-secondary-color-g: 23;
--theme-base-secondary-color-b: 170;

/* Override colors for dark mode only */
--theme-dark-primary-color-h: 288;
--theme-dark-primary-color-s: 45%;
--theme-dark-primary-color-l: 60%;
--theme-dark-primary-color-r: 180;
--theme-dark-primary-color-g: 105;
--theme-dark-primary-color-b: 199;

--theme-dark-secondary-color-h: 288;
--theme-dark-secondary-color-s: 45%;
--theme-dark-secondary-color-l: 60%;
--theme-dark-secondary-color-r: 180;
--theme-dark-secondary-color-g: 105;
--theme-dark-secondary-color-b: 199;
}
</style>
body
1 2 Next
.themed.theme-team-001
C
1 2 Next
.themed.theme-team-002
E
1 2 Next
.themed.theme-team-003
F
1 2 Next
body .theme-light__forced
1 2 Next
.theme-light__forced .themed.theme-team-001
C
1 2 Next
.theme-light__forced .themed.theme-team-002
E
1 2 Next
.theme-light__forced .themed.theme-team-003
F
1 2 Next
body .theme-dark__forced
1 2 Next
.theme-dark__forced .themed.theme-team-001
C
1 2 Next
.theme-dark__forced .themed.theme-team-002
E
1 2 Next
.theme-dark__forced .themed.theme-team-003
F
1 2 Next
Deploys by Netlify