Currently dark mode is determined solely by the new Admin theme. This means that when the theme is not in use, any dark mode setting is not respected.

This can become disconcerting when navigating between the front-end and back-end of the site. The Navigation Toolbar will be light, and then when navigating to the admin area, it will be dark.

In my opinion, this isn’t only disconcerting, it's sloppy. Drupal is a premium CMS, and our end users need to feel this.

Proposal

Lets move the dark mode setting (which will be set to auto by default) into a module. We’ll also attach various CSS variables there that can be used within Admin user interfaces.

Questions

  1. What module should this functionality live in? System
  2. Should the dark mode setting be per user (I think it should be)? Yes
  3. Where should the UI (per user) for enabling dark mode be? What is the path? The option for the user should live under /user o user/settings?. That page is a mess now, but let's try not to solve all the things in one issue.
  4. Should the UI above include things like accent color, etc? That user UI should not have accent color. At least not as a core feature. That's a whole site/theme feature.
  5. What should the UI look like? Page title: "User settings" or "UI user settings" (for example, not a copy expert). Form > Dark mode toogle

Steps

  1. Get signoff from product managers
  2. Place code in the system module (per @catch)
  3. Place any CSS / JS in core/libraries (per @catch)
  4. Build UI at user/settings

Issue fork drupal-3606655

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mherchel created an issue. See original summary.

gábor hojtsy’s picture

As a Drupal core product manager, I think having first class support for this would be great. Where to put it I'm not sure, it may depend on what other frontend settings we need. Currently the main appearance settings are handled by system module.

kentr’s picture

Issue tags: +Accessibility

My understanding is that some people struggle with one or the other, so I think there's an accessibility piece to it.

+1 to making it per user.

To me, it would be equally disconcerting / sloppy if the Navigation UI is dark while the rest of the front-end page is light.

Of course, themes can opt to get the setting and use it. However, I think it would be misleading to provide the setting when the theme doesn't use it.

So, maybe include a way for themes to indicate support for it and only present the setting when they do, or display a notice that the theme doesn't support it.

mherchel’s picture

However, I think it would be misleading to provide the setting when the theme doesn't use it.

I think we'd need to phrase the description of the checkboxes to indicate what it affects and does not.

include a way for themes to indicate support for it and only present the setting when they do

This is a bad idea IMO, because it takes away functionality from the backend. There are certain users who (content editors) who do the majority of the work in the backend.

tolstoydotcom’s picture

I don't know what "auto by default" means, but please don't force dark mode on people. Here's a starting point:

https://medium.com/@h_locke/why-dark-mode-causes-more-accessibility-issues-than-it-solves-54cddf6466f5

Light mode/day mode should be the default. Those who want dark/night mode can easily select it.

mherchel’s picture

I don't know what "auto by default" means, but please don't force dark mode on people.

It means that Drupal will respect the OS/Browser preferences by default.

kentr’s picture

I just noticed @mgifford's "Light/Dark Mode Accessibility Best Practices" linked from the a11y office hour notes.

The User override pattern section is pertinent to this issue:

User override pattern

Toggle control design

Use a single toggle button that switches between light and dark modes:

  • Visual affordance: Use sun/moon icons to provide clear indication of theme switching functionality
  • Placement: Position in the top-right corner of the header in both desktop and mobile views
  • Scroll behavior: Do not make the toggle fixed/sticky; it should remain in normal document flow within the header
  • Keyboard navigation: Place the toggle in the DOM after navigation/menu items so it appears later in the tab order
  • Language consideration: This guidance assumes left-to-right (LTR) languages
mherchel’s picture

Issue summary: View changes
tobiasb’s picture

Our journey in OpenCulturas with light/darkmode:

We used prefers-color-scheme to set the color for dark-mode.

And I hate it. My system is per default dark, but when a theme is half-baked for a dark-mode, or just not all pages looks good, I want to switch it, without hacking tools.

So I searched for better way and found the contrib module dark_mode_toggle.

We forked the module into OpenCulturas, customized it to allow us to reuse the same code for frontend (block) and backend (Toolbar).

