Dark Mode Support

Last modified by Thiago Krieck on 2026/10/16 19:50

Description

This proposal was co-authored by Anthropic's Claude Code.

WIP, the umbrella proposal is done. The sub-proposal pages need refinement and publishing. 

What is Dark Mode?

Dark mode is a second color scheme: dark backgrounds with light text. Users pick it in the application, or the application follows the setting of their operating system, which browsers expose as prefers-color-scheme. Operating systems, browsers and most web applications now offer it, so users expect a wiki to do the same.

It matters for three reasons:

  • Comfort: less glare in low light and during long reading or editing sessions.
  • Accessibility: some people with light sensitivity, migraines or certain visual impairments find light interfaces hard or painful to use. For them a dark scheme is a requirement, not a preference.
  • Consistency: a wiki that stays bright while everything around it is dark feels out of place, and users ask for it (11958).

Screenshot 2026-10-14 at 09.09.55.png

Main challenges

  • Dark mode needs its own palette: surfaces get lighter as they get closer to the user, saturated colors are toned down, and each text and background combination must be checked for contrast again.
  • Every color has to come from the theme
  • The choice must be known before the page draws. Otherwise the page flashes light before turning dark.
  • Content we don't control: inline styles in wiki pages, images and diagrams with transparent backgrounds, embedded tools.
  • Customizable themes: every theme an admin creates needs a dark counterpart, or it stays light-only.
  • Twice the testing: each screen and each accessibility check has to pass in both modes.

Dark mode support for XWiki

Summary

At the end of this work XWiki gets dark mode support in two tracks:

  1. A stop-gap: an Iceberg Dark theme (proposal), with each light theme paired with a dark one. A System / Light / Dark preference picks between them. It works with today's styling pipeline and gives users dark mode early, marked as experimental.
  2. The proper solution: a documented set of semantic CSS tokens (--xwiki-*) that every stylesheet reads, so colors switch in the browser without a reload. This is also the token layer Cristal will share.

The stop-gap is designed so that the proper solution reuses it. Theme pairs, the user preference, the theme resolver and Iceberg Dark's palette all carry over. Only the way colors reach the page changes.

To keep each discussion short, the work is split into seven supplementary proposals:

  1. Token naming and register: how CSS variables are named, which ones are public API, and the single file that lists them.
  2. New theme fields: five colors that themes can't set today, such as muted text and borders.
  3. Stop-gap: Iceberg Dark: the dark theme, light/dark theme pairs, the user preference and the administration screens.
  4. Bootswatch themes to contrib: moving the 16 Bootswatch themes out of XWiki Standard, unchanged.
  5. Token renaming and migration: the new name of every current variable, and how extensions keep working during the change.
  6. CSS color migration: moving stylesheets from fixed colors to tokens, module by module.
  7. Full dark mode: colors that switch in the browser without a reload, and a stable API for extensions.

Proposals 1, 5 and 6 build the token layer. Proposals 2, 3 and 4 prepare the themes. Proposal 7 brings both tracks together, and is posted once the core migration is underway. The next section explains which parts are architecture and which are visual design.

Dark mode support and dark themes

Two kinds of work are needed.

  • Dark mode support is architecture. It's the set of mechanisms that let any theme have a dark counterpart and let the page switch between them. It's development work, and it can be built and tested without a final palette.
  • Dark themes are visual design. They're the actual dark colors, checked for contrast and reviewed on real screens. It's design work, and it can start before the mechanisms exist: Iceberg Dark already has.
 Dark mode supportDark themes
What it isToken names and register, theme fields, theme pairs, the theme resolver, the user preference, the token stylesheet, the stylesheet migrationThe dark value of every token, and the rules for colors derived from others
SkillsFront-end and back-end developmentVisual and accessibility design
Reviewed throughPull requests and forum discussionScreenshots on design.xwiki.org, WCAG contrast checks
Done whenEvery color comes from a token and switches without a reloadEvery screen is readable and consistent in dark mode

Where each proposal sits:

