In this article, we are going to take a look at how to implement dark mode on a WordPress website without using a plug-in or a third-party library or solution.
For this specific used case, I had several requirements. First, when the user toggled dark mode, I wanted it to immediately appear on the website without having to reload the page. I also wanted this to remember the users preference, whether they were logged in or logged out. To make this functional, I decided to use cookies to remember the preference if the user is logged out, but the more durable WordPress database, if the user is logged in.
The actual toggling of the dark mode is relatively simple. All we do is add a class to the body, which can then be used with CSS to create a dark mode style sheet.
/* Regular Card Style */
.custom_card {
background:white;
}
/* Dark Mode Card Style */
body.cwp_dark .custom_card {
background:black;
}
The Main Snippet
To test out the capabilities of CodeWP, I wanted to create the snippet in one go. The benefit of doing it with a single prompt is that the code will be coherent and related, meaning that I won't need to go in and switch out placeholder classes and other generic elements.
Here's the prompt. When entering into the generator, I excluded the bullet points and just used line breaks.
- Shortcode for checkbox called "darkmode".
- If user is logged in and checkbox is checked, set user meta field of "cwp_dark" to 1, if unchecked set to 0, using ajax. If field is equal to 1, set body class to "cwp_dark".
- Include ajax function code.
- If user isn't logged in and checkbox is checked, set cookie "cwp_dark", If not checked remove cookie. If cookie exists, set body class to "cwp_dark".
- If usermeta is 1, or cookie exists, add the class "cwp_dark" to the body, and make checkbox checked.
- Add js to footer that toggles the class "cwp_dark" based on checkbox state (without reloading page).
The thought process for this prompt is relatively simple. First, we're going to make a short code to add the toggle element onto the front end.
Then, if that is toggled, and the user is logged in, we're going to use Ajax to update that users preference in the database. If the user isn't logged in, we will set a cookie which will save the users preference in the browser.
If checked, or if dark mode is toggled on, either by the cookie or the user meta-value, we're going to a class to the body.
Finally, I also wanted to be sure to include JavaScript that would add the class to the body without needing to reload the page. So, when a user checks the checkbox, it instantaneously switches between dark and light mode, while remembering the users preference when they access the website in the future or navigate to a new page.
And here's what we get.
function cwpai_darkmode_shortcode() {
if (is_user_logged_in()) {
$user_id = get_current_user_id();
$darkmode = get_user_meta($user_id, 'cwp_dark', true);
if ($darkmode == 1) {
$checked = 'checked';
} else {
$checked = '';
}
$output = '<input type="checkbox" id="cwp_darkmode" name="cwp_darkmode" value="1" '.$checked.'>';
$output .= '<label for="cwp_darkmode">Dark Mode</label>';
$output .= '<script>
jQuery(document).ready(function($) {
$("#cwp_darkmode").change(function() {
var darkmode = $(this).prop("checked");
if (darkmode == true) {
var darkmode = 1;
} else {
var darkmode = 0;
}
$.ajax({
type: "POST",
url: "'.admin_url('admin-ajax.php').'",
data: {
action: "cwpai_darkmode",
darkmode: darkmode
},
success: function(data) {
if (darkmode == 1) {
$("body").addClass("cwp_dark");
} else {
$("body").removeClass("cwp_dark");
}
}
});
});
});
</script>';
} else {
if (isset($_COOKIE['cwp_dark'])) {
$checked = 'checked';
} else {
$checked = '';
}
$output = '<input type="checkbox" id="cwp_darkmode" name="cwp_darkmode" value="1" '.$checked.'>';
$output .= '<label for="cwp_darkmode">Dark Mode</label>';
$output .= '<script>
jQuery(document).ready(function($) {
$("#cwp_darkmode").change(function() {
var darkmode = $(this).prop("checked");
if (darkmode == true) {
document.cookie = "cwp_dark=1; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/";
$("body").addClass("cwp_dark");
} else {
document.cookie = "cwp_dark=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
$("body").removeClass("cwp_dark");
}
});
});
</script>';
}
return $output;
}
add_shortcode('darkmode', 'cwpai_darkmode_shortcode');
function cwpai_darkmode_ajax() {
$user_id = get_current_user_id();
$darkmode = $_POST['darkmode'];
update_user_meta($user_id, 'cwp_dark', $darkmode);
die();
}
add_action('wp_ajax_cwpai_darkmode', 'cwpai_darkmode_ajax');
add_action('wp_ajax_nopriv_cwpai_darkmode', 'cwpai_darkmode_ajax');
function cwpai_darkmode_body_class($classes) {
if (is_user_logged_in()) {
$user_id = get_current_user_id();
$darkmode = get_user_meta($user_id, 'cwp_dark', true);
if ($darkmode == 1) {
$classes[] = 'cwp_dark';
}
} else {
if (isset($_COOKIE['cwp_dark'])) {
$classes[] = 'cwp_dark';
}
}
return $classes;
}
add_filter('body_class', 'cwpai_darkmode_body_class');
It works perfectly on the first go (as long as you've already created your custom dark mode stylesheet). To improve performance on a production site, I'd move the jQuery from the shortcode and enqueue it in the footer of the frontend.
function cwpai_enqueue_custom_js() {
global $post;
if (is_a($post, 'WP_Post') && has_shortcode($post->post_content, 'need_to_be_here')) {
wp_enqueue_script('custom', get_stylesheet_directory_uri() . '/custom.js', array(), '1.0', true);
}
}
add_action('wp_enqueue_scripts', 'cwpai_enqueue_custom_js');
Prompt:// Enqueue file "custom.js" in footer, only if shortcode "need_to_be_here" is present on the page.
^^This is an elegant way to do just that.
The only thing that I want to do now is style that checkbox to be a little more visually appealing. To do this, I'll jump into CodePen, find some CSS to style a dark mode toggle, and add it to my project.
And just like that, you have a pretty robust dark mode feature added to your WordPress website without the need for a plugin.