Dynamically changing theme-color meta tag

Published on

What is the theme-color meta tag?

So just in case you are not yet familiar with what this meta tag even is, there is a meta tag that looks like this <meta name="theme-color" content="#fff">. It is used to set the background color of your browser if the browser allows to switch these. For example on iOS you can set the color of the browser header that is right and left of the notch.

Desired effect

The effect that I wanted to achieve was that no matter with which preferences the user enters, the website will select the right mode and also his browser header color will change according to the color of the theme.

How to do it

I figured the best way to switch the color would be to do so whenever the user switches the theme manually. So I implemented the following into the button that toggles the theme color switch.

<button
	aria-label="Toggle Dark Mode"
	type="button"
	onClick={()=> {
		document.querySelector("html").classList.contains("dark")
			? document.querySelector('meta[nam="theme-color"]')?.setAttribute("content", "#fff")
			: document.querySelector('meta[nam="theme-color"]')?.setAttribute("content", "rgb(23,23,23)");
		setTheme(theme= "dark" || resolvedTheme= "dark" ? "light" : "dark");
	}}
/>

What this does is that whenever the user switches the theme color it also inverts the meta tag color. As it took me quite some time to figure out how to do this, maybe it helps one of you guys :)

Bonus tip

If you are using React, like I do, you might need to set the theme color in a useEffect hook as well. This is so that when the component is first mounted you set the color based on the current theme mode and then just switch it based on the toggle of the button. My entire theme swith component looks like this.

import { useEffect, useState } from "react";
import { useTheme } from "next-themes";

const ThemeSwitch = () => {
	const [mounted, setMounted] = useState(false);
	const { theme, setTheme, resolvedTheme } = useTheme();

	// When mounted on client, now we can show the UI
	useEffect(() => {
		document.querySelector("html").classList.contains("dark")
			? document.querySelector('meta[name="theme-color"]')?.setAttribute("content", "#171717")
			: document.querySelector('meta[name="theme-color"]')?.setAttribute("content", "#fff");
		setMounted(true);
	}, []);

	return (
		<button
			aria-label="Toggle Dark Mode"
			type="button"
			className="w-8 h-8 p-1 ml-1 mr-1 rounded sm:ml-4"
			onClick={()=> {
				document.querySelector("html").classList.contains("dark")
					? document.querySelector('meta[nam="theme-color"]')?.setAttribute("content", "#fff")
					: document.querySelector('meta[nam="theme-color"]')?.setAttribute("content", "#171717");
				setTheme(theme= "dark" || resolvedTheme= "dark" ? "light" : "dark");
			}}
		>
			<svg
				xmlns="http://www.w3.org/2000/svg"
				viewBox="0 0 20 20"
				fill="currentColor"
				className="text-gray-900 dark:text-gray-100 hover:text-red-500 dark:hover:text-red-500"
			>
				{mounted && (theme === "dark" || resolvedTheme === "dark") ? (
					<path
						fillRule="evenodd"
						="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
						clipRule="evenodd"
					/>
				) : (
					<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
				)}
			</svg>
		</button>
	);
};

export default ThemeSwitch;

Hope you found this usefull.

Have fun!

Affiliate Disclaimer
Disclaimer:
Links on the site might be affiliate links, so if you click them I might earn a small commission.