Whakerexa > Getting started > Themes

Themes

What is a theme?

A theme is a standalone CSS file. It redefines CSS variables and decoration rules — colors, gradients, borders, typography accents — without touching structure or accessibility layers.

Themes are mutually exclusive: each theme replaces the previous one entirely. They never stack. This is enforced by ThemeManager, which manages a single <link id="wexa-theme"> element whose href is swapped on every theme switch.

Unlayered colors, layered accents

Most color variables (--bg-color, --border-color, --nav-bg-color, --buttons-bg-color…) are declared unlayered in a theme file — no @layer wrapper. An unlayered rule always wins over any @layer rule, regardless of selector specificity or load order, so this guarantees the active theme's colors beat wexa.css's own @layer theme defaults every time.

--custom-color1 and --custom-color2 (the accent pair used for chapter titles in book.css and slide headings in slides.css) are the exception: they are declared inside @layer theme. This lets a single document write its own unlayered override for a specific element — see wexa_theme_highcontrast.css's flat .slide h1 border — without a specificity fight against the theme file.

Integration

Two requirements make theme switching work correctly:

  1. The default theme's <link> element must carry id="wexa-theme". Without it, ThemeManager creates a second link element and themes accumulate instead of replacing each other.
  2. The default theme must be registered with ThemeManager and declared via setDefault().

Never load a theme file with a static <link> in addition to registering it. ThemeManager only ever swaps the href of #wexa-theme — it does not remove a second, separately linked copy. If that file is also needed for page-specific rules that must always be present (layout, custom classes for one document), split it in two: the page-specific file stays statically linked, and only the color/variable file is registered as a theme.

Add id="wexa-theme" to the default theme link in <head>.

<link id="wexa-theme" rel="stylesheet"
      href="wexa_statics/css/themes/wexa_theme.css" />

The active theme name is persisted in the URL parameter wexa_theme and propagated to all internal links via setUrlWithParameters().

const themes = new ThemeManager();
themes.register('wexa_theme',   'wexa_statics/css/themes/wexa_theme.css');
themes.register('aurora',       'wexa_statics/css/themes/wexa_theme_aurora.css');
themes.register('highcontrast', 'wexa_statics/css/themes/wexa_theme_highcontrast.css');
themes.setDefault('wexa_theme');
window.themes = themes;

Available themes

wexa_theme

Default theme. Navy and teal palette, animated gradient underline on links, subtle background gradients.

aurora

Cool blues and greens. Inspired by northern lights. Soft glow on interactive elements.

highcontrast

OS-style high contrast. White/black with dark-blue or yellow links (WCAG AAA). No gradients, no border-radius, flat colors throughout. Designed for maximum legibility.

Creating a custom theme

Redefine only the variables you need — all others fall back to wexa.css defaults. Keep colors unlayered; put --custom-color1/--custom-color2 inside @layer theme, matching the built-in themes.

Register it the same way as built-in themes. The .contrast class is managed by AccessibilityManager — a theme must not override colors in contrast mode.

/* my_theme.css */
:root:not(.dark) {
    --bg-color: rgb(255, 250, 240);
    --a-color:  rgb(180, 60, 0);
}

.dark {
    --bg-color: rgb(20, 15, 10);
    --a-color:  rgb(255, 160, 80);
}

@layer theme {

    :root:not(.dark) {
        --custom-color1: rgb(80, 20, 0);
        --custom-color2: rgb(180, 60, 0);
    }

    .dark {
        --custom-color1: rgb(255, 220, 180);
        --custom-color2: rgb(255, 160, 80);
    }

}