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;
}