WordPress.org

WordPress Developer Blog

Building a light/dark toggle with the Interactivity API

Building a light/dark toggle with the Interactivity API

“How can I build a light/dark toggle for my theme?” 

It’s one of the most-asked questions that I get from theme authors in the block theme space. There’s even an ongoing Gutenberg GitHub ticket requesting it as a feature. 

But it’s not necessarily the simplest problem in the world to solve. If it was, we would’ve long ago settled on a standard practice for handling light and dark color scheme toggles. And there would be no need for multiple plugins with their own spin on it.

Fortunately, browser vendors have done a lot of the work for us, particularly in the past year. There are CSS standards that we can use at this very moment to build WordPress themes that conform to the user’s OS preferences. Much of this is outlined in a previous article I published on light and dark mode styles

In the time since I wrote that initial tutorial, I wanted to explore taking it to the next level. Light and dark color schemes are relatively easy to implement in modern web development, but can we build a button to toggle between them?

I took it as a personal challenge to build a light/dark toggle that:

  • Doesn’t require a custom block
  • Implements modern CSS techniques
  • Uses standard WordPress hooks and APIs
  • And, most importantly, can be bundled with any block theme

So let me walk you through how I built a modern light/dark toggle using the WordPress Interactivity API. 

While it’s not required, I highly encourage reading the original article on mastering light and dark mode styling, which is the foundation of this followup tutorial.

Theme setup

For this tutorial, I decided to stick with a child of the default Twenty Twenty-Four theme as an example. It’s what I used in the original tutorial, so it made sense to build from there.

To start, create a new tt4-dark-mode folder in the wp-content/themes directory of your WordPress installation. Then, go ahead and set up a few empty files following this exact folder structure:

tt4-dark-mode/
├── functions.php
├── package.json
├── public/
├── resources/
│   ├── js/
│   │   ├── color-scheme.js
│   │   └── editor.js
│   └── scss/
│       └── core-button.scss
├── style.css
├── theme.json
└── webpack.config.js

You’ll be using each of those as you follow this tutorial. I would advise against deviating from this until you’ve learned how this technique works.

Now add some basic theme file header fields in your style.css file so that WordPress recognizes it as a valid theme:

/*!
 * Theme Name:        TT4: Dark Mode
 * Description:       Exploring dark mode with Twenty Twenty-Four.
 * Version:           1.0.0
 * Template:          twentytwentyfour
 * Text Domain:       tt4-dark-mode
 * Tested up to:      6.8
 * Requires at least: 6.6
 * Requires PHP:      8.0
 */

Installing npm packages

For this tutorial, you’ll need to install a few JavaScript packages. To get started with that, insert the following snippet into your package.json file, which will add start and build commands that you’ll use later:

{
	"name": "tt4-dark-mode",
	"scripts": {
		"start": "wp-scripts start --webpack-src-dir=resources --output-path=public --experimental-modules",
		"build": "wp-scripts build --webpack-src-dir=resources --output-path=public --experimental-modules"
	}
}

With that in place, open your command line application on your computer and navigate to your wp-content/themes/tt4-dark-mode directory. You’ll first install some development dependencies with this command:

npm install @wordpress/scripts webpack-remove-empty-scripts --save-dev

For a deeper dive into how these scripts work, check out the Build Process documentation in the Theme Handbook.

Then install some WordPress dependencies that you’ll use for your own scripts later:

npm install @wordpress/blocks @wordpress/i18n @wordpress/interactivity @wordpress/primitives

The above isn’t strictly necessary because use of these modules will automatically be converted to the wp JavaScript global during the build process. But it will ensure your IDE recognizes them.

Configuring webpack

The next step in the process is configuring webpack to handle scripts and stylesheets. I’ve covered this process in depth in an article on using the WordPress scripts package with themes. But the format has changed slightly because you’ll be working with the Interactivity API and creating a script module.

Add this code to your webpack.config.js file:

const [scriptConfig, moduleConfig] = require('@wordpress/scripts/config/webpack.config');
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
const path = require('path');