The toggle does also toggle the gin dark/light mode, on some pages I wish I could make an exception. :D

kentr’s picture

What about other UI settings within the Admin theme (accent color, focus color, etc)?

I suggest making that a separate issue.

If we're providing sensible & accessible defaults for light & dark themes, then I view the ability to fine-tune the colors as a lower priority than the ability to control light / dark.

hexabinaer’s picture

First of all: Great move, thanks for emphasizing the importance of an accessibility feature!

If we're providing sensible & accessible defaults for light & dark themes, then I view the ability to fine-tune the colors as a lower priority than the ability to control light / dark.

IMHO it is not a matter of "fine-tuning". Colors convey meaning and they need to have accessible and understandable contrasts, otherwise we sacrifice a strength of Drupal (overall accessibility) for the sake of one related feature. Futhermore, the design is vital for decision-makers, and that includes dark-mode design.

I'd like to share or learnings from making OpenCulturas dark-mode compatible and what should be considered beyond that for moving ahead in core:

Recommendations

  • Promote boldly. Themers need to become aware of the initiative, otherwise their projects might inherit a dark mode from a base theme they hadn't been aware of with the risk of unsatisfied customers.
  • Allow for deliberate user choices. Some might be happy with OS inheritance, others are not. I mean: offer a toggle, do not silently inherit from the OS. Auto-inheritance may become a default in a future release but it's not a good idea for a feature introduction (see above)
  • First, let's deliver a system-wide toggle first ("experimental", allowing to place a block in both front-end and back-end theme). It can help a lot to prepare a theme when you know what CSS class (preferable) to work with
  • Second, core theme maintainers should agree on principles and publish them to help the odd contrib theme developer to adapt these principles
  • Promote the usage of custom properties everywhere. It would be good to have a set of drupal core-prefixed custom properties for colors that any module and theme developer can rely on in the future. Example: many modules still set #efefef or the like as the background color for a field widget. A common --dr-color-background-light property could foster a lot of low-hanging contributions

How we approached it