ProposalDark mode supportDark themes
1. Token naming and registerNaming, API contract, register 
2. New theme fieldsWhich colors a theme can setRules for derived colors (alert backgrounds, borders, highlights)
3. Stop-gap: Iceberg DarkTheme pairs, resolver, preference, administration screensThe Iceberg Dark palette
4. Bootswatch themes to contrib Which themes ship in XWiki Standard
5. Token renaming and migrationMapping, aliases, deprecation 
6. CSS color migrationMoving stylesheets to tokensVisual review of each migrated module in both modes
7. Full dark modeToken stylesheet, switching in the browser, automated checksDark colors for syntax highlighting and diff views

The dark themes work, listed:

  • Iceberg Dark: already designed (proposal), 25 values with their contrast.
  • The dark default of every token in the register, mostly taken from Iceberg Dark.
  • Rules for derived colors: how much of an intent color goes into its subtle background, and how borders and highlights are mixed with the surface.
  • Dark palettes for syntax highlighting and diff views.
  • What "Create dark version" keeps and changes when it starts a dark theme from a light one.
  • Dark versions of Charcoal, Garden, Kitty and Marina, if we decide to ship them.
  • Visual sign-off of each module in dark mode as the migration moves through it.

The administration and profile screens (mockups) are design work too, but they are the interface of the support side, not part of a theme.

Why dark mode?

What users say

The XWiki Product Survey feedback shows the same needs:

 The lack of dark themes is a problem. Also had "white on white" text/background problems with existing ones. I was about to use Dark Reader, but it made stuff like color pickers render white color as black, so I ditched the idea. Took me a few days to configure theme by hand […] themes do include custom LESS code which overrides variables from other parts of theme editing interface.
 — 1 February 2025

 Implement accessibility in terms of colors. Some elements have too bright colour, which causes bad effect on our eyes. Especially during long working hours.
 — 26 August 2025

 Dark style which includes all elements like input fields.
 — 12 June 2024

 When changing the colours of a theme, it would be nice to see faster, which colour need to be changed for which area.
 — 31 July 2024

The first quote runs into most of the issues this proposal addresses: no dark theme, unreadable combinations in the existing ones, browser extensions breaking parts of the UI, and lessCode overriding the theme fields. The last one is addressed by the theme editor changes in proposal 3.

Main challenges

  • It should not be a color inversion. Dark mode needs its own palette: surfaces get lighter as they get closer to the user, saturated colors are toned down, and each text and background combination must be checked for contrast again.
  • Every color has to come from the theme. One hard-coded color is enough to show a white box or unreadable text, this includes extensions and third-party libraries also.
  • The choice must be known before the page draws. Otherwise the page flashes light before turning dark.
  • Content we don't control: inline styles in wiki pages, images and diagrams with transparent backgrounds, embedded tools.
  • Customizable themes: every theme an admin creates needs a dark counterpart, or it stays light-only.
  • Twice the testing: each screen and each accessibility check has to pass in both modes.

The next section explains where XWiki stands against these challenges.

Context

XWiki's colors come from the active color theme, but most of them are fixed when the stylesheets are compiled on the server. Almost every rule uses those compiled values. CSS variables exist (--body-bg, --text-color…) and have carried the theme's values since 17.9, but only about 54 files read them. So colors can't change in the browser, and a different theme means different compiled CSS.

There is also no agreed list of variables. The 148 CSS variables mostly use Bootstrap names without a prefix and aren't documented as API. Some key colors (muted text, borders, hover background) can't be set from a theme at all. On top of that, the UI stylesheets hard-code about 220 colors (about 105 distinct values, mostly grays), and the 16 Bootswatch themes keep their colors in free-form LESS code. Users have asked for dark mode (11958), and attempts to build one run into these limits (18537). The details are in Appendix: current pipeline.

