Limit the scope of bootstrap styles

You can fork the bootstrap repo and change bootstrap.less to wrap the bootstrap styles:

.bootstrap-scope {
    //put bootstrap @includes in here
}

Then, in the root of the bootstrap project, run make to generate the bootstrap.css file.

You'll lose the global styles bootstrap assigns to the body and html tags, but you might not want them anyway.

And then, in your app, give the container you want the .bootstrap-scope class.


Since the result of modifying less/bootstrap.less was not good enough for me (see why at bottom of post), I ended up filtering bootstrap.css :

var css = require('css'), fs = require('fs');

var prefix = '.bootstrap-scope';
var inFile = 'bootstrap.css';

var cssAst = css.parse(fs.readFileSync(inFile, 'utf8'));
prefixNode(cssAst);
console.log(css.stringify(cssAst));

function prefixSelector(sel){
    if (sel.match(/^@/)) return sel;
    var m = sel.match(/(^| )(body|html)($|\W.*)/i);
    if (m) 
        return m[1] + prefix + m[3];
    else
        return prefix + ' ' + sel;
}

function prefixNode(node) {
    if (node.selectors) {
        node.selectors = node.selectors.map(prefixSelector);
    } else if (node.stylesheet) {
        node.stylesheet.rules.forEach(prefixNode);
    } else if (node.rules) {
        node.rules.forEach(prefixNode);
    }
}

Since bootstrap.css is generated, one can also do it blindly (solution similar to Alexey's, but also handling a few corner cases):

perl -lpe 'BEGIN { $prefix = ".bootstrap-scope" } if (($indent, $s, $sep) = /^(\s*)([^@\s].*?)(\s*[,{])$/) { $s =~ /^(from|to)*$/ or $s =~ s/(^| )(body|html)($|\W.*)/$1$prefix$3/i or $s = "$prefix $s"; $_ = "$indent$s$sep" }' bootstrap.css

As for modifying less/bootstrap.css and calling grunt to generate dist/css/bootstrap.css (Andrew Homeyer's solution), it seems not working nicely (in bootstrap 3 at least) for some cases:

  • various mixins like .make-grid-columns (from less/mixins.xml) end up badly prefixed. Example:

    .bootstrap-scope .col-sm-1,
      .col-sm-2,
      .col-sm-3,
    
  • the usage of "&" in LESS is limited:

    .caret {
      .btn-default & {
    

    becomes

     .btn-default .bootstrap-scope .caret {
    

    instead of what we want:

     .bootstrap-scope .btn-default .caret {
    

There is way much easier way to achieve this legitimate requirement: replace }NEWLINE and ,NEWLINE with previous value and your class scope. It's very easy to to in e.g. Sublime:

  • Select }\r\n

  • Alt + F3 (to select all occurrences)

  • Replace with }\r\n.bootstrap-scope

  • Select ,\r\n

  • Alt + F3 (to select all occurrences)

  • Replace with ,\r\n.bootstrap-scope

That's it! It will be pretty easy to update it going forward since replacement takes about a minute. Hope my answer helped.


If you have the following file structure:

  • mystyles.less
  • mystyles.css
  • /bootstrap/
    • bootstrap.less
    • bootstrap.css
    • grid.less
    • ...
    • /mixins/

And you want to limit the scope of bootstrap, you have to put in your mystyles.less (right way):

.your-class-to-limit-bootstrap-scope{
    @import (less) "bootstrap/bootstrap.css";
}

We have to import the bootstrap.css file instead of bootstrap.less, but using the (less) @import option in order to treat the file as a Less file, no matter what the file extension. So, you will get the correct css, for example:

.your-class-to-limit-bootstrap-scope .col-xs-1,
.your-class-to-limit-bootstrap-scope  .col-sm-1,
.your-class-to-limit-bootstrap-scope ...
.your-class-to-limit-bootstrap-scope .col-lg-12 {
    position: relative;
    min-height: 1px;
    padding-left: 7px;
    padding-right: 7px;
 }

But if you import bootstrap less files you will get a incorrectly scoped css (wrong way):

.your-class-to-limit-bootstrap-scope{
    @import "bootstrap/bootstrap.less";
}

So, you will get the incorrect scoped css, for example:

.your-class-to-limit-bootstrap-scope .col-xs-1, 
.col-sm-1,
.col-md-1,
...
.col-lg-12 {
    position: relative;
    min-height: 1px;
    padding-left: 15px;
    padding-right: 15px;
}