Dark Mode with a base CSS theme file, and a deltas add-on

Basic Setup With no user toggle support

  • A fully themed Bootstrap CSS as a base file
  • And a deltas add-on CSS with only the changes, filtered by stylesheet media filter
  • Does not require jQuery
  • Does not require fallback code

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">
<!-- Load the primary CSS first ... -->
<link id="css-light" rel="stylesheet" href="bootstrap.css">
<!-- ... and then the alternate CSS first as a snap-on for dark color scheme preference -->
<link id="css-dark" rel="stylesheet" href="bootstrap-nightfall.css" media="(prefers-color-scheme: dark)">

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 .

Additional Setup Giving user control

The basic principle is honouring the browser preference (which we assume the user set with intent), detecting this state and setting a `dark` data variable in the body. We also update the toggle switch in the UI. Once this switch has been clicked on, iether enable or disable the snap-on CSS based on the color scheme selection.

The following code will require jQuery:

<script>
  $(document).ready(function(){

    // Update the toggle button based on current color scheme

    function updateDarkToggleButton() {
      if (typeof $("body").attr("data-color-scheme") === 'undefined') {
        $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 toggle the css
    function toggle_color_scheme_css($id, $mode) {
      $dark = ($mode == 'dark') ? true : false;
      $("#"+$id+"-dark").attr( "disabled", !$dark );
      $("body").attr( "data-color-scheme", ($dark ? "dark" : "light" ) );
    }

    // function to initialise the css
    function init_color_scheme_css($id, $mode) {
      $dark = ($mode == 'dark') ? true : false;
      toggle_color_scheme_css($id, $mode);
      setTimeout(function(){  // let the browser catch up
        $("#"+$id+"-dark").removeAttr("media");
      }, 100);
    }

    // toggle button click code
    $("#css-toggle-btn").bind("click", function() {
      // get current mode
      // don't use `.data("color-scheme")`, it doesn't refresh
      $mode = $("body").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);
      }
      // by here we have the current mode, so swap it
      $mode = ($mode == 'dark') ? 'light' : 'dark';
      toggle_color_scheme_css("css", $mode);
    });

  });
</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.