module.exports = (() => {
	return [
		{
			// Includes the default webpack config from WordPress.
			...scriptConfig,
			...{
				entry: {
					'js/editor': path.resolve(process.cwd(), 'resources/js', 'editor.js'),
					'css/core-button': path.resolve(process.cwd(), 'resources/scss', 'core-button.scss')
				},
				plugins: [
					...scriptConfig.plugins,
					new RemoveEmptyScriptsPlugin({
						stage: RemoveEmptyScriptsPlugin.STAGE_AFTER_PROCESS_PLUGINS
					})
				]
			}
		},
		{
			// Includes the default webpack config from WordPress.
			...moduleConfig,
			...{
				entry: {
					'js/color-scheme': path.resolve(process.cwd(), 'resources/js', 'color-scheme.js')
				}
			}
		}
	]
})();

Now run the start command from your computer’s command line application:

npm run start

Adding support for the user-preferred color scheme

By default, the Twenty Twenty-Four theme has a light color scheme that looks like this:

In the previous tutorial on light and dark mode styling, I walked you through using the CSS light-dark() function and color-scheme properties to make the theme’s color scheme adhere to the user preference defined via their OS. You’ll do the same thing here.

First, open your empty theme.json file and add the following properties:

{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 3,
	"settings": {},
	"styles": {}
}

Next, you will define two properties under settings.color. The first is to disable custom colors by setting settings.color.custom to false

The next is to define a color palette using the CSS light-dark() function for each of the color values. light-dark() accepts two color values with the first being the color to use in light mode and the second being the color for dark mode.

I’ve already defined a palette for this child theme, so you can copy and paste it over the settings property in your theme.json file:

"settings": {
	"color": {
		"custom": false,
		"palette": [
			{
				"color": "light-dark(#f9f9f9, #1c1c1c)",
				"name": "Base",
				"slug": "base"
			},
			{
				"color": "light-dark(#ffffff, #111111)",
				"name": "Base / Two",
				"slug": "base-2"
			},
			{
				"color": "light-dark(#111111, #ffffff)",
				"name": "Contrast",
				"slug": "contrast"
			},
			{
				"color": "light-dark(#636363, #888888)",
				"name": "Contrast / Two",
				"slug": "contrast-2"
			},
			{
				"color": "light-dark(#A4A4A4, #3d3d3d)",
				"name": "Contrast / Three",
				"slug": "contrast-3"
			},
			{
				"color": "light-dark(#cfcabe, #60544c)",
				"name": "Accent",
				"slug": "accent"
			},
			{
				"color": "light-dark(#c2a990, #9d7359)",
				"name": "Accent / Two",
				"slug": "accent-2"
			},
			{
				"color": "#d8613c",
				"name": "Accent / Three",
				"slug": "accent-3"
			},
			{
				"color": "light-dark(#b1c5a4, #465a3b)",
				"name": "Accent / Four",
				"slug": "accent-4"
			},
			{
				"color": "light-dark(#b5bdbc, #4d5757)",
				"name": "Accent / Five",
				"slug": "accent-5"
			}
		]
	}
},

You also need to let the browser know that this theme supports both light and dark modes by setting the color-scheme property to light-dark on the :root element. Do this by adding the following css field to the styles property in theme.json:

"styles": {
	"css": ":root { color-scheme: light dark; }"
}

Once you’ve saved, you should be able to freely switch between light and dark mode in your operating system preferences, and the theme will adhere to your settings. For example, here is what the theme looks like when the OS is set to dark mode:

Building a light/dark toggle button

The previous section was a quick overview of what I’d already covered in the earlier tutorial, but it provides the necessary foundation for building a toggle button. After all, you can’t actually toggle between light and dark mode without both color schemes defined!

In this section, you will learn how to store the light/dark state, build a button for toggling, style it, and register a block variation for it.

Developing a storage mechanism for the light/dark state

For a light/dark toggle to be effective, you must have a method of storing which mode the user has chosen. They’ll likely expect to see the same color scheme when they visit another page on the site or come back later.

There are two good methods for doing this:

  • User meta: For storing the state when a user has an account and is logged in.
  • Cookies: For storing the state when a user is not logged in.

This tutorial uses a combination of both techniques, but you can choose one or the other if you prefer. You could also rewrite the code to use localStorage, but this data is not available on the server, so you wouldn’t have access to it via PHP.

The first thing to do is register some user meta using the WordPress register_meta() function. Add this code to your functions.php file:

add_action( 'init', 'tt4_dark_mode_register_user_meta' );

