How to support dark and light modes in UIkit

As smartphones and laptops nowadays support light and dark modes, you want to support those modes on your website eventually as well. CSS supports those modes with @media rules like @media (prefers-color-scheme: dark) { … } which allows for variations in style. Supporting this on uikit based websites this requires a lot of CSS coding.
Is there a better way to to do this via your stacks and what uikit already offers? If not, are there plans to add some light/dark mode support in the future? One first step could be the Visibility stack, that could make elements visible or hide based on the current mode. Even better would be some additional color choosers in the Customizer stack for background, text, headings, etc. to select the appropriate colors for the respective mode. Also the invers component could be helpful there. Any thoughts?

I actually implemented a solution which allows me to switch the color of lets say a section with the ID “featureSection” based on the OS theme settings from uk-section-default (light theme) to uk-section-secondary (dark theme) using a media query in JS.
Depending on the result of this query I remove one of the classes and add the other to the section.
This is managed by a handler that is added as an event listener to the media query.

// Create the media query list
const prefersDarkMql = window.matchMedia("(prefers-color-scheme: dark)");

// Define the handler event listener
function handlePrefersDark(mql) {
	let section = document.getElementById("featureSection");
	if (mql.matches) {
		section.classList.remove("uk-section-default");
		section.classList.add("uk-section-secondary");
	} else {
		section.classList.remove("uk-section-secondary");
		section.classList.add("uk-section-default");
	}
}

// Call handler once
handlePrefersDark(prefersDarkMql);

// Add the handler as a listener to the media query list
prefersDarkMql.addListener(handlePrefersDark);

It would be nice to wrap that into a Stack to avoid the manual JS code.

So, problem solved, but maybe a feature request towards UIKit Stacks :)

I couldn’t resist and created a rough-cut Stack that basically implements the above mentioned behaviour. It works for Backgrounds and Sections at this point and allows you to choose one of the UIKit themes like Default, Muted, Primary or Secondary for the light and the dark theme. Just give the Background or Section an ID and use it as a reference in the stack. This should be extended to support other UIKit elements like Text, Buttons, etc. It’s just a proof of concept. Have fun …

Updated Stack

3 Likes

Wow nice one! I guess we could use the uk-toggle in this context too.

We can already make a manual switcher of classes using the Toggle stack, so maybe an automated script couldn’t be difficult to implement.

The Toggle example is here: UIkit for RapidWeaver

Cool. That could eventually require even less custom JS coding as it is a built-in UIKit already. I’ll take a look at it and see if I can utilize it.

I did take a look and UIKit should implement a similar concept like Toggle in order to support @media queries on a framework level. The Toggle only supports toggle as a message to change the state while in the case of a media query you would really like to set the state based on the query result.

UIkit.toggle(element).toggle();

But it would be useful to leverage the features of a toggle to switch classes in a target element. Something like

UIkit.mediaQuery(element, options)

would actually be nice as a component which would basically wrap the behaviour of my custom code into a UIkit component. Maybe something they could add in the future.

The other feature that’s needed is some logic to load different images depending on the media query result. Something I have already implemented using some CSS code which hides one out of two images depending on the result. In a perfect world you don’t want to load the not used image at all instead of just hiding it. Not sure if the UIkit image component is already clever enough to support this kind of lazy loading.

@media (prefers-color-scheme: dark) {
	#phone_bright {
		display: none;
	}
}

@media (prefers-color-scheme: light) {
	#phone_dark {
		display: none;
	}
}