Issues

  1. Colors are fixed on the server. Almost every rule uses compiled LESS values, so nothing can switch in the browser.
  2. Hard-coded colors and compile-time color functions (~220 hard-coded colors in UI stylesheets, ~125 color functions) ignore the theme entirely.
  3. No naming convention or API contract. The variables mostly use unprefixed Bootstrap names, with a few --xwiki-* and --cr-* ones, and none of them are documented as API. Four are used but never defined (--brand-error, --navbar-default-border, --panel-default-heading-bg, --table-cell-padding).
  4. Key colors can't be set from a Flamingo theme. Muted text (31 uses), the default border (16), the hover background (9), the secondary background and the panel header background come from the legacy color theme or fixed defaults. A dark theme can only override them through lessCode.
  5. Fields declared twice, in the theme class and in variablelist.vm.
  6. The Bootswatch themes keep their colors in lessCode, so they can't get a dark version and are costly to maintain.
  7. No notion of light or dark in the theme administration or the user preferences.

Goals and out-of-scope items for this proposal

Goals

  • A System / Light / Dark preference that works across the core UI.
  • A documented, versioned --xwiki-* token API that extensions and Cristal can rely on.
  • WCAG AA contrast for Iceberg and Iceberg Dark.

Out-of-scope

  • A new skin, or the Cristal migration itself.
  • Dark versions of the Bootswatch themes.
  • Email notifications, print and PDF export (these stay light).
  • User content: inline styles, images and diagrams with transparent backgrounds. Pages that set their own colors are adapted by their authors.
  • The CKEditor editing area, previews and other embedded frames (a known limit).
  • High-contrast modes (prefers-contrast, Windows forced-colors), for now. The token layer makes them possible later.
  • Changes to the Bootstrap source shipped with XWiki.

Proposed solution

The work is split into seven supplementary proposals, each discussed in its own thread. This page only gives the overview; the details are in each proposal. Some items are plain fixes that can be done right away: each proposal marks its own.

Supplementary proposals

#ProposalWhat it decidesDepends on
1Token naming and register--xwiki- prefix, semantic and component tiers, private names, API contract and lifecycle, and a JSON register (Design Tokens format) as the single list of tokens—
2New theme fieldsFive new theme fields, and which colors are derived instead—
3Stop-gap Iceberg DarkIceberg Dark (proposal), light/dark theme pairs, the System / Light / Dark preference, one theme resolver, and the Themes page, editor and profile screens (mockups)2
4Bootswatch themes to contribMoving the 16 Bootswatch themes out of XWiki Standard, with an upgrade path—
5Token renaming and migrationThe new name of every current variable, aliases, theme fields kept as they are, LESS variables, Cristal, deprecation timeline1
6CSS color migrationMoving stylesheets from compiled values to tokens, module by module, mostly scripted5
7Full dark modeThe token stylesheet for a theme pair, colors that switch in the browser, dark code and diff colors, checks, and the stable API for extensions3, 4, 6

Sequencing

dark-mode-sequencing.png

  • Two tracks run in parallel: tokens (1, 5, 6) and themes (2, 3, 4).
  • 2 and 4 depend on nothing, so they can be discussed first.
  • Full dark mode needs both tracks.
  • The token track keeps visual changes small on the themes shipped in XWiki Standard. It's useful even without dark mode.

Approval plan

  • Where: this page on design.xwiki.org is the umbrella reference. Each supplementary proposal is written to be posted as is in its own forum.xwiki.org thread. Proposal 7 is posted once the core migration is underway.
  • Order: post in the numbered order. 2 and 4 can go at any time.
  • Before posting 1 and 5: agree the naming, the register and the Cristal aliases with the Cristal team.
  • Fixes that can be done right away: marked in each proposal. They go straight to JIRA and pull requests.
  • Tracking: an umbrella JIRA issue, with one sub-task per supplementary proposal.

Risks and mitigations