function tt4_dark_mode_register_user_meta() {
	$sanitize = fn( $value ) => in_array( $value, ['light', 'dark'], true ) ? $value : '';

	register_meta( 'user', 'tt4-dark-mode-color-scheme', [
		'label'             => __( 'Color Scheme', 'tt4-dark-mode' ),
		'description'       => __( 'Stores the preferred color scheme for the site.', 'tt4-dark-mode' ),
		'default'           => '',
		'sanitize_callback' => $sanitize,
		'show_in_rest'      => true,
		'single'            => true,
		'type'              => 'string'
	] );
}

There’s a few things to note about this code:

  • The meta key is tt4-dark-mode-color-scheme, which you’ll use below to access the data (note: this is also the cookie name that you’ll use).
  • The show_in_rest parameter must be true for this to be accessed via the REST API.
  • The sanitize_callback function only accepts values of light and dark.

Let’s build a couple of helper functions for accessing the light/dark state. The first is tt4_dark_mode_get_color_scheme(). It will check both user meta (for logged-in users) and cookies to determine whether the user has a saved light/dark preference. Otherwise, it returns light dark, which is the default scheme for the theme.

Add this code to your functions.php file:

function tt4_dark_mode_get_color_scheme() {
	$key = 'tt4-dark-mode-color-scheme';
	$valid_schemes = [ 'light', 'dark' ];

	if ( is_user_logged_in() ) {
		$scheme = get_user_meta( get_current_user_id(), $key, true );

		if ( $scheme && in_array( $scheme, $valid_schemes, true ) ) {
			return $scheme;
		}
	}

	if ( isset( $_COOKIE[ $key ] ) ) {
		$scheme = sanitize_key( wp_unslash( $_COOKIE[ $key ] ) );

		if ( $scheme && in_array( $scheme, $valid_schemes, true ) ) {
			return $scheme;
		}
	}

	return 'light dark';
}

Next, let’s create a simple conditional function named tt4_dark_mode_is_dark_scheme(). This function determines whether the current color scheme is dark. If the scheme can’t be determined, it returns null.

Add this code to functions.php:

function tt4_dark_mode_is_dark_scheme() {
	$scheme = tt4_dark_mode_get_color_scheme();

	return match( $scheme ) {
		'dark'   => true,
		'light'  => false,
		default  => null
	};
}

Creating the toggle button

The next stage of this process is building an actual button that can be toggled via the Interactivity API. To do this, you first need to add interactivity support to the WordPress Button block.

Add this filter to your functions.php file:

add_filter( 'block_type_metadata_settings', 'tt4_dark_mode_block_type_metadata_settings' );

function tt4_dark_mode_block_type_metadata_settings(array $settings) {
	if ( 'core/button' === $settings['name'] ) {
		$settings['supports']['interactivity'] = true;
	}

	return $settings;
}

The real trick to this entire tutorial is this next moment. 

If you didn’t know, WordPress allows you to use a <button> element for its Button block rather than the default link element, assuming you do so via Code Editor mode. This was added via a pull request two years ago. It’s not exactly visible because an HTML element selector was never added in the UI (coming in WordPress 6.9).

Regardless, you can use this to create actual <button> tags, which means there is a world of possibilities for creating interactive features in WordPress block themes. In this case, an interactive light/dark toggle!

Next, you’ll filter the Button block to do four things:

  • Determine whether it has the toggle-color-scheme class and an inner <button> element.
  • Add some interactivity directives to the <button>, which are HTML attributes for adding behavior via the Interactivity API. This lets you manage state, side effects, events and more.
  • Setting an initial interactive state via wp_interactivity_state(), which passes necessary data to the script that you’ll be building in the next section.
  • Enqueue scripts that you’ll use later to handle interactivity.

Much of this work happens using the WP_HTML_Tag_Processor class, which makes the process of altering HTML a breeze.

Add this code to your theme’s functions.php file:

add_filter( 'render_block_core/button', 'tt4_dark_mode_render_button', 10, 2 );

