Hey everyone,
I’d like to propose a plan for dark mode support in XWiki. The full proposal is on design.xwiki.org: it describes the plan in more details, split into seven parts. This post gives an overview of the work, so we can first discuss the overall direction. Each part would then get its own thread for its specific decisions.
Note: This is my first proposal with full on AI help. I tried to keep it concise as much as possible, but I welcome feedback on the overall output of it.
Note 2: While the topic focus on dark mode, this work would also help to have a much improved theming support in XWiki.
Context
Users ask for dark mode from time to time, on the forum and in the product survey. A few things make it hard today:
- Most colors are fixed when the stylesheets are compiled on the server, so a page can’t switch between light and dark.
- Our CSS variables use Bootstrap names and aren’t documented, so extensions can’t really rely on them.
- Some colors, such as muted text and borders, can’t be set from a theme.
- About 220 colors are hard-coded in stylesheets.
- XWiki Standard has no maintained dark theme. Darkly and Cyborg keep their colors in custom LESS code and can’t follow a user’s preference.
Proposed solution
I see two kinds of work:
- Dark mode support, on the architecture side: the mechanisms that would let a theme have a dark counterpart and let the page switch between them.
- Dark themes, on the visual design side: the dark colors themselves. I’ve drafted a first one, Iceberg Dark, as a starting point.
My suggestion would be to do it in two steps:
- A stop-gap, marked experimental: an Iceberg Dark theme, a light and a dark theme per wiki, and a System / Light / Dark choice for each user. It could work with today’s styling. Upgraded wikis would keep a single theme until an admin turns dark mode on.
- Full dark mode: a documented set of CSS tokens (
--xwiki-*) read by the stylesheets, so colors could switch in the browser without a reload. Cristal could use the same tokens. Here’s a good article from Penpot about design tokens: What are design tokens? A complete guide
The idea is that the second step reuses most of the first: theme pairs, the user choice and Iceberg Dark’s colors.
Draft of Iceberg Dark
*A possible way to choose the color mode in the user profile.
Draft Iceberg Dark colors next to Iceberg’s, with their contrast ratio
The Sandbox page with the Iceberg Dark draft
The seven parts of the full proposal, each to be discussed in its own thread:
Required for the stop-gap solution:
(1) gives Iceberg Dark the fields it needs for colors such as muted text and borders, and (2) is the theme itself and admin UIs for setting it.
- New theme fields. Some of the most used colors, such as muted text and the default border, can’t be set from a theme: they come from the old Colibri color themes or from fixed values. A dark theme gets light values for them, or has to override them in LESS code. This part suggests five new fields. Each field is one more value admins set in every theme, light and dark, so we should agree on which colors deserve a field and which can be derived from others.
- Stop-gap with Iceberg Dark. The Iceberg Dark theme, a light and a dark theme per wiki, a System / Light / Dark choice in the user profile, and the administration screens to manage them. It changes how themes are chosen and adds new preferences.
Required for full dark mode support:
- Token naming and register. Our 148 CSS variables use Bootstrap names without a prefix and aren’t documented, so nothing tells extension authors which ones they can rely on. This part suggests a naming convention, which tokens would be public API (with a deprecation policy), and one file that lists them all.
- Bootswatch themes to contrib. The 16 Bootswatch themes keep their colors in about 1,000 lines of custom LESS code each. That code can’t switch between light and dark, and these themes aren’t tested for accessibility. Converting them would take months of visual review, and they still wouldn’t have dark versions. The suggestion is to move them, unchanged, to a contrib extension, so existing wikis keep them. Removing themes from XWiki Standard was discussed before without a decision, so it deserves its own thread.
- Token renaming and migration. A new name for each current variable, with aliases so extensions and customized wikis keep working. It affects extension authors and admins who customized their wiki’s CSS.
- CSS color migration. For colors to switch in the browser, stylesheets have to read tokens instead of compiled values: about 380 references to LESS color variables and 220 hard-coded colors. This part suggests migrating module by module, mostly with a script, with screenshot checks to keep visual changes small. This work from @NicoletaCiausu comes to mind Proposal: Visual Regression Testing in xwiki-platform
- Full dark mode. Once the stop-gap is in place and the core stylesheets read tokens, colors could switch in the browser without a reload and follow the device setting as it changes. It also covers dark colors for code and diff views, and declaring the tokens a stable API. It depends on the migration.
What I’d like your opinion on:
- Do the proposed documented
--xwiki-*tokens sound like the right way for themes and stylesheets to share colors? - Does a stop-gap first, followed by full dark mode, make sense?
- Is this split into seven proposals a good way to discuss it, or would you group things differently?
The details of each part are in the full proposal, and will be discussed in their own threads, provided the overall direction is decided here.
Alternatives
Some other options I looked at:
- A new skin: most hard-coded colors are in module stylesheets that load with any skin, so I don’t think it would solve the problem.
- Only the stop-gap: colors still couldn’t switch in the browser, and hard-coded colors would stay light.
- Only the tokens: users would wait for the whole migration before getting any dark mode.
This post was co-written with Claude, Anthropic’s AI assistant.
Thank you all for reading, I welcome any feedback that you might have.
Thiago



