Dark mode with Oxywind plugin

Tailwind enables you to quickly apply light and dark mode styles with their simple, yet powerful, classing system. Here's a simple way using Oxygen and Oxywind to build a toggle to enable this whilst also respecting and remembering a user's preference.

Demo

Note: I will assume you have a compatible version of Oxygen Builder and Oxywind running and patched.

By default, Tailwind utilises a systems preference approach for dark mode - i.e. if a user has dark mode selected on their device settings, then Tailwind will show the dark mode stylings in the browser. To learn more, check out the Tailwind Dark Mode documentation.

We want to have this feature AND also enable a user preference toggle with the ability to remember the user's selection for future visits.

Config setup

We need to change Oxywind's default approach for dark mode to apply dark mode when the HTML element has class="dark".

Go to the Oxywind Config section via wp-admin > Oxygen > Oxywind > Settings > Tailwind Config *

You need to add "darkMode: 'class'" in the tailwind.config = {} level. It should look something like this:

tailwind.config = {
	darkMode: 'class',
  // ...
	theme: {
		extend: {
		}
	}
}

Shout-out to Joshua Siagian for helping me realise this!

Toggle

I am going to use Oxygen's code block (probably should go in your catch all template - I will skip the setup and styling of this for this post) to house all of the code which is related to the actual style and function of the toggle.

Button

Under the PHP section of the aforementioned code block, I have the following script which creates a button element containing a sun and moon SVG with individual classes:

<button class="theme-toggle" id="theme-toggle" title="Toggle light & dark" aria-label="auto" aria-live="polite">
    <svg class="sun block dark:hidden" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path
            d="M10 7C9.9998 8.39064 10.4138 9.74983 11.1892 10.9042C11.9646 12.0586 13.0663 12.9559 14.3538 13.4816C15.6412 14.0073 17.0561 14.1376 18.4179 13.8559C19.7797 13.5742 21.0268 12.8933 22 11.9V12C22 17.523 17.523 22 12 22C6.477 22 2 17.523 2 12C2 6.477 6.477 2 12 2H12.1C11.434 2.65113 10.9051 3.42896 10.5445 4.28768C10.1838 5.1464 9.99869 6.06862 10 7ZM4 12C3.99927 13.785 4.59553 15.5189 5.69389 16.926C6.79226 18.333 8.32963 19.3323 10.0614 19.7648C11.7932 20.1974 13.6199 20.0383 15.2508 19.313C16.8818 18.5876 18.2233 17.3377 19.062 15.762C17.5694 16.1136 16.0118 16.0781 14.5368 15.6587C13.0619 15.2394 11.7185 14.4501 10.6342 13.3658C9.54992 12.2815 8.76065 10.9381 8.34128 9.46318C7.92192 7.98821 7.88636 6.43056 8.238 4.938C6.95758 5.62014 5.88678 6.63766 5.14026 7.88164C4.39373 9.12562 3.99958 10.5492 4 12Z"
            fill="#09121F"
        />
    </svg>
    <svg class="moon hidden dark:block" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path
            d="M12 18C10.4087 18 8.88258 17.3679 7.75736 16.2426C6.63214 15.1174 6 13.5913 6 12C6 10.4087 6.63214 8.88258 7.75736 7.75736C8.88258 6.63214 10.4087 6 12 6C13.5913 6 15.1174 6.63214 16.2426 7.75736C17.3679 8.88258 18 10.4087 18 12C18 13.5913 17.3679 15.1174 16.2426 16.2426C15.1174 17.3679 13.5913 18 12 18ZM12 16C13.0609 16 14.0783 15.5786 14.8284 14.8284C15.5786 14.0783 16 13.0609 16 12C16 10.9391 15.5786 9.92172 14.8284 9.17157C14.0783 8.42143 13.0609 8 12 8C10.9391 8 9.92172 8.42143 9.17157 9.17157C8.42143 9.92172 8 10.9391 8 12C8 13.0609 8.42143 14.0783 9.17157 14.8284C9.92172 15.5786 10.9391 16 12 16ZM11 1H13V4H11V1ZM11 20H13V23H11V20ZM3.515 4.929L4.929 3.515L7.05 5.636L5.636 7.05L3.515 4.93V4.929ZM16.95 18.364L18.364 16.95L20.485 19.071L19.071 20.485L16.95 18.364ZM19.071 3.514L20.485 4.929L18.364 7.05L16.95 5.636L19.071 3.515V3.514ZM5.636 16.95L7.05 18.364L4.929 20.485L3.515 19.071L5.636 16.95ZM23 11V13H20V11H23ZM4 11V13H1V11H4Z"
            fill="#09121F"
        />
    </svg>