function tt4_dark_mode_render_button( string $content, array $block ) {
	$processor = new WP_HTML_Tag_Processor( $content );

	// Determine if this is a light/dark toggle button.
	if (
		! $processor->next_tag( [ 'class_name' => 'toggle-color-scheme' ] )
		|| ! $processor->next_tag( 'button' )
	) {
		return $processor->get_updated_html();
	}

	// Add interactivity directives to the `<button>`.
	$attr = [
		'data-wp-interactive'           => 'tt4-dark-mode/color-scheme',
		'data-wp-on--click'             => 'actions.toggle',
		'data-wp-init'                  => 'callbacks.init',
		'data-wp-watch'                 => 'callbacks.updateScheme',
		'data-wp-bind--aria-pressed'    => 'state.isDark',
		'data-wp-class--is-dark-scheme' => 'state.isDark'
	];

	foreach ( $attr as $name => $value ) {
		$processor->set_attribute( $name, $value );
	}

	// Set the initial interactivity state.
	wp_interactivity_state( 'tt4-dark-mode/color-scheme', [
		'colorScheme'       => tt4_dark_mode_get_color_scheme(),
		'isDark'            => tt4_dark_mode_is_dark_scheme(),
		'userId'            => get_current_user_id(),
		'name'              => 'tt4-dark-mode-color-scheme',
		'cookiePath'        => COOKIEPATH,
		'cookieDomain'      => COOKIE_DOMAIN
	] );

	// Enqueue scripts.
	if ( is_user_logged_in() ) {
		wp_enqueue_script( 'wp-api-fetch' );
	}

	$script = include get_theme_file_path( 'public/js/color-scheme.asset.php' );

	wp_enqueue_script_module(
		'tt4-dark-mode-color-scheme',
		get_theme_file_uri( 'public/js/color-scheme.js' ),
		$script['dependencies'],
		$script['version']
	);

	return $processor->get_updated_html();
}

There’s a lot to process in that function, which could be covered in an entire tutorial in and of itself. I encourage reading the Interactivity API documentation for a full overview of what’s going on.

Let’s just give this a quick test to see what the current version of the toggle button looks like. Copy this block markup and paste it into the Code Editor for a post or page:

<!-- wp:buttons -->
<div class="wp-block-buttons">
	<!-- wp:button {"tagName":"button","className":"toggle-color-scheme"} -->
	<div class="wp-block-button toggle-color-scheme">
		<button type="button" class="wp-block-button__link wp-element-button">Toggle Color Scheme</button>
	</div>
	<!-- /wp:button -->
</div>
<!-- /wp:buttons -->

You’ll notice that both of the things we checked in the previous PHP filter: the toggle-color-scheme class and the <button> element.

If you switch back to the Visual Editor, you’ll see this:

If you try to test this button on the front end, it won’t actually toggle the color scheme yet. That’s the next step.

Making the toggle button interactive

Now for the moment you’ve been waiting for: toggling between light and dark mode!

It’s time to switch over to writing a tiny bit of JavaScript code. The Interactivity API makes this pretty easy, even for a hardened PHP developer like myself. First, open your resources/js/color-scheme.js file and import store from the @wordpress/interactivity package:

import { store } from '@wordpress/interactivity';

Next, you need a constant that will check whether the user prefers a dark color scheme using window.matchMedia():

const prefersDarkScheme = window.matchMedia( '(prefers-color-scheme: dark)' );

This constant is necessary for determining the color scheme when a user has not yet clicked the light/dark toggle button. You’ll also use it later to listen for changes.

For working with the Interactivity API, you’ll call its store() function. Add this code to the same file:

const { callbacks, state } = store( 'tt4-dark-mode/color-scheme', {
	state: {},
	actions: {},
	callbacks: {}
});

You’ll notice that there are empty properties of state, actions, and callbacks. Each of these are needed for defining how the toggle button works. Below, we’ll walk through each together.

For state, there’s only one thing you need: a function for determining whether the user is logged in. On the server, we passed this data into wp_interactivity_state() as userId. Therefore, we only need to check if this value is greater than 0.

Add this code to the state property:

state: {
	get isLoggedIn() {
		return 0 < state.userId;
	}
}

Next, you will define the actions property. Action functions are triggered when an event happens. In this case, you need a toggle() action that toggles the current light/dark state. Just like on the server, this code will store the user’s preference as either user meta (if logged in) or a cookie.

Add this code to the actions property in your file:

actions: {
	toggle() {
		state.isDark      = ! state.isDark;
		state.colorScheme = state.isDark ? 'dark' : 'light';

		// If the user is logged in, set the value as
		// user meta instead of a cookie.
		if ( state.isLoggedIn ) {
			wp.apiFetch( {
				path: `/wp/v2/users/${state.userId}`,
				method: 'POST',
				data: {
					meta: {
						[state.name]: state.colorScheme
					}
				}
			} );
			return;
		}

		// Define the cookie path and domain.
		let path   = state.cookiePath || '/';
		let domain = state.cookieDomain ? "; domain=" + state.cookieDomain : '';

		// Save preference to a cookie.
		document.cookie = `${state.name}=${state.colorScheme};path=${path}${domain}`;
	}
}