As @tobiasb stated, we started with prefers-color-scheme in OpenCulturas. We already had defined custom properties for all colors and thus it was a no-brainer to re-fill them with adapted colors under this condition. This had put some pressure on setups that inherited from the base theme (the need to adjust colors inherited from the base theme's dark mode to the brand colors).

Then we moved on to a dark-mode toggle (keeping the front-end default theme and Gin as the back-end theme in sync. This adds the class "dark" to the html tag. With :root.dark { } we are then re-defining the color set, that's it. In one instance I know of, that class was simply globally set because the designer wanted a "dark mode only" theme anyways.

Themers may have opinions about our approach to deliver the dark-mode and any objection may be valid.

TL;DR

My point is: let's define standards. Contributed modules and themes need a little preparation.

This may slow down this initiative a bit but it will save so much time for implementers.

hexabinaer’s picture

  • Placement: Position in the top-right corner of the header ...
  • Language consideration: This guidance assumes left-to-right (LTR) languages

Assuming the latter refers to the first: Using logical properties (inset-block-start, inset-inline-end) instead of physical properties (top, right) would be another standard I'd like to promote. That way we can get rid of [ltr] selectors adding specificity and positioning automatically follows the reading direction.

catch’s picture

mherchel’s picture

@hexabinaer Note that the admin theme (derived from Gin) already supports and implements dark mode. You can see the current variable structure at https://git.drupalcode.org/project/drupal/-/blob/main/core/themes/defaul... (note this will continue to evolve over the next few months).

I suggest making that a separate issue. - @kentr

We need to think about this now. We'll need to figure out the UI and where the toggle(s) are kept.

mherchel’s picture

Issue summary: View changes

Per @catch in https://drupal.slack.com/archives/C079NQPQUEN/p1783429146306869

Wouldn't normally say system module because ideally we'd get rid of it, but in this case, probably system module because it also has all the admin routes including theme config etc.

[ css ] could possibly go in core/libraries instead of system.

Updating IS

mherchel’s picture

Issue summary: View changes
mherchel’s picture

Issue summary: View changes
kentr’s picture

@mherchel,

We need to think about this now. We'll need to figure out the UI and where the toggle(s) are kept.

I was referring to the implementation of other UI settings within the Admin theme (accent color, focus color, etc).

Planning and designing first, definitely. But please don't delay deployment of the dark mode toggle feature on the deployment of a feature for the other UI settings. They're independent features (though related) and for me the dark mode toggle is higher priority.

@hexabiner,

IMHO it is not a matter of "fine-tuning". Colors convey meaning and they need to have accessible and understandable contrasts, otherwise we sacrifice a strength of Drupal (overall accessibility) for the sake of one related feature. Futhermore, the design is vital for decision-makers, and that includes dark-mode design.

It sounds like you thought I was referring to dark mode, or dark mode toggling, as fine-tuning. I wasn't.

I was referring to a user choosing personal values for accent color, focus color, etc as fine-tuning. From Webster:

fine-tune
verb
: to make small changes to (something) in order to improve the way it works or to make it exactly right
fine-tune a TV set
fine-tune the temperature of the room
They're fine-tuning interest rates to achieve economic growth without inflation.

Take heart that Drupal policy states that core changes must pass through the core gates quality gates—which include usability and accessibility—and for accessibility we generally use WCAG 2.2 AA as a standard (even though the accessibility gate still officially declares WCAG 2.1 AA. Some things slip through the cracks, but @mherchel is on the accessibility team (from my POV 🙂).

The accessibility team is already tracking bugs for the new dark theme.

Can't speak for @mherchel, but the reason I voted for per-user selection of dark mode is exactly because of the user choice that you mentioned.

My point is: let's define standards. Contributed modules and themes need a little preparation.

This may slow down this initiative a bit but it will save so much time for implementers

It's not my bailiwick, but I thought there was already an initiative to define standards. IIRC I've seen it discussed.

AFAICT this issue is only about moving the existing controls in the new Default Admin theme out of theme and into a standalone feature.

mgifford’s picture

Great to see this discussion. I love that this is something that we might be able to start providing support for, as this is definitely an accessibility issue (even if it isn't a WCAG issue). It is definitely a best practice.

I would argue for support for a Light/Dark/System mode. Defaulting to the browser default, but over-ruled by user preferences. This could be something stored with a users account, but not all users will have an account, so it should also be something we consider as something we store in a cookie.

I've tried to aggregate some best practices here:
https://github.com/mgifford/accessibility-skills/blob/main/skills/light-...

or, another view of the same content:
https://mgifford.github.io/ACCESSIBILITY.md/examples/LIGHT_DARK_MODE_ACC...

There are so many great guides on light/dark mode, it is hard to keep up.

Every browser and every operating system supports light/dark mode. Users expect it, but it is so poorly done.

It does add complexity to both creating style guides and testing accessibility compliance.

I think partly it is about not just choosing colors, but choosing types of functionality and ideas expressed by the choice of color.

It's done quite well here:
https://www.radix-ui.com/colors
https://github.com/radix-ui/colors

I've developed something like it here:
https://mgifford.github.io/ui-palette-generator/

I tried to move beyond simply looking at the seed value. This code definitely needs work.

But yes, this should be something that is moved out of the Default Admin theme and into something that is supported system-wide.

catch’s picture

This could be something stored with a users account, but not all users will have an account, so it should also be something we consider as something we store in a cookie.

If this is done, the mode will have to be controlled via JavaScript, otherwise it can't be compatible with page caching. I'm not sure we need to do that in core, especially since core won't provide a UI to switch mode for anonymous users.

kentr’s picture

especially since core won't provide a UI to switch mode for anonymous users.

To me, providing this for anonymous users is desirable, so that there's a seamless experience that FE themes can use regardless of whether the user is logged in. For example, the login page should respect the user's preferences also IMO.

We can't make themes use the setting, but since it's a best practice hopefully more and more themes would.

Couldn't it be a theme-able JavaScript widget in a block that FE themes can put in their page header?

mherchel’s picture

Spoke to User Experience maintainer @ckrina via DM in Slack and have some answers to questions:

Where should the UI (per user) for enabling dark mode be? What is the path?

The option for the user should live under /user o user/settings?. That page is a mess now, but let's try not to solve all the things in one issue.

Should the UI above include things like accent color, etc?

That user UI should not have accent color. At least not as a core feature. That's a whole site/theme feature.

What should the UI look like?

The simpler form possible to start:
Page title: "User settings" or "UI user settings" (for example, not a copy expert)
Form > Dark mode toogle

mherchel’s picture

From @kentr

Couldn't it be a theme-able JavaScript widget in a block that FE themes can put in their page header?

I don't think this is in scope for this issue. This issue is specifically about the admin /editorial experience including the navigation sidebar and top bar when logged in and viewing the site's front-end.

I don't think a block helps front-end themes much. It's extremely simple to code a dark mode toggle button, and have it change a CSS class. The hard part is the front-end to have a theme that supports dark mode. And we don't have the capability to do that.

mherchel’s picture

I think if we want to do this for general front-end themes or anonymous sessions, we should do this in a followup, since that dramatically increases scope and might not be realistic (though it might be, too).

mherchel’s picture

Issue summary: View changes

Updating IS.

kentr’s picture

Regarding frontend: Looks like the Dark Mode Toggle module has an issue related to this: #3554120: Add functionality to persist a user's preferred dark mode setting in their account.

I suggested that they coordinate with the setting that we're talking about in this issue.

nevergone’s picture

mherchel’s picture

jurgenhaas’s picture

jurgenhaas’s picture

Assigned: Unassigned » jurgenhaas
jurgenhaas’s picture

@mherchel regarding the location of that user setting:

Where should the UI (per user) for enabling dark mode be? What is the path?

The option for the user should live under /user o user/settings?. That page is a mess now, but let's try not to solve all the things in one issue.

Does that have to be a separate tab in the user profile, or could this be included in the user profile form? I mean, there are already settings about language preference, timezone, and other settings that impact the rendering for each user. Selecting their dark mode preference there would feel like a natural choice. Any objections?

kentr’s picture

Seems like it would be simpler / easier / quicker to put it on the user profile form, also. Doesn't Default Admin already do that when the per-user changes setting is enabled?

jurgenhaas’s picture

Yes, we would build back the settings from default_admin as part of this issue anyway.

mherchel’s picture

Yeah, go the easy route for now. Once its built, I'll run it by the UX maintainers again and they can suggest tweaks.

hexabinaer’s picture

Trying to keep my points concise this time ;-)

We should offer an option to keep front-end and back-end theme color scheme in sync.

Gin sets a class gin--dark-mode and only handles the theme's dark mode. What would be soothing for any theme: define and set a theme-agnostic class (dark|light|auto) with any color scheme toggle.

Of course, we could leave that to respective theme development but then users would have to make their choice for each theme separately and would probably find different, incompatible solutions.

kentr’s picture

@hexabinaer

Yeah, I was pushing for that also. But it was determined out of scope for this issue.

I'm planning to work on it from another angle.

pdureau’s picture

Hello all,

This topic is related to #3517033: Add a style utility API because, from a design system point of view, dark mode is a "special" case (with the browser and the CSS having a specific knowledge) of mode switching.

Example of other modes:

  • branding (for multi-brands designs)
  • event (summer vibe, Halloween...)
  • high/low contrast (and other accessibility alterations...)
  • celebration/mourning
  • ...

Ping @grimreaper

mherchel’s picture

Yeah, I have a feeling once that gets in, we'll need to rework the UI a lot to make things usable, accessible, and beautiful.

In the meantime, we'll proceed with adding the setting to the user settings form per @ckrina in #22.

jurgenhaas’s picture

Status: Active » Needs review

The MR now provides the following parts:

  • The color scheme user setting is handled similarly to timezone and language
  • The user setting gets output as data-color-scheme attribute in the html element, defaulting to auto if no setting is available; the user cache context is added
  • The setting is stored in the user entity, similar to timezone and language; an update hook to add this new field is included
  • The dark-mode settings in default_admin are removed

Here are a couple of questions:

  • Should we also add a <meta name="color-scheme"> to the page header? That would tell the browser to render native UI (form controls, scrollbars) in light or dark. Or should that be delegated to the themes?
  • Should this MR also adjust the CSS and JS part of things, or should that be a follow-up for the frontend experts?

As soon as the approach of this MR gets approved, I'll also add tests to it.

mherchel’s picture

Should we also add a to the page header? That would tell the browser to render native UI (form controls, scrollbars) in light or dark. Or should that be delegated to the themes?

No. I don't want to change native front-end UI elements, because the front-end theme might not support dark mode. Themes can rely on the color-scheme CSS property

Should this MR also adjust the CSS and JS part of things, or should that be a follow-up for the frontend experts?

Lets keep it simple for now, and leave the front-end to a different issue.

mherchel’s picture

Status: Needs review » Needs work

Reviewed while on a call today. Changes needed:

  • "Auto" should be first in the list
  • The label should be changed to "Light / dark mode"
  • Instead of outputting the attribute directly on html_attributes, lets output a color_scheme variable in the html.html.twig, and then do the work in that template.

Also, tests are failing :)

jurgenhaas’s picture

Assigned: jurgenhaas » Unassigned
Status: Needs work » Needs review

Addressed all review items from #42 except for the handling of the html attribute. We've discussed that on Slack:

jurgenhaas

@mherchel regarding the dark mode change to explicitly set the data attribute in Twig templates, it feels a bit awkward now that I look into the required change. Current:

# In the hook
$variables['html_attributes']['data-color-scheme'] = $this->currentUser->getColorScheme();

# In Twig
<html{{ html_attributes }}>Making this explicit would look like this:
# In the hook
$variables['color_scheme'] = $this->currentUser->getColorScheme();

# In Twig
<html{{ html_attributes.setAttritbute('data-color-scheme', color_scheme) }}>

So, instead of setting the attribute directly, we still set a variable at the same place in the code, only to make the Twig code significantly more complex. Really?

mherchel

My thought process is that the themer might want to use the variable in other places

can we change $this->currentUser->getColorScheme() to just color_scheme?

jurgenhaas

$this->currentUser->getColorScheme() is needed to get the value in the hook. And this value is either passed to html_attributes/color_scheme or to color_scheme. For the latter, we then need to do extra work in every html twig template. For the former, nothing else is needed.

I see your point that themers may want to use that elsewhere, but would that even be required if the root element has the data attribute?

mherchel

Maybe? Maybe not. If you feel strongly, you can leave as-is. We can always change stuff around later, or people can do it in proprocess

I left it for now as the code is significantly simpler, and the other use case is (a) unclear and (b) may be achievable anyway.

Also, all tests are now green and this issue is ready for another review.

pdureau’s picture

I agree $variables['html_attributes']['data-color-scheme'] is too specific. This attributes will depend of the design (system) implementation in the theme.

Examples:

Sometimes this attribute or class can be added only on specific elements, like HTML or BODY, HTML, sometimes anywhere.

That's why I believe ticket must be done and merged hand-to-hand with #3517033: Add a style utility API. Here, we manage:

  • User specific value, extending User/Account/Session API and managing the related cache
  • Specific settings for admin_theme

In #3517033: Add a style utility API, we miss the User specific part, but we manage:

  • declaration of modes in theme (with plugin definition): the values, the meta, the renderable alterations...
  • in a more generic way (not only dark/light)
kentr’s picture

I agree with everyone else about the importance of exposing the setting to frontend themes, with @pdureau about the value of making it more agnostic, and with @mherchal and @jurgenhaas that it can be done later and that there's a workaround available (aka, preprocess, possibly even twig).

I opened #3613964: [PP-1] Make dark mode a first class citizen of Drupal core, Part 2 for making those improvements.

jurgenhaas’s picture

In my view, the dark-mode setting is different from all other theme and style settings: it is a global preference that's important for both the admin and the default theme. That's not the case for other preferences. And whether a theme (again, either admin or default, i.e. frontend) will leverage that data attribute or not, is not being enforced.

Therefore, the narrow scope of this issue seems like what would be an important improvement to core in its own right.