Override primary color in Bootstrap 4 depending on body class?

Viewed 4687

I have a website where each section has its own primary color.

I'd like to put something in Bootstrap 4's Sass code, to override the primary color depending on a body class I set.

I have this, but it's not working so far:

$theme-colors: () !default;
$theme-colors: map-merge((
    // primary: $blue,
    // secondary: $gray-600,
    success: $green,
    info: $cyan,
    warning: $yellow,
    danger: $red,
    light: $gray-100,
    dark: $gray-800
), $theme-colors) !default;

// override according to portal
html {
    &.portal-1 {
        $theme-colors: map-merge((
            primary: #f1b36d
        ), $theme-colors);
    }

How can this be implemented in Bootstrap 4's Sass file?

3 Answers

You can't change the sass variables which are already converted to static CSS dynamically at the client side. However, to build theme system you can apply one of the following options:

1. Generate independent theme

Put a theme argument in your build system which will generate different themes CSS Ex: grunt build-sass --theme=brown

var themes = {
    "default": "https://bootswatch.com/flatly/bootstrap.min.css",
    "cerulean" : "https://bootswatch.com/cerulean/bootstrap.min.css"
}

// Use something like this to add theme: (You can also append instead of overriding whole head html)
var headHTML = document.getElementsByTagName('head')[0].innerHTML;
headHTML    += '<link type="text/css" rel="stylesheet" href="' + themes.cerulean +'">';
document.getElementsByTagName('head')[0].innerHTML = headHTML;

2. Change properties based upon the parent class

You can have a base CSS which defines general CSS. And then you can have separate CSS Properties based upon the parent

In the following example update green-theme class to blue-theme and vice versa

div[class^=component] {
  display: inline-block;
  width:150px;
  height: 150px;
  margin: 1em;
  border: 1px solid black;
  box-shadow: 1px 1px 5px gray;
  border-radius:1em;
}

/* Colors*/
$blue1: #3066BE; 
$blue2: #090C9B;
$green1: #5C946E; 
$green2: #80C2AF;

/* Blue Theme */
.blue-theme {
  .component1 {
    background-color: $blue1;
  }
  
  .component2 {
    background-color: $blue2;
   }
}
/* Green Theme */
.green-theme {
  .component1 {
    background-color: $green1;
  }
  
  .component2 {
    background-color: $green2;
   }
}
<div class="green-theme" id="mainBody">
    <div class="component1">
    </div>
    
     <div class="component2">
    </div>
</div>

*Run Snippet won't work as we are using SCSS *

Make a scss file structure like this,

- vendors
  - bootstrap
    - stylesheets /*[Bootstrap 4 official sass files]*/
      - _bootstrap.scss
- scss
  - themes
    - _theme-1.scss
    - _theme-2.scss
  - _variables.scss
  - styles.scss
  - portal-1.scss
  - portal-2.scss

_variables.scss

@import "../vendors/bootstrap/stylesheets/bootstrap/variables";
@import "../vendors/bootstrap/stylesheets/bootstrap/mixins";
/* override bootstrap default variables ... */
/* custom variables with !default ... */

styles.scss

@import "variables";
@import "../vendors/bootstrap/stylesheets/bootstrap";
/* custom styles ... */

themes/_theme-1.scss

@import "../variables";
/* change value bootstrap and custom default variables ... */
$brand-primary:         #428bca
$brand-success:         #5cb85c;
$brand-info:            #5bc0de;
$brand-warning:         #f0ad4e;
$brand-danger:          #d9534f;
$body-bg:               #eff;

themes/_theme-2.scss

@import "../variables";
/* change value bootstrap and custom default variables ... */
$brand-primary:         #C04848;

portal-1.scss

@import "themes/_theme-1";
/* change style with new variable bootstrap default components ... */
.portal-1 {
    @import "../vendors/bootstrap/stylesheets/bootstrap/buttons";
    @import "../vendors/bootstrap/stylesheets/bootstrap/alerts";
   /* custom style overrides ... */
}

portal-2.scss

@import "themes/_theme-2";
/* change style with new variable bootstrap default components ... */
.portal-2 {
    @import "../vendors/bootstrap/stylesheets/bootstrap/buttons";
    @import "../vendors/bootstrap/stylesheets/bootstrap/alerts";
   /* custom styles from style.scss overrides ... */
}

After sass compilation we will get 3 files styles.css, portal-1.css and portal-2.css. Use style.css by default and others include in head tag for theming.

<html>
    <head>
         <link href="styles.css" rel="stylesheet" />
         <link href="portal-1.css" rel="stylesheet" />
    </head>
    <body>
        <button class="btn btn-primary">BUTTON</button>
        <div class="portal-1">
            <button class="btn btn-primary">BUTTON</button>
        </div>
    </body>
</html>

NOTE: After searching a while for a simple solution, I though I could avoid the pain for anyone trying to achieve this:

You can use CSS variables to achieve this.

There is a thread on the main bootstrap github page Github Issue and user @johanlef has posted a gist showing his method and the relevant code gist.

Then you can just make sure the css variables is initialized in you page

.body.theme1{
   --primary: #5d8be5;
}

I used for one of my multi tenanted website and it works great.

Related