</button>

Credits to Adam Argyle for the idea and Joshua Siagian for helping me optimise this!

You can of course change the SVGs and colours as per your preference.

Functionality

Under the JavaScript section, I have the following snippet to create and enable the functionality:

  if (
    localStorage.getItem('color-theme') === 'dark' ||
    (!('color-theme' in localStorage) &&
      window.matchMedia('(prefers-color-scheme: dark)').matches)
  ) {
    document.documentElement.classList.add('dark');
  } else {
    document.documentElement.classList.remove('dark');
  }

var themeToggleBtn = document.querySelector('.theme-toggle');

themeToggleBtn.addEventListener('click', function() {

    // if set via local storage previously
    if (localStorage.getItem('color-theme')) {
        if (localStorage.getItem('color-theme') === 'light') {
            document.documentElement.classList.add('dark');
            localStorage.setItem('color-theme', 'dark');
        } else {
            document.documentElement.classList.remove('dark');
            localStorage.setItem('color-theme', 'light');
        }

    // if NOT set via local storage previously
    } else {
        if (document.documentElement.classList.contains('dark')) {
            document.documentElement.classList.remove('dark');
            localStorage.setItem('color-theme', 'light');
        } else {
            document.documentElement.classList.add('dark');
            localStorage.setItem('color-theme', 'dark');
        }
    }
    
});

Credits to Zoltán Szőgyényi for providing the snippet.

Note: From my limited understanding of JavaScript, the first portion of the above script should be "inline" i.e. somewhere in the head of the website so the styling is quickly set on page load and not cached.

Basic styling

Under the CSS section of the code block, I added the following snippet to remove default button stylings, make the cursor pointer on hover, and make the overall area slightly bigger:

.theme-toggle{
	cursor: pointer;
	background: transparent;
	border: none;
	padding: 10px;
}

Note: You will not see any changes if you do not check the following:

  • Your actual URL structure needs to be the same as the URL structures in wp-admin > Settings > General - Oxywind's Worker requires this to be the same!
  • You need to purge all of your caches (i.e. Oxywind, Oxygen, and hosting). I use SiteGround hosting (affiliate link) and they enable Dynamic Cache, which has to be purged via SiteTools.

Provided you followed the above steps, you should now have a working Oxywind setup and toggle which can switch between light/dark mode with ease.

TLDR (source code)

You can copy and paste the JSON snippet below into your Oxygen build if you do not want to build from scratch (but you still need to do the Config Setup step).

