Skip to content

Default to user prefers-color-scheme value and have switch invert itΒ #17

@jerclarke

Description

@jerclarke

This was already brought up in the comments on the blog post, but since I have a hacky solution, I figured I'd share it here along with an overall request for the block to someone support this by default.

Goal: When user arrives they see the site in dark mode if their browser already has prefers-color-scheme: dark, then have the switch still work to switch it back to light.

Ideally the block would handle this, show the "moon" by default, and clicking to switch it to "sun" would make the site light again.

WHILE WE WAIT WITH BAITED BREATH LOL: I figured out a "clever" CSS solution that gives a similar effect, allowing my site to match the user setting by default, while letting them invert it.

The main issue with my hack is that the sun/moon icons don't change, so if someone has prefers-color-scheme: dark, they'll still see the sun by default and clicking the button "switches to light mode" but that means switching to the sun. IMO this isn't the end of the world, and I'd rather it honors their preference, while still allowing them to invert, than to have the icons working perfectly.

Here's the CSS I put in the style.css of my Twenty Twentyfive child theme (I added the dark colors in the palette section of theme.json):

/* Hide switch by default so old browsers without prefers-color-scheme see nothing */
.wp-block-tabor-dark-mode-toggle {
	display: none;
}
/* In light default mode clicking the switch enables the dark colors */
@media (prefers-color-scheme: light) {
	.wp-block-tabor-dark-mode-toggle {
		display: block;
	}
	:root.theme-dark body {
		--wp--preset--color--base: var(--wp--preset--color--base-dark);
		--wp--preset--color--contrast: var(--wp--preset--color--contrast-dark);
		--wp--preset--color--accent-1: var(--wp--preset--color--accent-1-dark);
		--wp--preset--color--accent-2: var(--wp--preset--color--accent-2-dark);
		--wp--preset--color--accent-3: var(--wp--preset--color--accent-3-dark);
		--wp--preset--color--accent-4: var(--wp--preset--color--accent-4-dark);
		--wp--preset--color--accent-5: var(--wp--preset--color--accent-5-dark);
		--wp--preset--color--accent-6: var(--wp--preset--color--accent-6-dark);
	}
}
/* In dark default the switch is inverted with :not, so the dark colors apply as long as 
the switch is NOT clicked, and clicking the switch reverts the colors to light mode */
@media (prefers-color-scheme: dark) {
	.wp-block-tabor-dark-mode-toggle {
		display: block;
	}
	:root:not(.theme-dark) body {
		--wp--preset--color--base: var(--wp--preset--color--base-dark);
		--wp--preset--color--contrast: var(--wp--preset--color--contrast-dark);
		--wp--preset--color--accent-1: var(--wp--preset--color--accent-1-dark);
		--wp--preset--color--accent-2: var(--wp--preset--color--accent-2-dark);
		--wp--preset--color--accent-3: var(--wp--preset--color--accent-3-dark);
		--wp--preset--color--accent-4: var(--wp--preset--color--accent-4-dark);
		--wp--preset--color--accent-5: var(--wp--preset--color--accent-5-dark);
		--wp--preset--color--accent-6: var(--wp--preset--color--accent-6-dark);
	}
}

How does it work?

  • We only enable our special dark mode when we have access to the prefers-color-scheme
  • When prefers-color-scheme is light, we use the normal CSS expected by this block: Modify all the color presets to use dark variants with the :root.theme-dark body selector.
  • When prefers-color-scheme is dark we invert that logic by using the :root:not(.theme-dark) body selector. This means that "by default" the dark colors will be loaded, but if the switch has been flipped, the dark colors are disabled, thus bringing back the "light" colors.
  • As a point of progressive enhancement, the CSS above also disables the switch entirely by default, and only shows it in the @media (prefers-color-scheme) queries. This is because older browsers without support for prefers-color-scheme will never see any of the dark colors because their browser won't trigger either of the media queries, so it's better to just deny them the switch and leave them with the default colors. 94% of browsers support prefers-color-scheme according to caniuse, so you can remove this if you don't want the hassle lol.

Notably, I think this could also be a solution for someone with a "dark by default" theme, just inverting the logic a few more times. Either way I think the CSS principle of putting all colors in a prefers-color-scheme media query, then using :root:not(.theme-dark), should let anyone control the "inversion" of their color scheme, as long as they don't mind the icons getting mixed up.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions