{"id":2164,"date":"2022-08-02T16:55:56","date_gmt":"2022-08-02T09:55:56","guid":{"rendered":"https:\/\/wptips.dev\/?p=2164"},"modified":"2022-08-02T17:03:08","modified_gmt":"2022-08-02T10:03:08","slug":"easy-dark-mode","status":"publish","type":"post","link":"https:\/\/pixelstudio.id\/blog\/easy-dark-mode\/","title":{"rendered":"Quick and Easy Dark Mode without Plugin"},"content":{"rendered":"\n<p>Dark Mode has risen in popularity in the past few years. But developing it is quite difficult. Especially if the website isn&#8217;t using CSS Variable.<\/p>\n\n\n\n<p>In this article, we will take a look at a <strong>quick and easy way<\/strong> of creating it.<\/p>\n\n\n\n<p><strong>TABLE OF CONTENTS<\/strong><\/p>\n\n\n\n<ul><li><a href=\"#how-it-works\">How it Works<\/a><\/li><li><a href=\"#automatic-detection\" data-type=\"internal\" data-id=\"#automatic-detection\">Method #1 &#8211; Automatic Detection<\/a><\/li><li><a href=\"#manual-toggle\">Method #2 &#8211; Manual Toggle<\/a><\/li><li><a href=\"#adminbar-bug\">Fixing Adminbar Bug<\/a><\/li><li><a href=\"#conclusion\">Conclusion<\/a><\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"how-it-works\">How It Works<\/h2>\n\n\n\n<p>The CSS below will do most of the heavy-lifting:<\/p>\n\n\n\n<pre title=\"style.css\" class=\"wp-block-code\"><code lang=\"css\" class=\"language-css\">body.is-dark {\n  filter: invert(100%) hue-rotate(180deg);\n}\n\nbody.is-dark img {\n  filter: invert(100%) hue-rotate(180deg) brightness(1.2);\n}\n\n\/* Images that you want to stay inverted, change it accordingly *\/\nbody.is-dark .site-logo,\nbody.is-dark .icon {\n  filter: none;\n}\n\n\/* All wrapping elements need to have background color *\/\nbody,\n.main-container {\n  background-color: white;\n}<\/code><\/pre>\n\n\n\n<p>We basically <strong>inverted everything<\/strong>. So a white background becomes black, black texts become white, and a blue button becomes dark blue.<\/p>\n\n\n\n<p>After that, we <strong>reverted back the images<\/strong> so they don&#8217;t look like a negative film while slightly reducing their brightness.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"423\" src=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2022\/08\/dark-mode-comparison.jpg\" alt=\"\" class=\"wp-image-2184\" srcset=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2022\/08\/dark-mode-comparison.jpg 750w, https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2022\/08\/dark-mode-comparison-480x271.jpg 480w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><figcaption>Light mode (left) and Dark mode (right)<\/figcaption><\/figure>\n\n\n\n<p>Here&#8217;s the demo, click the toggle at the top-right corner:<\/p>\n\n\n\n<iframe height=\"600\" style=\"width: 100%;\" scrolling=\"no\" title=\"Dark Mode Simple\" src=\"https:\/\/codepen.io\/hrsetyono\/embed\/YzYdEpr?default-tab=result\" frameborder=\"no\" loading=\"lazy\" allowtransparency=\"true\" allowfullscreen=\"true\">\n  See the Pen <a href=\"https:\/\/codepen.io\/hrsetyono\/pen\/YzYdEpr\">\n  Dark Mode Simple<\/a> by hrsetyono (<a href=\"https:\/\/codepen.io\/hrsetyono\">@hrsetyono<\/a>)\n  on <a href=\"https:\/\/codepen.io\">CodePen<\/a>.\n<\/iframe>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\"\/>\n\n\n\n<p>Now that we know how the dark mode works, we need to decide whether you want (1) automatic detection or (2) manual toggle.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"automatic-detection\">Method #1 &#8211; Automatic Detection<br><sub>(Without JavaScript)<\/sub><\/h2>\n\n\n\n<p>This method uses a <strong>media query<\/strong> that checks your device&#8217;s Dark Mode setting. Simply change the CSS above into this:<\/p>\n\n\n\n<pre title=\"style.css\" class=\"wp-block-code\"><code lang=\"css\" class=\"language-css\">@media (prefers-color-scheme: dark) {\n  body {\n    filter: invert(100%) hue-rotate(180deg);\n  }\n\n  img {\n    filter: invert(100%) hue-rotate(180deg) brightness(1.2);\n  }\n   \n   \/* Images that you want to stay inverted, change it accordingly *\/\n  .site-logo,\n  .icon {\n    filter: none\n  }\n}\n\n\/* All wrapping elements need to have background color *\/\nbody,\n.main-container {\n  background-color: white;\n}<\/code><\/pre>\n\n\n\n<p>That&#8217;s all!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"manual-toggle\">Method #2 &#8211; Manual Toggle<br><sub>(With JavaScript)<\/sub><\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"453\" height=\"93\" src=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2022\/08\/dark-mode-toggle.jpg\" alt=\"\" class=\"wp-image-2185\"\/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>HTML<\/strong><\/h4>\n\n\n\n<p>First, we need a toggle. The simplest markup for it is by using Checkbox wrapped in a Label like shown below:<\/p>\n\n\n\n<pre title=\".html\" class=\"wp-block-code\"><code lang=\"html\" class=\"language-html\">&lt;label class=\"dark-toggle\"&gt;\n  &lt;span&gt; Light &lt;\/span&gt;\n  &lt;input type=\"checkbox\"&gt;\n  &lt;div class=\"dark-toggle__switch\" tabindex=\"0\"&gt;&lt;\/div&gt;\n  &lt;span&gt; Dark &lt;\/span&gt;\n&lt;\/label&gt;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>CSS<\/strong><\/h4>\n\n\n\n<p>The <strong>CSS <\/strong>is quite long, so here&#8217;s a link to the Gist. We also provide the <strong>Sass<\/strong> version if you prefer that one.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link has-blue-background-color has-background\" href=\"https:\/\/gist.github.com\/hrsetyono\/6a1fd0c43b3e0d7d8b3b9a0b2314d548\" target=\"_blank\" rel=\"noreferrer noopener\">Gist for the Toggle CSS<\/a><\/div>\n<\/div>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>JavaScript<\/strong><\/h4>\n\n\n\n<p>There are 3 things we need to do:<\/p>\n\n\n\n<ol><li><strong>Change Listener<\/strong> on the Checkbox to toggle dark mode.<\/li><li><strong>Enter Key Listener<\/strong> on the Checkbox for accessibility.<\/li><li>Store in a <strong>cache <\/strong>so it can automatically change to Dark Mode on load.<\/li><\/ol>\n\n\n\n<p>Let&#8217;s go straight to the code!<\/p>\n\n\n\n<pre title=\"script.js\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">const myDarkMode = {\n  init() {\n    this.changeListener();\n    this.tabindexListener();\n  },\n\n  \/**\n   * Change listener for dark mode toggle\n   *\/\n  changeListener() {\n    const $darkToggles = document.querySelectorAll('.dark-toggle input[type=\"checkbox\"]');\n    if ($darkToggles.length &lt;= 0) { return; }\n\n    $darkToggles.forEach(($t) =&gt; {\n      $t.addEventListener('change', (e) =&gt; {\n        this.toggle(e.currentTarget.checked);\n      });\n    });\n  },\n\n  \/**\n   * Keyboard listener for dark mode toggle\n   *\/\n  tabindexListener() {\n    const $darkSwitches = document.querySelectorAll('.dark-toggle__switch');\n\n    $darkSwitches.forEach(($s) =&gt; {\n      $s.addEventListener('keyup', (e) =&gt; {\n        if (e.key === 'Enter' || e.keyCode === 13) {\n          const $checkbox = e.currentTarget.closest('.dark-toggle').querySelector('input[type=\"checkbox\"]');\n          $checkbox.checked = !$checkbox.checked;\n          this.toggle($checkbox.checked);\n        }\n      });\n    });\n  },\n\n  \/**\n   * Toggle the body class and cache the variable\n   *\/\n  toggle(isChecked) {\n    document.querySelector('body').classList.toggle('is-dark', isChecked);\n    localStorage.setItem('darkMode', isChecked);\n  },\n};\n\ndocument.addEventListener('DOMContentLoaded', () =&gt; {\n  myDarkMode.init();\n});<\/code><\/pre>\n\n\n\n<p>The code above stored <code>darkMode<\/code> variable in <code>localStorage<\/code> every time we toggle it. But where&#8217;s the code to <strong>check on load?<\/strong><\/p>\n\n\n\n<p>Yes, we left that one out because it needs to be the <strong>first thing to run<\/strong>. So we have to place it right after opening <code>&lt;body&gt;<\/code> using hook like this:<\/p>\n\n\n\n<pre title=\"functions.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">add_action('wp_body_open', 'my_add_dark_mode_checker', 5);\n\nfunction my_add_dark_mode_checker() { ?&gt;\n&lt;script&gt;\n  (function() {\n    const darkMode = localStorage.darkMode === 'true';\n    if (darkMode) {\n      document.querySelector('body').classList.add('is-dark');\n\n      \/\/ activate the toggle\n      document.addEventListener('DOMContentLoaded', () =&gt; {\n        const $toggles = document.querySelectorAll('.dark-toggle input[type=\"checkbox\"]');\n        $toggles.forEach(($t) =&gt; {\n          $t.checked = true;\n        });\n      });\n    }\n  })();\n&lt;\/script&gt;\n&lt;?php }\n<\/code><\/pre>\n\n\n\n<p><strong>Note<\/strong>: Your theme might not have <code>wp_body_open()<\/code> hook placed.<\/p>\n\n\n\n<p>That&#8217;s all!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"adminbar-bug\">Fixing AdminBar Bug<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"322\" src=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2022\/08\/dark-mode-adminbar-bug.jpg\" alt=\"\" class=\"wp-image-2197\" srcset=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2022\/08\/dark-mode-adminbar-bug.jpg 750w, https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2022\/08\/dark-mode-adminbar-bug-480x206.jpg 480w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><figcaption>Adminbar Bug with CSS Filter<\/figcaption><\/figure>\n\n\n\n<p>When your body has a filter, the WP Admin Bar is placed incorrectly. Simply add this CSS to fix it:<\/p>\n\n\n\n<pre title=\"style.css\" class=\"wp-block-code\"><code lang=\"css\" class=\"language-css\">html[lang]\n {\n  margin-top: 0 !important\n}\n\n#wpadminbar {\n  position: sticky\n;\n}\n\n\/* Revert back the adminbar so it doesn't become White *\/\nbody.is-dark #wpadminbar {\n  filter: invert(100%) hue-rotate(180deg);\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n\n<p><strong>Method #1<\/strong> is much faster to implement. But your visitors won&#8217;t know that you have Dark Mode if their device isn&#8217;t set on Dark in the first place.<\/p>\n\n\n\n<p><strong>Method #2<\/strong> is clearer to visitors, but more complex to implement. Personally, I prefer this one.<\/p>\n\n\n\n<p>This method is indeed quick and easy. But it&#8217;s a <strong>dirty way<\/strong> of creating Dark Mode. The coloring might look off and images looks muted.<\/p>\n\n\n\n<p>If you would like to properly design a dark mode, the <a rel=\"noreferrer noopener\" href=\"https:\/\/material.io\/design\/color\/dark-theme.html\" data-type=\"URL\" data-id=\"https:\/\/material.io\/design\/color\/dark-theme.html\" target=\"_blank\">Android Dark Theme Guideline<\/a> is a very good read.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p>If you have any question regarding Dark Mode, let me know in the comment below<\/p><\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Dark Mode has risen in popularity in the past few years. But developing it can be difficult. We will take a look at 2 shortcuts for this.<\/p>\n","protected":false},"author":1,"featured_media":2200,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":["post-2164","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-general"],"blocksy_meta":"","_links":{"self":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/2164","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/comments?post=2164"}],"version-history":[{"count":26,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/2164\/revisions"}],"predecessor-version":[{"id":2202,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/2164\/revisions\/2202"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/media\/2200"}],"wp:attachment":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/media?parent=2164"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/categories?post=2164"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/tags?post=2164"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}