How to Properly Introduce a Light/Dark Mode in Bootstrap

How to properly introduce a light/dark mode in Bootstrap?

Bootstrap 5 (update 2021)

Although there is still no definitive support for light/dark mode in Bootstrap 5, SASS can be used to create a dark theme

Bootstrap 4

Here are some answers to your question on Bootstrap light or dark mode:

"Am I supposed to replace all occurrences of bg-dark with bg-light
once the "switch" is turned on?"

Yes, but you'd probably also want to switch all -light and -dark classes such as text-dark, navbar-dark, btn-dark, etc..

If I want to slightly modify the colors of bg-light and bg-dark.. I
can't find any examples to override these variables (via SASS), except
of manually overwrite them in my CSS like .bg-dark...

These are derived from $light and $dark SASS variables so you can change them like this...

$light: #dddddd;
$dark: #011100;

@import "bootstrap";

Demo Bootstrap Light Dark Mode Switch

Also see:

Customizing Bootstrap CSS template

How to extend/modify (customize) Bootstrap 4 with SASS

Create new color scheme for dark-light mode in bootstrap sass

As explained here, there's no way to attach a class to :root. However, you don't need this to achieve what you want.

Simply make a dark class then you can add that as desired to the html or body tag.

Make all the needed theme color changes inside .dark{}, and then @import "bootstrap". When .dark doesn't exist on the body, the theme colors will return to Bootstrap defaults.

@import "functions";
@import "variables";
@import "mixins";

.dark {

/* redefine theme colors for dark theme */
$primary: #012345;
$secondary: #111111;
$success: #222222;
$dark: #000;

$theme-colors: (
"primary": $primary,
"secondary": $secondary,
"success": $success,
"danger": $danger,
"info": $indigo,
"dark": $dark,
"light": $light,
);

/* redefine theme color variables */
@each $color, $value in $theme-colors {
--#{$variable-prefix}#{$color}: #{$value};
}

/* redefine theme color rgb vars (used for bg- colors) */
$theme-colors-rgb: map-loop($theme-colors, to-rgb, "$value");
@each $color, $value in $theme-colors-rgb {
--#{$variable-prefix}#{$color}-rgb: #{$value};
}

$body-color: #eeeeee;
$body-bg: #263C5C;

--#{$variable-prefix}body-color: #{$body-color};
--#{$variable-prefix}body-bg: #{$body-bg};

@import "bootstrap";
}

Bootstrap 5 Dark Theme

The most appropriate way to implement dark/light/... mode in Bootstrap 5?

Implementing dark mode for bootstrap is a long task as there are multiple variables that need to be changed.The simplier aproach will be using var() with .dark/.light in root, but still there are functions of bootstrap that needs a color to function properly.

How to change form-select Option Select to dark mode

Add the class bg-dark to make the background dark and text-white to achieve a better contrast.

<select class="form-select bg-dark text-white">
<option value="1" selected>1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>

How to dynamically set text color according to background in Bootstrap SASS?

You can iterate through the theme-colors(), test lightness/hue, and then extend text-light as needed...

@each $color, $value in $theme-colors {
@if ((lightness($value)) < 60 and (hue($value)) > 45) {
.bg-#{$color} {
@extend .text-light
}
}
}

Demo on Codeply

How to extend/modify (customize) Bootstrap with SASS

Update 2022 (Bootstrap 5)

Generally speaking, customizing Bootstrap 5 SASS works the same way as it did in Bootstrap 4. However, some of the maps have changed (and new ones have been added) so you should follow the Bootstrap SASS documentation for details on how to add, remove or modify any of the maps.

Recent comments on this post indicate there's still some confusion about the order of customizations and imports. However, this concept hasn't changed...

"Variable overrides must come after our functions are imported, but before the rest of the imports"

Because of the way SASS variable defaults work, bootstrap ("the rest of the imports") should be imported AFTER any variable customizations/changes. Since your variable changes will not contain the !default flag, your changes will not be overridden by the Bootstrap defaults when bootstrap is imported at the end.

The proof is in the pudding


Here's how to override / customize Bootstrap 4 with SASS...

Overrides and customizations should be kept in a separate custom.scss file that is separate from the Bootstrap SASS source files. This way any changes you make don't impact the Bootstrap source, which makes changes or upgrading Bootstrap later much easier.

1- Consider Bootstrap's SASS folder structure, alongside your custom.scss...

|-- \bootstrap
| |-- \scss
| | |-- \mixins
| | |-- \utilities
| | |-- bootstrap.scss
| | |-- variables.scss
| | |-- functions.scss
| | |-- ...more bootstrap scss files
| custom.scss

2- In your custom.scss, import the Bootstrap files that are needed for the overrides. (Usually, this is just variables.scss. In some cases, with more complex cutomizations, you may also need the functions, mixins, and other Bootstrap files.). Make the changes, then @import "bootstrap". It's important to import Bootstrap after the changes...