RiskEffectMitigation
The stop-gap shows rough edgesUsers judge dark mode by itMarked experimental; off by default on upgraded wikis ("Single theme" mode); its bug reports set the migration order
Iceberg and Iceberg Dark drift apartLight and dark look inconsistentChange both in the same pull request; compare screenshots in pairs
Bootswatch themes leave XWiki StandardTheir users get no dark modeThe contrib extension keeps them working; release notes point to Iceberg Dark; the themes adapt at their own pace
Admin themes that set colors in lessCodeNo dark pair; values follow only through the sync blockSync block kept while lessCode is supported; release notes on moving colors to fields
Extensions use the old variable namesTheir styles stop applyingAliases for a release cycle; migration guide and script
Admins' skin extensions set old variable namesTheir overrides stop applying, since aliases only help code that reads the old namesRelease notes and documentation list each old name with its replacement
darken() / lighten() conversions aren't identicalSmall shifts in hover and active colorsExplicit tokens; screenshot review
Raw colors keep being addedMigrated modules regressStylelint rules become errors per migrated module
Browser support for relative color syntax and color-mix()Broken colors in older browsersCheck against XWiki's supported browsers first
Scope grows (Cristal migration, Bootstrap upgrade)The work stallsDark mode needs only the tokens; Bootstrap stays untouched

Alternatives considered

AlternativeWhy not
A new skinMost hard-coded colors are in module stylesheets that load whatever the skin is. A third UI to maintain, dropped when Cristal arrives
Convert the Bootswatch themes to theme fieldsWeeks of visual review, and the themes would still have no dark version
Ship only the stop-gapA dark theme can't switch in the browser, and it misses hard-coded and prebuilt CSS
Go straight to tokens, no stop-gapUsers wait until the core migration is done before seeing any dark mode

Open questions

Each supplementary proposal lists its own. These are about the whole:

  • Can admins set a wiki-wide default color mode only, or also per space?
  • Do Charcoal, Garden, Kitty and Marina get dark versions in XWiki Standard, or only Iceberg?

References

Appendix: current pipeline

How a color theme reaches the page

A Flamingo color theme reaches the page through three channels:

ChannelBuilt byReadsCan change in the browser
CSS variables (--body-bg…)cssVariablesInit.css, then a :root block compiled at the end of variablesInit.vm, which wins (XWIKI-23080, 17.9)defaults, theme fields, theme lessCodeYes
Compiled LESS colors (@body-bg…)style.less.vm, LESS skin extensions, webjar .lessdefaults, theme fields, theme lessCodeNo: fixed per theme on the server and cached
$theme Velocity mapcolorThemeInit.vmcompiled LESS output, read back with a regexNo

dark-mode-styling-pipeline.png

Since 17.9 the channels agree on values. But nearly every rule still uses the compiled @variables, so most colors can't change in the browser, and overriding a CSS variable changes almost nothing (forum topic 18537).

Numbers (xwiki-platform, tracked files)

WhatCountMain locations
References to LESS color variables~380Flamingo 234, tree 36, menu 34, like 15, CKEditor 13
Hard-coded colors in UI stylesheets~220 occurrences (~190 hex and rgb, 30 named such as white and black), ~105 distinct valuesweb-war 91, tree 38, Flamingo 14, web templates 13, panels 11; mostly near-duplicate grays (#fff ×12, #ccc ×10, #e8e8e8 ×7), 57 values used once
Inline styles with colors2 in the UI itselfcommentsinline.vm and macros.vm. Not counted: 20 theme previews in the theme administration (intended), ~270 examples in the XWiki Syntax help pages (page content), 6 in the share page email
Hard-coded colors not migrated~290 occurrencesBootstrap source 196 (overridden, not edited), theme and token definitions 73, email and print 23
LESS color functions (darken, fade, contrast…)~125Bootstrap 80, Flamingo 44
Files already using var(--…)~54node, web-war, Flamingo
lessCode in Bootswatch themes900–1,500 lines eachall 16 Bootswatch themes
CSS variables defined148~140 Bootstrap-style names without a prefix, 3 --xwiki-*, plus 63 --cr-* from Cristal

Theme fields

A Flamingo color theme is a wiki page holding a FlamingoThemesCode.ThemeClass object (since 6.2, XWIKI-10212): 63 single values plus the free-form lessCode. The same list is declared again in the $flamingoColorThemeVariables map of variablelist.vm (since 17.3, XWIKI-22667), which is what the generators actually read. The two lists have already drifted apart.

Previous discussions


 


Get Connected