What is the best way to identify dark mode with JavaScript?

Both Windows and macOS have introduced dark mode.

To style with CSS, I can utilize the following code:

@media (prefers-dark-interface) { 
  color: white; background: black 
}

However, the Stripe Elements API that I am using injects colors via JavaScript.

Here is an example of how it's done:

const stripeElementStyles = {
  base: {
    color: COLORS.darkGrey,
    fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"`,
    fontSize: '18px',
    fontSmoothing: 'antialiased',
    '::placeholder': {
      color: COLORS.midgrey
    },
    ':-webkit-autofill': {
      color: COLORS.icyWhite
    }
  }
}

I am looking for a way to detect the preferred color scheme of the operating system using JavaScript. Any suggestions?

Answer №1

if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
        // dark mode
    }
    

To monitor for any changes:

window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
        const newColorScheme = event.matches ? "dark" : "light";
    });
    

Answer №2

You have the option to directly examine CSS Media-Queries using JavaScript

The window.matchMedia() function provides a MediaQueryList object that reflects the outcomes of the specified CSS media query statement. The method can encompass various media features from the CSS @media rule, such as min-height, min-width, orientation, and more.

To determine if the Media Query is valid, utilize the matches property

// Verify support for Media Queries
if (window.matchMedia) {
  // Assessing dark-mode Media Query match
  if(window.matchMedia('(prefers-color-scheme: dark)').matches){
    // Dark mode
  } else {
    // Light mode
  }
} else {
  // Default behavior (in case Media Queries are not supported)
}

You can also use a simple shorthand method for a straightforward boolean check (with a default value at the end):

const checkIsDarkSchemePreferred = () => window?.matchMedia?.('(prefers-color-scheme:dark)')?.matches ?? false;

To dynamically adjust the `color-scheme` based on user preferences, you can implement the following approach:
function setColorScheme(scheme) {
  switch(scheme){
    case 'dark':
      console.log('dark');
      
      break;
    case 'light':
      console.log('light');
      // Light mode
      break;
    default:
      // Default mode
      console.log('default');
      break;
  }
}

function getPreferredColorScheme() {
  if (window.matchMedia) {
    if(window.matchMedia('(prefers-color-scheme: dark)').matches){
      return 'dark';
    } else {
      return 'light';
    }
  }
  return 'light';
}

function updateColorScheme(){
    setColorScheme(getPreferredColorScheme());
}

if(window.matchMedia){
  var colorSchemeQuery = window.matchMedia('(prefers-color-scheme: dark)');
  colorSchemeQuery.addEventListener('change', updateColorScheme);
}

updateColorScheme();

Answer №3

As stated on MDN's MediaQueryList Web APIs page, the recommended approach to monitor changes is by utilizing the addListener method. I encountered issues with using addEventListener specifically on iOS 13.4.

window.matchMedia('(prefers-color-scheme: dark)').addListener(function (e) {
  console.log(`The theme has switched to ${e.matches ? "dark" : "light"} mode`)
});

Answer №4

Check out this concise code snippet inspired by SanBen's solution:

function determineColorScheme() {
  return window?.matchMedia?.('(prefers-color-scheme:dark)')?.matches ? 'dark' : 'light';
}

Answer №5

Utilizing the power of optional chaining with matchMedia:

const selectedTheme = window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light"

Answer №6

Take a look at the matchMedia feature:

function checkTheme() {
  if(window.matchMedia && window.matchMedia("(prefers-color-scheme:dark)").matches) {
    return "dark";
  } else {
    return "light";
  }
}

Answer №7

Before you start coding in JavaScript:

Utilize CSS to create a @media query that instructs an ::after pseudo class on the body element to display different text based on the user's color scheme preference. To prevent confusion for users, ensure to add a display: none; property to the ::after element inside the body.

CSS Snippet:

@media (prefers-color-scheme:dark){
    body::after{
        content: 'd';
        display: none;
    }
}
@media (prefers-color-scheme:light){
    body::after{
        content: 'l';
        display: none;
    }
}

Next step is adding your JavaScript code:

Given that we have a target object within the document to select, we can access the ::after pseudo class of the body element. Extracting the content from it is crucial, ensuring your CSS loads before executing any JavaScript commands! Remember, use 'd' for dark mode and 'l' for light mode.

JavaScript Piece:

var colorScheme = getComputedStyle(document.body,':after').content;
// d for dark mode, l for light mode.

The significance of this approach:

Although achievable through CSS and HTML alone, a scenario may arise where employing JavaScript becomes necessary. For instance, when inserting an img element containing text, requiring two images—one for light mode, the other for dark mode. Here, JavaScript proves handy in dynamically adjusting the src attribute value of the img element based on the active color scheme.

While there are possibly more applications, this specific use case stands out as a practical example.

References:

I gleaned insights on using the getComputedStyle function from a helpful discussion on Stack Overflow.

My understanding of @media (prefers-color-scheme:color scheme definition) was refined by consulting MDN Web Docs.

Additionally, I acquired knowledge on extracting .content from computed styles through a code suggestion in VSCode while typing

getComputedStyle(document.body,':after')
, validating its functionality and utility. (Unfortunately, the original source of this information is untraceable.)

Answer №8

If you're working with Bootstrap 5 and need to default to a dark theme when JavaScript is disabled, try this solution:

// prevent eye strain /////////////////////////////////////////////////////
const matchPrefersDark = window.matchMedia('(prefers-color-scheme:dark)');
if (matchPrefersDark.matches) {
  document.documentElement.setAttribute('data-bs-theme', 'dark');
}
matchPrefersDark.addEventListener('change', event => {
  document.documentElement.setAttribute('data-bs-theme', event.matches ? "dark" : "light");
});

Don't forget to include the following in your HTML:

<html lang="en-US" data-bs-theme="dark">

You can adjust the language attribute accordingly based on your requirements.

Answer №9

Check out this JavaScript code snippet designed to detect changes in the OS theme by using the appearance-changed event.

// This event triggers whenever the OS theme changes
window.addEventListener('appearance-changed', function(e) {
  console.log(e.detail); // Outputs 'light' or 'dark'
});

In addition, it introduces a new property window.appearance that allows you to retrieve the current OS theme:

switch (window.appearance) {
    
    case 'light': {
       // Perform actions for light theme
    } break;

    case 'dark': {
       // Perform actions for dark theme
    } break;
}

You can find the source code on GitHub.

Answer №10

If you are working with React, check out the useDarkMode hook. This handy tool listens for changes and allows users to easily toggle or switch between dark mode and light mode.

import { useDarkMode } from 'usehooks-ts'

export default function Component() {
  const { isDarkMode, toggle, enable, disable } = useDarkMode()
  return (
    <div>
      <p>Current theme: {isDarkMode ? 'dark' : 'light'}</p>
      <button onClick={toggle}>Toggle</button>
      <button onClick={enable}>Enable</button>
      <button onClick={disable}>Disable</button>
    </div>
  )
}

Answer №11

If you're working on a Nuxt app, make sure to incorporate the color-mode plugin for better results.

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 jQuery HTTP Post function suddenly stopped functioning properly, only working when lengthy strings are entered

Just starting out with web development. I have a bootstrap page featuring a form for users to input data. The form includes two text boxes, one radio button, and one dropdown menu. My goal was to collect this input data and create a JSON object to POST it ...

Is there a workaround for utilizing a custom hook within the useEffect function?

I have a custom hook named Api that handles fetching data from my API and managing auth tokens. In my Main app, there are various ways the state variable "postId" can be updated. Whenever it changes, I want the Api to fetch new content for that specific p ...

Using the jQuery library to define the base URL reference

I'm attempting to dynamically set the base href using JQuery by fetching information from a PHP file. Why is it that $("base").load("ip.php"); functions properly, but $("base[property='href']").load("ip.php"); does not? The goal is to spec ...

Ways to streamline repetitive CSS rules within media queries