{
    "id": 0,
    "name": "root",
    "depth": 0,
    "children": [
        {
            "id": 2,
            "name": "ct_code_block",
            "options": {
                "ct_id": 2,
                "ct_parent": 0,
                "selector": "theme-toggle-wrapper",
                "original": {
                    "image_type": "2",
                    "code-php": "<button class=\"theme-toggle\" id=\"theme-toggle\" title=\"Toggle light & dark\" aria-label=\"auto\" aria-live=\"polite\">\n    <svg class=\"sun block dark:hidden\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n        <path\n            d=\"M10 7C9.9998 8.39064 10.4138 9.74983 11.1892 10.9042C11.9646 12.0586 13.0663 12.9559 14.3538 13.4816C15.6412 14.0073 17.0561 14.1376 18.4179 13.8559C19.7797 13.5742 21.0268 12.8933 22 11.9V12C22 17.523 17.523 22 12 22C6.477 22 2 17.523 2 12C2 6.477 6.477 2 12 2H12.1C11.434 2.65113 10.9051 3.42896 10.5445 4.28768C10.1838 5.1464 9.99869 6.06862 10 7ZM4 12C3.99927 13.785 4.59553 15.5189 5.69389 16.926C6.79226 18.333 8.32963 19.3323 10.0614 19.7648C11.7932 20.1974 13.6199 20.0383 15.2508 19.313C16.8818 18.5876 18.2233 17.3377 19.062 15.762C17.5694 16.1136 16.0118 16.0781 14.5368 15.6587C13.0619 15.2394 11.7185 14.4501 10.6342 13.3658C9.54992 12.2815 8.76065 10.9381 8.34128 9.46318C7.92192 7.98821 7.88636 6.43056 8.238 4.938C6.95758 5.62014 5.88678 6.63766 5.14026 7.88164C4.39373 9.12562 3.99958 10.5492 4 12Z\"\n            fill=\"#09121F\"\n        />\n    </svg>\n    <svg class=\"moon hidden dark:block\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n        <path\n            d=\"M12 18C10.4087 18 8.88258 17.3679 7.75736 16.2426C6.63214 15.1174 6 13.5913 6 12C6 10.4087 6.63214 8.88258 7.75736 7.75736C8.88258 6.63214 10.4087 6 12 6C13.5913 6 15.1174 6.63214 16.2426 7.75736C17.3679 8.88258 18 10.4087 18 12C18 13.5913 17.3679 15.1174 16.2426 16.2426C15.1174 17.3679 13.5913 18 12 18ZM12 16C13.0609 16 14.0783 15.5786 14.8284 14.8284C15.5786 14.0783 16 13.0609 16 12C16 10.9391 15.5786 9.92172 14.8284 9.17157C14.0783 8.42143 13.0609 8 12 8C10.9391 8 9.92172 8.42143 9.17157 9.17157C8.42143 9.92172 8 10.9391 8 12C8 13.0609 8.42143 14.0783 9.17157 14.8284C9.92172 15.5786 10.9391 16 12 16ZM11 1H13V4H11V1ZM11 20H13V23H11V20ZM3.515 4.929L4.929 3.515L7.05 5.636L5.636 7.05L3.515 4.93V4.929ZM16.95 18.364L18.364 16.95L20.485 19.071L19.071 20.485L16.95 18.364ZM19.071 3.514L20.485 4.929L18.364 7.05L16.95 5.636L19.071 3.515V3.514ZM5.636 16.95L7.05 18.364L4.929 20.485L3.515 19.071L5.636 16.95ZM23 11V13H20V11H23ZM4 11V13H1V11H4Z\"\n            fill=\"#09121F\"\n        />\n    </svg>\n</button>",
                    "code-js": "if (\n    localStorage.getItem('color-theme') === 'dark' ||\n    (!('color-theme' in localStorage) &&\n        window.matchMedia('(prefers-color-scheme: dark)').matches)\n) {\n    document.documentElement.classList.add('dark');\n} else {\n    document.documentElement.classList.remove('dark');\n}\n\nvar themeToggle = document.querySelector('.theme-toggle');\n\nthemeToggle.addEventListener('click', function () {\n    let currentTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';\n\n    if ('dark' === currentTheme) {\n        document.documentElement.classList.remove('dark');\n    } else {\n        document.documentElement.classList.add('dark');\n    }\n    \n    localStorage.setItem('color-theme', currentTheme);\t\n});",
                    "code-css": ".theme-toggle{\n\tcursor: pointer;\n\tbackground: transparent;\n\tborder: none;\n\tpadding: 10px;\n}"
                },
                "nicename": "Code Block (#2)",
                "activeselector": false
            },
            "depth": 1
        }
    ],
    "meta_keys": []
}

Want to be notified of new posts like this?

Sign up to be notified of new posts using the form below. Email addresses are used for the sole purpose of sending once-in-a-while email notifications and nothing else. I promise I don't spam!