Dark Mode with two CSS theme files
Basic Setup With no user toggle support
- Two fully themed Bootstrap CSS are required
- Filtered by stylesheet media filters
- Based on advice by Thomas Steiner in Hello darkness, my old friend.
- JavaScript fallback if the browser does not support dark-mode
- Does not require jQuery
Replace the bootstrap stylesheet with the following code:
<!-- Bootstrap CSS -->
<!-- Inform modern browsers that this page supports both dark and light color schemes,
and the page author prefers light. -->
<meta name="color-scheme" content="light dark">
<script>
// If `prefers-color-scheme` is not supported, fall back to light mode.
// i.e. In this case, inject the `light` CSS before the others, with
// no media filter so that it will be downloaded with highest priority.
if (window.matchMedia("(prefers-color-scheme: dark)").media === "not all") {
document.documentElement.style.display = "none";
document.head.insertAdjacentHTML(
"beforeend",
"<link id=\"css\" rel=\"stylesheet\" href=\"bootstrap.css\" onload=\"document.documentElement.style.display = ''\">"
);
}
</script>
<!-- Load the alternate CSS first ... -->
<link id="css-dark" rel="stylesheet" href="bootstrap-night.css" media="(prefers-color-scheme: dark)">
<!-- ... and then the primary CSS last for a fallback on very old browsers that don't support media filtering -->
<link id="css-light" rel="stylesheet" href="bootstrap.css" media="(prefers-color-scheme: no-preference), (prefers-color-scheme: light)">
This is all you need to enable dark mode with Bootstrap.
You also have the flexibility to give the user control with a toggle switch, but that requires some additional code.
Additional Setup Giving user control
The basic principle is honouring the browser preference (which we assume the user set with intent), but also provide a switch in the UI. Once this switch has been clicked on, swap the color mode and remove browser based color scheme selection.
The following code will require jQuery:
<script>
$(document).ready(function(){
// Update the toggle button based on current color scheme
function updateDarkToggleButton() {
$dark = (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches);
$("#css-toggle-btn").prop( "checked", $dark );
}
// Update on first load.
updateDarkToggleButton();
// and every time it changes
if (window.matchMedia) window.matchMedia("(prefers-color-scheme: dark)").addListener( updateDarkToggleButton );
// Color Scheme toggle botton
// function to initialise the css
function init_color_scheme_css($id, $mode) {
if ($("#"+$id)) $("#"+$id).remove(); // remove exitsing id
$("#"+$id+"-"+$mode).attr( {
"data-href-light": $("#"+$id+"-light").attr("href"), // store the light CSS url
"data-href-dark": $("#"+$id+"-dark").attr("href"), // store the dark CSS url
"data-color-scheme": $mode, // store the mode, so that we don't re-initalise
"media": "all", // drop the media filter
"id": $id // rename the id (drop the `-{mode}` bit)
} );
$other = ($mode == 'dark') ? 'light' : 'dark';
$("#"+$id+"-"+$other).remove();
}
// function to toggle the CSS
function toggle_color_scheme_css($id, $mode) {
// grab the new mode css href
$href = $("#"+$id).data("href-"+$mode); // use `.data()` here, leverage the cache
// set the CSS to the mode preference.
$("#"+$id).attr( {
"href": $href,
"data-color-scheme": $mode,
});
}
// toggle button click code
$("#css-toggle-btn").bind("click", function() {
// get current mode
// don't use `.data("color-scheme")`, it doesn't refresh
$mode = $("#css").attr("data-color-scheme");
// test if this is a first time click event, if so initialise the code
if (typeof $mode === 'undefined') {
// not defined yet - set pref. & ask the browser if alt. is active
$mode = 'light';
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) $mode = 'dark';
init_color_scheme_css("css", $mode);
// `init_color_scheme_css()` any other CSS
}
// by here we have the current mode, so swap it
$new_mode = ($mode == 'dark') ? 'light' : 'dark';
toggle_color_scheme_css("css", $new_mode);
// `toggle_color_scheme_css()` any other CSS
});
});
</script>
Remember that the above code does not include a persistence layer to remember a user toggled preference.
Read the README.md for more on this proof of concept.