Adjust `theme-color` as the class and theme of the page are switched

My website has a JavaScript toggle that changes the color/theme of the page by adding/removing classes on the html element. The default theme is white, the second theme is black (.dark-mode), and the third theme is beige (.retro).

The JavaScript code for the toggle has been simplified below:

const html = document.querySelector("html");
const button = document.querySelector(".contrast__link");

button.addEventListener("click", (e) => {
    e.preventDefault();
    if (html.classList.contains("dark-mode")) {
        html.classList.remove("dark-mode");
        html.classList.add("retro");
    } else if (html.classList.contains("retro")) {
        html.classList.remove("retro");
    } else {
        html.classList.add("dark-mode");
    }
});

After the class/theme updates, I want to update the theme-color in the document head to match the new color scheme. Initially, it was set as:

<meta name="theme-color" content="#ffffff">

I attempted using CSS variables hoping it would dynamically change the color:

CSS

:root       { --color-secondary : rgb(255,255,255); }
.dark-mode  { --color-secondary : rgb(0,0,0); }
.retro      { --color-secondary : rgb(243, 238, 226); }

Tag

<meta name="theme-color" content="var(--color-primary">

Unfortunately, it seems this method is not supported. Is there a way for the theme-color to be inherited or does it have to be a fixed value?

Answer №1

It is accurate that the content attribute does not have support for CSS variables.

To adjust this value according to your theme, JavaScript will need to be utilized.

const html = document.querySelector("html");
const button = document.querySelector(".contrast__link");

// Begin by defining the colors for each theme
const colors = {
  default: '#ffffff', // color for the default theme
  darkMode: '#000000', // color for the dark-mode theme
  retro: '#f3eee2' // color for the retro theme
}

// Locate the meta tag
const metaThemeColor = document.querySelector("meta[name=theme-color]");

button.addEventListener("click", (e) => {
  e.preventDefault();
  if (html.classList.contains("dark-mode")) {
    html.classList.remove("dark-mode");
    html.classList.add("retro");
    // update the theme-color meta tag
    metaThemeColor.setAttribute('content', colors.retro);
  } else if (html.classList.contains("retro")) {
    html.classList.remove("retro");
    // update the theme-color meta tag
    metaThemeColor.setAttribute('content', colors.default);
  } else {
    html.classList.add("dark-mode");
    // update the theme-color meta tag
    metaThemeColor.setAttribute('content', colors.darkMode);
  }
});

Answer №2

This snippet of JavaScript code serves the purpose of updating the theme-color meta tag in an HTML document dynamically based on the background color of the body element. Let's dissect the functionality of each component within the code:

// Function to fetch the computed background color of the body element
function updateThemeColor() {
    var bgColor = window.getComputedStyle(document.body, null).getPropertyValue('background-color');
    
    // Locating the meta tag with name="theme-color"
    var metaTag = document.querySelector('meta[name="theme-color"]');
    
    // Updating content attribute of the meta tag with the retrieved background color
    if (metaTag) {
       metaTag.setAttribute('content', bgColor);
    } else {
        // Creating a new meta tag if it doesn't exist and appending it to the head element
        var newMetaTag = document.createElement('meta');
        newMetaTag.name = "theme-color";
        newMetaTag.content = bgColor;
        document.getElementsByTagName('head')[0].appendChild(newMetaTag);
    }
}

// Calling updateThemeColor once DOM content is fully loaded
document.addEventListener("DOMContentLoaded", updateThemeColor);

// Periodically updating the theme color meta tag every 100 milliseconds
setInterval(updateThemeColor, 100);

Explanation:

  1. updateThemeColor() function:

    • Retrieves the calculated background color of the body element utilizing
      window.getComputedStyle(document.body, null).getPropertyValue('background-color')
      .
    • Ensures retrieval of the actual background color defined through stylesheets or inline styles.
  2. Finding or creating the meta tag:

    • Utilizes
      document.querySelector('meta[name="theme-color"]')
      to search for an existing meta tag named theme-color.
    • If found (metaTag not being null), updates its content attribute to match the current bgColor.
    • If not found, generates a new meta tag (newMetaTag) carrying a name="theme-color" and sets its content to bgColor. Then appends this fresh tag to the document's head element (
      document.getElementsByTagName('head')[0].appendChild(newMetaTag)
      ).
  3. Event listener and interval:

    • document.addEventListener("DOMContentLoaded", updateThemeColor);
      guarantees that updateThemeColor executes once the DOM content is fully loaded, initiating the theme-color.
    • setInterval(updateThemeColor, 100);
      schedules updateThemeColor to run every 100 milliseconds, continuously refreshing the theme-color meta tag to mirror dynamic changes in background color.

Purpose:

The primary intention behind this script usually lies within web applications or websites that alter their themes or background colors dynamically. The theme-color meta tag aids browsers and certain platforms (such as Android's Chrome) in adjusting the browser's UI to align with the website's color scheme, enhancing user experience and visual harmony.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The form isn't accessible through a new tab

I'm having an issue with the code I've written. Even though I specified target="_blank" in the form, it's not opening in a new tab like I intended. <script> function navigate() { var webUrl = "https://website.com/search/" + document.g ...

What could be causing an error in my Vue app when attempting to process a payment using Google Pay on a mobile device, resulting in the message "Blocked a frame with origin

When implementing payment through Google Pay on Chrome desktop, it functions smoothly. However, an error occurs when attempting to pay using a smartphone, triggering an additional modal window with a card selection: vue.esm.js?a026:152 Uncaught (in promise ...

Using the append() method in d3 with a function argument adds new

This is functional code: // A d3.select("body").selectAll(".testDiv") .data(["div1", "div2", "div3"]) .enter().append("div") .classed("testDiv", true) .text(function(d) { return d; }); The next snippet is essentially the same, except that ins ...

ReactJS is encountering a situation where two children are using the same key and causing

When I try to view the profile information of another user, I encounter a duplicate key error that says: "Warning: Encountered two children with the same key, ``. Keys should be unique so that components maintain their identity across updates. Non-unique k ...

The first time I try to load(), it only works partially

My script used to function properly, but it has suddenly stopped working. Can anyone help me figure out why? The expected behavior is for the referenced link to be inserted into target 1, while target 2 should be updated with new content from two addition ...

Is there a way to extract the data enclosed by "<div>" and "</div>" utilizing python's sgmllib or parser module?

This is the HTML snippet provided: <div id="wrap"> <div id="content"> <h1>head</h1> <ul class="jobpara"> <li class="floatl"><span>time:</span>2013-08-13</li> <li clas ...

The _doc property within Mongoose

I need assistance with this JavaScript code I have: app.post('/auth', async (req, res) => { try { const user = UserModel.findOne({email: req.body.email}).exec() if (!user) return res.status(404).json({ message: ...

Creating a promise to write data to a file

When executing the function, it creates a series of files but fails to write data to them. Strangely, omitting Promise.all at the end and not resolving the function actually results in the data being written to the files. It's puzzling that no matter ...

Calculating the size of an array based on its attributes

Hey there, I'm working with an array containing attributes and need to determine its length. In this particular case, the array should have a length of 2 since there are only two "items" present. {"items":[{"value":"2","valor":0,"name":"Limpeza"}, {" ...

Regular expression ignores the initial "less than" character

Question Statement: From: John Doe <<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ddb7b2b5b3aeb0b4a9b59dbab0bcb4b1f3beb2b0">[email protected]</a>> Date: Mon, 25 Oct 2021 09:30:15 -0400 Message-ID: << ...

Designing a dropdown menu with sub-menus that appear when hovered over

In my quest to design CSS that will dynamically display links in left-to-right divs, I am seeking a way for sub-menus to appear below the menu being rolled over. The challenge lies in the fact that these menus will be loaded from a WordPress database, mean ...

Why doesn't the address bar automatically update to the correct path after successfully authenticating with NextAuth using credentials?

Is there a way to automatically refresh the URL path once a successful login is completed with credentials? I attempted to set up credential authentication similar to the guide provided by Next in their tutorial here. However, I am only using email for au ...

Styling Text on Buttons (Using CSS)

I need to modify my button, which contains lengthy text. When the mouse pointer hovers over the button, I would like the long text to wrap into 2 or 3 lines so that more of it is visible. Additionally, I want the remaining text to be displayed with an el ...

Encountering an error stating "Potential null object" while attempting to retrieve the total count of characters and numbers in a given string

Currently, I am trying to find the number of characters and digits that repeat more than once in a given input string. For example, if the input is "zzrrcde", the output should be 2 as both z and r occur more than once. Here is the function I have writte ...

Using JQuery, select and remove a single element from a dropdown while simultaneously removing the same element

I am experiencing an issue with multiple drop-downs that have the same options. Currently, my code functions correctly when the user selects an item from the first drop-down list, causing that item to be removed from the second and third lists. However, if ...

Tips for positioning text below an image using HTML and CSS

Hey there, I've been working on a website that needs to match this design: https://i.sstatic.net/39bW3.png This is an image of what I have completed so far: https://i.sstatic.net/WyEC2.png Here is the code I've written: body { ...

Reactjs event handler binding to functions

Can you explain the reason behind having to bind an object null to the function? add(text) { this.setState(prevState=> ({ notes: [ ...prevState.notes, { id: this.nextId(), note: text ...

Sharing data between different JavaScript files using Knockout.js

I am working with two JavaScript files named FileA.js and FileB.js. Within FileA.js, there is a function called Reports.CompanySearch.InitGrid: function InitGrid(viewModel, emptyInit) { var columns = [ { name: 'CompanyName', ...

What is the best method to display a component using a string in Vue 3?

I've been attempting to render a component from a string without success. Here are my codes: <template> <div v-html="beautifyNotification(notification)"></div> </template> <script> import { Link } from '@i ...

Step-by-step guide to uploading files using cucumber-js

Is there a way to write a script that can successfully fill out a form and upload a file using cucumber-js, selenium, and protractor? I am encountering an issue where there is no xpath/id available to click on when trying to upload a file. How have you d ...