Skip to content

Themes

Iris CSS follows the system light or dark setting. data-theme forces either one, on the page or on any element.

Every theme color is a light-dark() pair, and :root sets color-scheme: light dark. The browser picks the side that matches the user's setting, so dark mode needs no extra CSS. Add the color-scheme meta tag too, so the page background is right before the stylesheet loads.

<meta name="color-scheme" content="light dark">

Force a theme

Set data-theme on <html> to choose the theme for the whole page.

<html lang="en" data-theme="dark">

Theme one element

data-theme works on any element. The element repaints its text and background, and everything inside follows, nested themes included. The .light and .dark classes do the same.

Light

Always light.

Dark

Always dark.

Theme toggle

A toggle only has to set data-theme and remember it. Read the saved value in the <head>, before the page paints, so it does not flash the wrong theme. This site uses the same script.

<script>
  try {
    const theme = localStorage.getItem("theme");
    if (theme) document.documentElement.dataset.theme = theme;
  } catch {}
</script>

<button type="button" id="theme-toggle">Toggle theme</button>
<script>
  document.getElementById("theme-toggle").addEventListener("click", () => {
    const root = document.documentElement;
    const dark = root.dataset.theme
      ? root.dataset.theme === "dark"
      : matchMedia("(prefers-color-scheme: dark)").matches;
    root.dataset.theme = dark ? "light" : "dark";
    localStorage.setItem("theme", root.dataset.theme);
  });
</script>

Your own theme colors

Override a theme color with your own light-dark() pair. A single color, without light-dark(), is used in both themes. A color set on :root reaches nested themes too. Browsers without light-dark() (Chrome before 123, Firefox before 120, Safari before 17.5) recompute colors on each nested theme, so there set it on :root, [data-theme], .light, .dark to cover them.

:root {
  --i-color-background: light-dark(#fffdf7, #101418);
  --i-color-primary: light-dark(#6d28d9, #a78bfa);
  --i-color-primary-inverse: light-dark(#fff, #1e1b4b);
}

Each color family is a color and an -inverse color, the text that sits on top of it. Hover, border and focus colors are mixed from these two, so you never set them one by one. See colors for the classes that use them.

Variables

Theme colors

VariableDefault
--i-color-textDefault colorslight-dark(#373c44, #c2c7d0)
--i-color-backgroundlight-dark(var(--i-color-white), #13171f)
--i-color-lightLight & dark colorsvar(--i-color-white)
--i-color-darkvar(--i-color-black)
--i-color-brandBrand colors. The text ones reach 4.5:1 on the page and on tinted bands, such as the footer, in both themes.light-dark(#b31fc6, #e12afb)
--i-color-brand-dark#a800b7
--i-color-brand-light#f4a8ff
--i-color-brand-namelight-dark(var(--i-color-brand-dark), #e47ef0)
--i-color-brand-accentlight-dark(#b31fc6, var(--i-color-brand-light))
--i-color-inkMix targets. Hovers and colored text mix toward ink, tints mix toward paperlight-dark(var(--i-color-black), var(--i-color-white))
--i-color-paperlight-dark(var(--i-color-white), #13171f)
--i-color-baseNeutral color of buttons and links without a color classlight-dark(#8e9fbc, #c2c7d0)
--i-color-focuscolor-mix(in oklab, var(--i-color-base), var(--i-color-ink) 40%)
--i-color-primaryEach family is a color and the text color used on top of it. Hover, border, focus and tint colors are derived where they are used.light-dark(#00d5be, #05cc47)
--i-color-primary-inversevar(--i-color-slate-900)
--i-color-secondarylight-dark(#5d6b89, #525f7a)
--i-color-secondary-inversevar(--i-color-white)
--i-color-contrastlight-dark(#1c2840, #eff1f4)
--i-color-contrast-inverselight-dark(var(--i-color-white), var(--i-color-black))
--i-color-accent#ffb900
--i-color-accent-inversevar(--i-color-slate-900)
--i-color-infovar(--i-color-cyan)
--i-color-info-inversevar(--i-color-slate-900)
--i-color-successvar(--i-color-green)
--i-color-success-inversevar(--i-color-slate-900)
--i-color-warningvar(--i-color-yellow)
--i-color-warning-inversevar(--i-color-slate-900)
--i-color-dangervar(--i-color-red-600)
--i-color-danger-inversevar(--i-color-white)
--i-color-linkvar(--i-color-blue-600)
--i-color-link-inversevar(--i-color-white)
--i-variantCurrent color family. The color classes set it, buttons and links read itvar(--i-color-base)
--i-variant-inversevar(--i-color-paper)
--i-color-subtleSubtle colorslight-dark(#646b79, #848d9e)
--i-color-subtle-borderlight-dark(#e7eaf0, #202632)
--i-color-shadowBox shadow, built from one color so it follows the themelight-dark(#8191b5, #07090c)
--i-shadow0.0145rem 0.029rem 0.174rem color-mix(in srgb, var(--i-color-shadow) 1.6%, transparent), 0.0335rem 0.067rem 0.402rem color-mix(in srgb, var(--i-color-shadow) 2.4%, transparent), 0.0625rem 0.125rem 0.75rem color-mix(in srgb, var(--i-color-shadow) 3.1%, transparent), 0.1125rem 0.225rem 1.35rem color-mix(in srgb, var(--i-color-shadow) 3.5%, transparent), 0.2085rem 0.417rem 2.502rem color-mix(in srgb, var(--i-color-shadow) 4.3%, transparent), 0.5rem 1rem 6rem color-mix(in srgb, var(--i-color-shadow) 5.9%, transparent), 0 0 0 0.0625rem color-mix(in srgb, var(--i-color-shadow) 1.6%, transparent)

Selection and scrollbar

VariableDefault
--i-selection-color-backgroundlight-dark(#05cc4740, #05cc4730)
--i-scrollbar-color-thumbScrollbarcolor-mix(in oklab, var(--i-color-subtle) 50%, transparent)
--i-scrollbar-color-tracktransparent