Take note that the code uses several state properties, which were passed in via the initial call to wp_interactivity_state() from your earlier PHP code.

Now you’ll define two functions for the callbacks property:

  • init(): Executes when the node is first created and ensures that state.isDark is defined.
  • updateScheme(): Runs whenever the state of the toggle button changes and sets the color-scheme property on the :root element.

Add this code to your callbacks property in the file:

callbacks: {
	init() {
		if ('dark' === state.colorScheme || 'light' === state.colorScheme) {
			return;
		}

		state.isDark = prefersDarkScheme.matches;
	},
	updateScheme() {
		document.documentElement.style.setProperty(
			'color-scheme',
			state.colorScheme
		);
	}
}

There is one other scenario that we must account for. Whenever a user has not yet clicked the toggle button, it’s possible for them to change their OS light/dark preference. Because the toggle button has already been initialized at this point, our code doesn’t know about it.

To handle this edge case, add an event listener to prefersDarkScheme and re-run the callbacks.init() function. Add this code at the end of your resources/js/color-scheme.js file:

prefersDarkScheme.addEventListener( 'change', () => {
	callbacks.init();
} );

Now you can finally test the button on the front end of your site. When you click it, you’ll switch between light and dark mode!

You could very well stop reading this tutorial right now and go your own way. But I encourage you to stick around for a couple of more nice-to-have customizations.

Styling the toggle button

The light/dark toggle is functionally useful at this point, but it looks just like any other ol’ button on your website. Let’s make it a little nicer, primarily by adding icons for displaying the current state.

The icons used here are from Google’s Material Icons library, but feel free to replace them with your own.

Open your theme’s resources/scss/core-button.scss file and add this code:

$light_svg: '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-360q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35Zm0 80q-83 0-141.5-58.5T280-480q0-83 58.5-141.5T480-680q83 0 141.5 58.5T680-480q0 83-58.5 141.5T480-280ZM200-440H40v-80h160v80Zm720 0H760v-80h160v80ZM440-760v-160h80v160h-80Zm0 720v-160h80v160h-80ZM256-650l-101-97 57-59 96 100-52 56Zm492 496-97-101 53-55 101 97-57 59Zm-98-550 97-101 59 57-100 96-56-52ZM154-212l101-97 55 53-97 101-59-57Zm326-268Z"/></svg>';
$dark_svg:  '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24"><path d="M480-120q-150 0-255-105T120-480q0-150 105-255t255-105q14 0 27.5 1t26.5 3q-41 29-65.5 75.5T444-660q0 90 63 153t153 63q55 0 101-24.5t75-65.5q2 13 3 26.5t1 27.5q0 150-105 255T480-120Zm0-80q88 0 158-48.5T740-375q-20 5-40 8t-40 3q-123 0-209.5-86.5T364-660q0-20 3-40t8-40q-78 32-126.5 102T200-480q0 116 82 198t198 82Zm-10-270Z"/></svg>';