/* custom.scss */    

/* import the necessary Bootstrap files */
@import "bootstrap/functions";
@import "bootstrap/variables";

/* make changes to the !default Bootstrap variables */
$body-color: green;

/* finally, import Bootstrap to set the changes! */
@import "bootstrap";

2a (optional) - Also, you can extend existing Bootstrap classes after the @import "bootstrap"; to create new custom classes. For example, here is a new .row-dark class that extends (inherits from) the Bootstrap .row class and then add a background-color.

 /* create new custom classes from existing classes */
.row-dark {
@extend .row;
background-color: #333333;
color: #ffffff;
}

3- Compile the SASS (node-sass, gulp-sass, webpack/NPM, etc..). The CSS output will contain the overrides! Don't forget to check the includePaths if your @imports fail. For a full list of variables you can override, see the variables.scss file. There are also these global variables.

Bootstrap SASS Demo on Codeply

In summary, here's how it works:

1_ First, when the custom.scss file is processed using SASS, the !default values are defined in the bootstrap/variables.scss

2_ Next, our custom values are set, which will override any of the variables that had !default values set in bootstrap/variables.scss

3_ Finally, Bootstrap is imported (@import "bootstrap") which enables the SASS processor (A.K.A. compiler) to generate all the appropriate CSS using both the Bootstrap defaults and the custom overrides.

For those that don't know SASS, try this tool that I made.


Also see:

How to get 15 columns in Bootstrap 4 in SASS CSS?

Bootstrap v4 grid sizes / Sass List

Customizing Bootstrap CSS template

Extending Bootstrap 4 and SASS

Specify hover colours directly in customized Bootstrap 4

The mixin used to generate the button css colour states is called button-variant() which is located in bootstrap/scss/mixins.

This sass mixin does have the parameter capability for handling custom .btn hover and active colour states...

@mixin button-variant(
$background,
$border,
$hover-background: darken($background, 7.5%),
$hover-border: darken($border, 10%),
$active-background: darken($background, 10%),
$active-border: darken($border, 12.5%)
) {
//...
}

But as you can see the hover and active params are $default: params using sass colour darken() function as values to calculate the passed $theme-colors colour value to automatically generate colours for the states.

This makes it impossible to handle button state colours by just using $theme-colors.







There are a few ways to customise the .btn active and hover state
colours whilst minimising compiled css and avoiding
duplicate/overriding selectors in compiled css output.

First method (easiest)

See scss below which handles bootstrap vendor @imports individually and handling custom vars and map-removals in the correct order, so bootstrap compiles and uses variables correctly.

Follow comments in the scss so you know what is happening...

// import initial bootstrap vendors

@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins";

// remove unused default bootstrap colours from bs colour maps to prevent larger compiled css file sizes
// this will also speed up compiling time as you are not generating css for all bootstrap standard colours
// if you wish to keep some standard bootstrap colours then remove the ones you want to keep from the map-remove lists below

$colors: map-remove($colors, "blue","indigo","purple","pink","red","orange","yellow","green","teal","cyan","white","gray","gray-dark");
$grays: map-remove($grays, "100","200","300","400","500","600","700","800","900");
$theme-colors: map-remove($theme-colors, "primary","secondary","success","danger","warning","info","light","dark");

// use sass @debug to check dump logs of your colour maps after modifying them if you wish...

// @debug $colors;
// @debug $grays;
// @debug $theme-colors;

// now add your custom colour variables

$new-color-1: #00a89c;
$new-color-2: #e61b53;

// now add your custom colour maps using above variables

$colors: (
"newcolor1": $new-color-1,
"newcolor2": $new-color-2
);

// you can always tidy this up by including these variables and any other bootstrap theming override variables into a separate sass _vars.scss file
// and then import _vars.scss here (before root.scss and after mixins.scss|map-removals)...

//@import "./vars";

// now import the rest of your needed bootstrap scss vendor files below this
// the below imports will now use any variable overrides you've defined above

// if you want to reduce you css output file size and speed up compiling
// you can remove unused scss vendor imports below, for example i've removed breadcrumb, toasts and carousel