As I work on defining multiple media queries, I realize there is some redundant code within them: @media only screen and (max-width:768px), only screen and (min-width:769px) and (max-width:1040px) { .list__figure { margin-right: 20px; ...

How can external webpages be effectively integrated under a banner for a cohesive user experience?

Google Images serves as a prime example. Upon clicking on an image, a persistent frame appears at the top of the page, prompting users to easily navigate back to Google. Is there a specific term for this method and what would be the most effective approach ...

Is it feasible to incorporate Mat-Paginator into a standard table within Angular?

I created an HTML table in Angular to display data from an API, and now I'm looking to incorporate a mat-paginator. Below is the code snippet: <table class="table" style="text-align: center;"> <thead class="thead-lig ...

Mastering the interaction between jQuery AJAX and PHP arrays

I am working on a jquery ajax request and need to extract data separately from the array returned by the handler. $.ajax({ url : 'fun_val_itmco.php', type : 'POST', data : "agnco="+agnco+"&itmco="+itmco, dataType : 'json' ...

Tips for styling the chosen option in a dropdown menu with <ul> and <li> tags

I am having trouble logging the selected dropdown value to the console in this code snippet. HTML <div class="dropdown"><button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" ...

Effortlessly browse through directory with the seamless integration of AngularJS or

Hey there! I'm working on a really cool feature - creating a list with editable inputs. However, I've encountered a bit of an issue. Is there any way to navigate through this list using arrow keys and focus on the desired input? .content ul { ...

The autoplay feature of the HTML5 audio player is not working on iOS Safari

<audio controls autoplay> <source src="horse.ogg" type="audio/ogg"> </audio> The audio element is programmed to autoplay, but it does not work on iOS browsers. I even attempted to trigger a simulated click, but the autoplay feature sti ...

Javascript window.scroll function malfunctioning in some browsers while running in localhost

Check out this codepen link where everything is working, but unfortunately not locally: https://codepen.io/LoudDesignStudios/pen/RwxPJKY <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> < ...

Determine whether to show or hide a div based on the width of the

Is there a way to dynamically hide a div using Bootstrap 4 based on the screen width? Can this be achieved without using JavaScript? I am particularly interested in hiding certain text elements that are not relevant on smaller screens, such as mobile de ...

Text input fields within a grid do not adjust to different screen sizes when placed within a tab

I noticed that my component under a tab is causing the Textfield to become unresponsive on small screens. To demonstrate this, I checked how the Textfield appears on an iPhone 5/SE screen size. https://i.stack.imgur.com/d8Bql.png Is there a way to make t ...

Customize the appearance of the v-autocomplete component in Vuetify

How can I style the autocomplete component to only show words that begin with the letters I'm typing, rather than all words containing those letters? Also, is there a way to display the typed letters in bold without highlighting them? https://i.sstat ...

An easy way to display an error status after clicking the next button using javascript

Whenever I input an email address, it displays an error status. I want the error status to appear after clicking the next button. In addition, I've included a "more view friends" link that, when clicked, will display one more textbox at each link. C ...

What is the best way to trigger a unique modal dialog for every element that is clicked on?

I simply want to click on the state and have that state's specific pop-up appear. $("path, circle").on('click', function(e) { $('#info-box').css('display', 'block'); $('#info-box').html($(this ...

Combining JS and PHP for secure function escaping

Looking for a solution to properly escape quotes in generated PHP Javascript code. Here is an example of the current output: foreach ($array as $element) { echo '<a onClick="myFunctionTakesPHPValues('.$element[0].','.$element[1] ...

Error: Attempting to import statement outside of module when calling JavaScript from Python (Dash)

I am attempting to incorporate a three.js example from the fireship-io YouTube channel to create an animated background on my Python dash application based on user scrolling. The JavaScript script continuously calls a function to update object rotation and ...

Showing content in a way that spaces out below the text

Could someone assist me with a CSS problem I'm having? I understand CSS, but debugging is not my strong suit. When I use display: inline-block for both my Academic Information and Personal Information, I'm getting extra white space below (above " ...

hover causing the table cell to act erratically

My table consists of three cells with widths of 30%, 40%, and 30% respectively. The table itself occupies 25% of its container, which can range from 1024px to 400px. The first cell contains a button labeled GALLERY. Upon hovering over the cell, the text c ...