Default Admin mode and token guide

This page explains the report sections that describe theme contrast mode, forced-colors mode, APCA polarity, and the accent token derivation logic used by Default Admin.

Generated
2026-05-05T20:56:36.736Z

Important: Theme "Increase Contrast Mode" vs Forced-Colors Mode

This guide distinguishes the theme's built-in "Increase contrast mode" setting from CSS @media (forced-colors: active) mode so the report is easier to interpret.

Default Admin already includes @media (forced-colors: active) rules for borders, buttons, links, and other components. Test it separately from the theme toggle because the browser, not the theme, controls the final colors in forced-colors mode.

Use Chrome DevTools forced-colors emulation or a real Windows High Contrast environment to verify those system-color overrides.

APCA Polarity Matters

WCAG 2.x contrast ratios are symmetric: swapping foreground and background produces the same numeric ratio. APCA is directional: dark text on a light background and light text on a dark background can produce different signed Lc values for the same two colors.

That means APCA should always be interpreted together with the intended foreground and background roles for a token pair. In this report, APCA threshold checks currently use absolute Lc magnitude, so the summary numbers do not fully expose polarity on their own.

ACCESSIBILITY.md Best Practices Reference

This report aligns with COLOR_CONTRAST_ACCESSIBILITY_BEST_PRACTICES.html from the ACCESSIBILITY.md project. Below are the relevant patterns applied to Default Admin:

WCAG 2.2 Level AA Compliance (Our Baseline)

CSS Custom Properties Pattern (Best Practice from ACCESSIBILITY.md)

Default Admin follows the semantic token pattern recommended in section 8 of the guide:

/* Design token layer (raw colors) */
--accent-base: #015efe;

/* Semantic layer (component usage) */
--gin-color-primary: var(--accent-color-500);
--button-bg-color--primary: var(--accent-color-500);
--button-fg-color--primary: var(--gin-color-button-text);

/* Components reference semantic tokens, not raw colors */
.button--primary {
  background-color: var(--button-bg-color--primary);
  color: var(--button-fg-color--primary);
}

Benefit: Changes to token values cascade across all components automatically, and contrast can be validated at the token layer.

Light/Dark Mode Pattern

Section 8 of ACCESSIBILITY.md recommends using @media (prefers-color-scheme: dark) to define separate tokens for each mode. Default Admin implements this via:

Forced-Colors Mode Support

Section 9 of ACCESSIBILITY.md covers Windows High Contrast and @media (forced-colors: active) rules.

Default Admin includes forced-colors support in multiple components:

APCA — Emerging Next-Generation Standard

Section 10 of ACCESSIBILITY.md covers APCA (Advanced Perceptual Contrast Algorithm), which improves on WCAG 2.x for saturated colors and perceptual accuracy.

Testing and Validation (from Section 12)

This report and theme follow the validation checklist:

Token Derivation Mathematics

This section explains how accent tokens are computed, especially the difference between light mode and dark mode derivation.

Overview

The Default Admin accent system derives an entire ramp of color values (030, 100, 200, ..., 900) from a single --accent-base hex value. The ramp uses CSS color-mix(in lch, ...) to blend colors in the LCH color space (perceptually uniform lightness, chroma, hue). Light mode and dark mode recompute the ramp from different base values, resulting in different hex values for the same token across modes.

Light Mode Derivation

In light mode, the accent system blends toward white:

/* Source: core/themes/default_admin/css/base/accents.css (light mode) */
--accent-base: (user preset, e.g., #015efe for blue)
--accent-mix-base: color-mix(in lch, --accent-base, #ffffff 97%)
  // Result: Very light tint of the accent (98% white blended)

--accent-color-030: color-mix(in lch, --accent-mix-base 85%, --accent-color-base 15%)
--accent-color-100: color-mix(in lch, --accent-mix-base 70%, --accent-color-base 30%)
--accent-color-200: color-mix(in lch, --accent-mix-base 55%, --accent-color-base 45%)
--accent-color-500: --accent-color-base  // Mid-tone, used for primary buttons
--accent-color-900: --accent-color-base  // Darkest, used for hover/active states

--gin-color-primary: --accent-color-500  // In light mode, = --accent-base

Dark Mode Derivation (Different Math!)

Dark mode recomputes the accent system by first transforming the base accent using LCH lightness adjustment, then deriving a new ramp:

/* Source: core/themes/default_admin/css/base/accents.css (dark mode) */
/* Transform accent-base via LCH lightness scaling and black blend */
--accent-color-base: color-mix(
  in lch, 
  lch(from --accent-base calc(l * 20) c h),  // Scale lightness to ~20% of original
  #000000 12%                                  // Blend 12% black
)

/* Then derive ramp from the dark-transformed base (same mix percentages as light) */
--accent-mix-base: color-mix(in lch, --accent-base, #000000 80%)
  // Result: Very dark tint of the accent

--accent-color-030: color-mix(in lch, --accent-mix-base 85%, --accent-color-base 15%)
--accent-color-500: --accent-color-base  // Recomputed dark-mode base
--accent-color-900: color-mix(in lch, --accent-mix-base 15%, --accent-color-base 85%)

--gin-color-primary: --accent-color-500  // In dark mode, ≠ --accent-base (different hex)

Example: Blue Accent (#015efe)

To illustrate, here's what the blue accent derives to in each mode:

Light mode:
  --accent-base: #015efe (original blue)
  --accent-mix-base: ≈ #e8f3ff (very pale blue, 98% white)
  --gin-color-primary (--accent-color-500): #015efe (same as base)
  
Dark mode:
  --accent-color-base: ≈ #0a2c6b (darkened and desaturated via LCH transform)
  --accent-mix-base: ≈ #001a3d (very dark blue, 80% black)
  --gin-color-primary (--accent-color-500): ≈ #0a2c6b (different hex than light mode!)
  
Result:
  Light: White text (#fff) on #015efe needs 4.52:1 contrast ✓
  Dark: Dark text (#1a1a1a) on #0a2c6b needs 5.74:1 contrast (light text fallback ✓)

Why This Matters for Accessibility