@import "~bootstrap/scss/root";
@import "~bootstrap/scss/reboot";
@import "~bootstrap/scss/type";
@import "~bootstrap/scss/images";
@import "~bootstrap/scss/code";
@import "~bootstrap/scss/grid";
@import "~bootstrap/scss/tables";
@import "~bootstrap/scss/forms";
@import "~bootstrap/scss/buttons";
@import "~bootstrap/scss/transitions";
@import "~bootstrap/scss/dropdown";
@import "~bootstrap/scss/button-group";
@import "~bootstrap/scss/input-group";
@import "~bootstrap/scss/custom-forms";
@import "~bootstrap/scss/nav";
@import "~bootstrap/scss/navbar";
@import "~bootstrap/scss/card";
//@import "~bootstrap/scss/breadcrumb";
@import "~bootstrap/scss/pagination";
@import "~bootstrap/scss/badge";
@import "~bootstrap/scss/jumbotron";
@import "~bootstrap/scss/alert";
@import "~bootstrap/scss/progress";
@import "~bootstrap/scss/media";
@import "~bootstrap/scss/list-group";
@import "~bootstrap/scss/close";
//@import "~bootstrap/scss/toasts";
@import "~bootstrap/scss/modal";
@import "~bootstrap/scss/tooltip";
@import "~bootstrap/scss/popover";
//@import "~bootstrap/scss/carousel";
@import "~bootstrap/scss/spinners";
@import "~bootstrap/scss/utilities";
@import "~bootstrap/scss/print";

Because we emptied the $theme-colors map, bootstrap will not compile @each loops which utilise $theme-colors because it is now empty.

We can now manually use button-variant() mixin inside any .btn-* selector to create custom buttons without add extra css to the compiled output.

This option is easiest because you can pass any colour you wish to the button-variant() params.

.btn-primary {
@include button-variant(
$new-color-1, // background
$new-color-1, // border
$new-color-1, // hover background (customise how you wish)
$new-color-1, // hover border (customise how you wish)
$new-color-1, // active background (customise how you wish)
$new-color-1 // active border (customise how you wish)
);
}

.btn-secondary {
@include button-variant(
$new-color-2, // background
$new-color-2, // border
$new-color-2, // hover background (customise how you wish)
$new-color-2, // hover border (customise how you wish)
$new-color-2, // active background (customise how you wish)
$new-color-2 // active border (customise how you wish)
);
}






Second method (trickier)

Using the above code, re-introduce $theme-colors and modify/override the @include button-variant() in mixins.scss.

Removed the custom button selectors from the above code before continuing.

This method is trickier to precisely control hover and active state colours because @include button-variant() will use the same param scss colour adjustments for each passed colour.

You can try this... re-introduce $theme-colors before root.scss and immediately after $colors map array.

$theme-colors: (
"primary": $new-color-1,
"secondary": $new-color-2
);

Then create an overriding button mixin to handle all button variant calls.

Add this bootstrap v4.5.3 modified button-variant mixin after our @import "~bootstrap/scss/mixins"; and before the map-removals.

You can put this in a sass file called _mixins.scss in your project and import like this @import "./mixins"; to keep things tidy.

You can also keep all your custom mixins for this project in "./mixins" too.

Now we can modify the custom @mixin button-variant() parameters to handle passed $theme-colors.

// i've customised params in the button-variant() mixin below as an example
// see original default mixin params as comments below

// $background
// $border
// $hover-background: darken($background, 7.5%)
// $hover-border: darken($border, 10%)
// $active-background: darken($background, 10%)
// $active-border: darken($border, 12.5%)

@mixin button-variant(
$background,
$border,
$hover-background: lighten($background, 10%),
$hover-border: lighten($border, 10%),
$active-background: lighten($background, 2.5%),
$active-border: lighten($border, 2.5%)
) {
color: color-yiq($background);
@include gradient-bg($background);
border-color: $border;
@include box-shadow($btn-box-shadow);

@include hover() {
color: color-yiq($hover-background);
@include gradient-bg($hover-background);
border-color: $hover-border;
}

&:focus,
&.focus {
color: color-yiq($hover-background);
@include gradient-bg($hover-background);
border-color: $hover-border;
@if $enable-shadows {
@include box-shadow($btn-box-shadow, 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5));
} @else {
// Avoid using mixin so we can pass custom focus shadow properly
box-shadow: 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5);
}
}

// Disabled comes first so active can properly restyle
&.disabled,
&:disabled {
color: color-yiq($background);
background-color: $background;
border-color: $border;
// Remove CSS gradients if they're enabled
@if $enable-gradients {
background-image: none;
}
}

&:not(:disabled):not(.disabled):active,
&:not(:disabled):not(.disabled).active,
.show > &.dropdown-toggle {
color: color-yiq($active-background);
background-color: $active-background;
@if $enable-gradients {
background-image: none; // Remove the gradient for the pressed/active state
}
border-color: $active-border;

&:focus {
@if $enable-shadows and $btn-active-box-shadow != none {
@include box-shadow($btn-active-box-shadow, 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5));
} @else {
// Avoid using mixin so we can pass custom focus shadow properly
box-shadow: 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5);
}
}
}
}

How to change Bootstrap 4's border-bottom-width via SASS?

I ended up simply keeping the !important for overriding and just made myself another scss file, and written the rules there:

_variables.scss

$border-widths:             1px 1px 4px 1px;

_custom.scss

.border-thick-bottom{ border-width:$border-widths!important; border-style:solid; }


Related Topics



Leave a reply



Submit