.wp-block-button.toggle-color-scheme .wp-block-button__link {
	border-radius: 9999em;
	display: flex;
	flex-wrap: wrap;
	gap: var( --wp--preset--spacing--10 );
	align-items: center;

	&::before {
		content: '';
		mask: no-repeat 50% 50% url( 'data:image/svg+xml,' + #{ $light_svg } );
		mask-size: cover;
		background: currentColor;
		display: inline-block;
		width: 1em;
		height: 1em;
	}

	&.is-dark-scheme::before {
		mask-image: url( 'data:image/svg+xml,' + #{ $dark_svg } );
	}
}

Then register it as a block stylesheet for the core Button block by adding this code to functions.php:

add_action( 'init', 'tt4_dark_mode_block_assets' );

function tt4_dark_mode_block_assets() {
	$asset = include get_theme_file_path( 'public/css/core-button.asset.php' );

	wp_enqueue_block_style( 'core/button', [
		'handle' => 'tt4-dark-mode-core-button',
		'src'    => get_theme_file_uri( 'public/css/core-button.css' ),
		'path'   => get_theme_file_path( 'public/css/core-button.css' ),
		'deps'   => $asset['dependencies'],
		'ver'    => $asset['version']
	]);
}

Now you will see the icon appear inside the Button block:

Registering a variation for easy editor insertion

If you recall from earlier in this tutorial, you had to add the Button block markup manually via the Code Editor view. That’s not an ideal user experience. A better method is wrapping that markup into a Block Variation.

To do this, add the following code to the resources/js/editor.js file in your theme:

import { registerBlockVariation } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { SVG, Path } from '@wordpress/primitives';

registerBlockVariation( 'core/button', {
	name: 'tt4-dark-mode/button-toggle-color-scheme',
	title: __( 'Toggle: Light/Dark', 'tt4-dark-mode' ),
	description: __( 'Toggle button for switching between light and dark mode.', 'tt4-dark-mode' ),
	icon: (
		<SVG xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24">
			<Path d="M480-120q-150 0-255-105T120-480q0-150 105-255t255-105q14 0 27.5 1t26.5 3q-41 29-65.5 75.5T444-660q0 90 63 153t153 63q55 0 101-24.5t75-65.5q2 13 3 26.5t1 27.5q0 150-105 255T480-120Zm0-80q88 0 158-48.5T740-375q-20 5-40 8t-40 3q-123 0-209.5-86.5T364-660q0-20 3-40t8-40q-78 32-126.5 102T200-480q0 116 82 198t198 82Zm-10-270Z"/>
		</SVG>
	),
	keywords: [ 'button', 'toggle', 'light', 'dark' ],
	attributes: {
		className: 'toggle-color-scheme',
		tagName: 'button',
		text: `<span class="screen-reader-text">${ __( 'Toggle Color Scheme', 'tt4-dark-mode' ) }<span>`
	},
	isActive: ( blockAttributes, variationAttributes ) => {
		const className = blockAttributes.className || '';
		const tagName = blockAttributes.tagName || '';

		return className.split( ' ' ).includes( variationAttributes.className )
			&& variationAttributes.tagName === tagName;
	}
} );

Take note of the text property from the variation:

text: `<span class="screen-reader-text">${ __( 'Toggle Color Scheme', 'tt4-dark-mode' ) }<span>`

I’ve wrapped it in a <span> with the screen-reader-text class, which means that it will not be shown visually. This will only display the toggle button with the icon. If you prefer to show the text, change this code to:

text: __( 'Toggle Color Scheme', 'tt4-dark-mode' )

For the final step, be sure to enqueue the editor.js script by adding this code to your functions.php file:

add_action( 'enqueue_block_editor_assets', 'tt4_dark_mode_editor_assets' );

function tt4_dark_mode_editor_assets() {
	$script = include get_theme_file_path( 'public/js/editor.asset.php' );

	wp_enqueue_script(
		'tt4-dark-mode-editor',
		get_theme_file_uri( 'public/js/editor.js' ),
		$script['dependencies'],
		$script['version'],
		true
	);
}

Now give the Toggle: Light/Dark variation a try. Note that it’s a variation of the Button block and still needs the Buttons container.

In the below screenshot, you can see where I’m adding it to the Navigation menu on my site:

Feel free to add it anywhere or even in multiple places. Because you’re using the Interactivity API, multiple instances will automatically work.

Give the toggle button a test

All that’s left to do is open your site and view it on the front end. You should be able to switch between light and dark modes freely.

One thing of particular note: Once you’ve implemented the toggle and a user clicks it, their OS light/dark setting will no longer take precedence. It will either be stored as user meta or a cookie and tied to the site itself. This is probably obvious, but it’s worth mentioning. You can, of course, always create a feature for deleting those, but that’s outside the scope of this tutorial.

Otherwise, I’m looking forward to seeing more block themes with light/dark toggles. Please reach out to show me what you’ve built.

Props to @bph for feedback this article. Special props @welcher for letting me steal borrow his original Interactivity API code and reviewing the post.

2 responses to “Building a light/dark toggle with the Interactivity API”

  1. Derek Avatar

    As a non-developer, I’d like to hear if there is a future where creating a light/dark toggle or variation switcher could be managed through Block Editor UI, maybe with Create Block Theme.

    1. Justin Tadlock Avatar

      I think there’s a future where it’s possible. With CSS color-scheme and light-dark() now baseline standards, it’s very possible for Core to handle this. It’s mostly a matter of letting users set both colors via the UI.

      Then the toggle/switcher itself would simply be a block or block variation.

Leave a Reply

Your email address will not be published. Required fields are marked *