Dark Mode Support
- XWiki
- Feature
- Idea
https://forum.xwiki.org/t/css-best-practive-generalize-on-hardcoding-rule/13224
https://forum.xwiki.org/t/only-support-3-color-themes-in-xs/14015
https://forum.xwiki.org/t/automatic-dark-mode-based-on-system-settings/11958
https://forum.xwiki.org/t/trying-to-develop-a-dark-mode-how-to-disable-color-theme-at-all/18537
- No
Description
This proposal was co-authored by Anthropic's Claude Code.
- What is Dark Mode?
- Dark mode support for XWiki
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).

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:
- 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.
- 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:
- Token naming and register: how CSS variables are named, which ones are public API, and the single file that lists them.
- New theme fields: five colors that themes can't set today, such as muted text and borders.
- Stop-gap: Iceberg Dark: the dark theme, light/dark theme pairs, the user preference and the administration screens.
- Bootswatch themes to contrib: moving the 16 Bootswatch themes out of XWiki Standard, unchanged.
- Token renaming and migration: the new name of every current variable, and how extensions keep working during the change.
- CSS color migration: moving stylesheets from fixed colors to tokens, module by module.
- 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 support | Dark themes | |
|---|---|---|
| What it is | Token names and register, theme fields, theme pairs, the theme resolver, the user preference, the token stylesheet, the stylesheet migration | The dark value of every token, and the rules for colors derived from others |
| Skills | Front-end and back-end development | Visual and accessibility design |
| Reviewed through | Pull requests and forum discussion | Screenshots on design.xwiki.org, WCAG contrast checks |
| Done when | Every color comes from a token and switches without a reload | Every screen is readable and consistent in dark mode |
Where each proposal sits:
| Proposal | Dark mode support | Dark themes |
|---|---|---|
| 1. Token naming and register | Naming, API contract, register | |
| 2. New theme fields | Which colors a theme can set | Rules for derived colors (alert backgrounds, borders, highlights) |
| 3. Stop-gap: Iceberg Dark | Theme pairs, resolver, preference, administration screens | The Iceberg Dark palette |
| 4. Bootswatch themes to contrib | Which themes ship in XWiki Standard | |
| 5. Token renaming and migration | Mapping, aliases, deprecation | |
| 6. CSS color migration | Moving stylesheets to tokens | Visual review of each migrated module in both modes |
| 7. Full dark mode | Token stylesheet, switching in the browser, automated checks | Dark 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
- Colors are fixed on the server. Almost every rule uses compiled LESS values, so nothing can switch in the browser.
- Hard-coded colors and compile-time color functions (~220 hard-coded colors in UI stylesheets, ~125 color functions) ignore the theme entirely.
- 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).
- 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.
- Fields declared twice, in the theme class and in variablelist.vm.
- The Bootswatch themes keep their colors in lessCode, so they can't get a dark version and are costly to maintain.
- 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
| # | Proposal | What it decides | Depends on |
|---|---|---|---|
| 1 | Token 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 | — |
| 2 | New theme fields | Five new theme fields, and which colors are derived instead | — |
| 3 | Stop-gap Iceberg Dark | Iceberg Dark (proposal), light/dark theme pairs, the System / Light / Dark preference, one theme resolver, and the Themes page, editor and profile screens (mockups) | 2 |
| 4 | Bootswatch themes to contrib | Moving the 16 Bootswatch themes out of XWiki Standard, with an upgrade path | — |
| 5 | Token renaming and migration | The new name of every current variable, aliases, theme fields kept as they are, LESS variables, Cristal, deprecation timeline | 1 |
| 6 | CSS color migration | Moving stylesheets from compiled values to tokens, module by module, mostly scripted | 5 |
| 7 | Full dark mode | The token stylesheet for a theme pair, colors that switch in the browser, dark code and diff colors, checks, and the stable API for extensions | 3, 4, 6 |
Sequencing

- 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
| Risk | Effect | Mitigation |
|---|---|---|
| The stop-gap shows rough edges | Users judge dark mode by it | Marked experimental; off by default on upgraded wikis ("Single theme" mode); its bug reports set the migration order |
| Iceberg and Iceberg Dark drift apart | Light and dark look inconsistent | Change both in the same pull request; compare screenshots in pairs |
| Bootswatch themes leave XWiki Standard | Their users get no dark mode | The contrib extension keeps them working; release notes point to Iceberg Dark; the themes adapt at their own pace |
| Admin themes that set colors in lessCode | No dark pair; values follow only through the sync block | Sync block kept while lessCode is supported; release notes on moving colors to fields |
| Extensions use the old variable names | Their styles stop applying | Aliases for a release cycle; migration guide and script |
| Admins' skin extensions set old variable names | Their overrides stop applying, since aliases only help code that reads the old names | Release notes and documentation list each old name with its replacement |
| darken() / lighten() conversions aren't identical | Small shifts in hover and active colors | Explicit tokens; screenshot review |
| Raw colors keep being added | Migrated modules regress | Stylelint rules become errors per migrated module |
| Browser support for relative color syntax and color-mix() | Broken colors in older browsers | Check against XWiki's supported browsers first |
| Scope grows (Cristal migration, Bootstrap upgrade) | The work stalls | Dark mode needs only the tokens; Bootstrap stays untouched |
Alternatives considered
| Alternative | Why not |
|---|---|
| A new skin | Most 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 fields | Weeks of visual review, and the themes would still have no dark version |
| Ship only the stop-gap | A dark theme can't switch in the browser, and it misses hard-coded and prebuilt CSS |
| Go straight to tokens, no stop-gap | Users 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
- User feedback: XWiki Product Survey feedback (2025, 2024)
- JIRA: XWIKI-10212, XWIKI-18169, XWIKI-22667, XWIKI-23080
- Source: cssVariablesInit.css, variablelist.vm, less/style.less.vm, less/variablesInit.vm, colorThemeInit.vm (Flamingo skin resources), templates/stylesheets.vm, FlamingoThemesCode/ThemeClass.xml
Appendix: current pipeline
How a color theme reaches the page
A Flamingo color theme reaches the page through three channels:
| Channel | Built by | Reads | Can 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 lessCode | Yes |
| Compiled LESS colors (@body-bg…) | style.less.vm, LESS skin extensions, webjar .less | defaults, theme fields, theme lessCode | No: fixed per theme on the server and cached |
| $theme Velocity map | colorThemeInit.vm | compiled LESS output, read back with a regex | No |

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)
| What | Count | Main locations |
|---|---|---|
| References to LESS color variables | ~380 | Flamingo 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 values | web-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 colors | 2 in the UI itself | commentsinline.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 occurrences | Bootstrap source 196 (overridden, not edited), theme and token definitions 73, email and print 23 |
| LESS color functions (darken, fade, contrast…) | ~125 | Bootstrap 80, Flamingo 44 |
| Files already using var(--…) | ~54 | node, web-war, Flamingo |
| lessCode in Bootswatch themes | 900–1,500 lines each | all 16 Bootswatch themes |
| CSS variables defined | 148 | ~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
- Trying to develop a dark mode: how to disable color theme at all?: a user hits every one of the limits above.
- Automatic Dark Mode based on System Settings: user demand.
- Only support 3 color themes in XS: a light, dark and high-contrast theme was proposed. Removing themes was contested, and replacing the Bootswatch technology was suggested (XWIKI-18169).
- CSS Best Practice - Generalize on hardcoding rule: which variables count as API.
Thiago Krieck