51

How do I limit Bootstrap to just the scope of a div? I need to nest the Bootstrap stylesheet inside an ExtJS app for especific divs and both are clashing since they need their own body, ul, li, a, etc.

I'm trying to avoid editing bootstrap.css manually (or keeping it to a minimum) so that it can be easily updated.

ojosilva
  • 1,984
  • 2
  • 15
  • 21
  • Wouldn't it be cleaner to just strip out the styles you want instead of including the full bootstrap stylesheet? – Andres I Perez May 13 '12 at 21:03
  • 3
    I agree it would be cleaner, but then it would be harder to upgrade bootstrap versions, wouldn't it? – ojosilva May 14 '12 at 12:07
  • You can download a customized version of the bootstrap right on the documentation page, all you have to do is clear the things you don't want and download what you need. You can find that here: http://twitter.github.com/bootstrap/download.html .. so whenever there is an update you can just do the same and re-download what you need. – Andres I Perez May 14 '12 at 14:10

9 Answers9

55

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.

Andrew Homeyer
  • 7,937
  • 5
  • 33
  • 26
  • 3
    Yeah, I ended up doing something like that, but with an #id instead, since using a .class did not seem to work 100% in some cases: – ojosilva Jan 10 '13 at 16:28
  • 1
    @Andrew Homeyer - any chance you could offer a precompiled download of your scoped bootstrap package? I would greatly appreciate that. I can't bring myself to build an entire unix environment just to create some CSS... :| – x0n Jan 22 '13 at 19:07
  • 1
    The built css is here: https://github.com/homeyer/bootstrap/blob/master/docs/assets/css – Andrew Homeyer Jan 28 '13 at 05:50
  • Hi Andrew. Thanks for sharing it. Your fork is using FontAwesome for icons instead of the bootstraps sprite-image. Where can I download it from? – WooDzu Jun 10 '13 at 15:20
  • 4
    To make my life easier, I created a new repo that pulls in bootstrap as a submodule, with simple grunt tasks to build the scoped version: https://github.com/homeyer/scoped-twbs – Andrew Homeyer Sep 28 '13 at 23:33
  • While Andrew has put in some great work here I found the css resulting from this approach not to work as required. I instead ran [Pascal Pixel Rigaux's javascript solution](http://stackoverflow.com/a/20051649/998793) (in node.js) and this worked for me. His answer includes an explanation of why the above wrapping solution does not work in all cases. – rogersillito Jan 11 '16 at 10:19
  • Can anyone guide me how to run "make" ? – Vipul Hadiya May 01 '16 at 08:12
  • 1
    @VipulHadiya, you would need to create a `make.bat` (assuming you're in windows), I just refer this repo https://github.com/TheCopartnery/Make.Bat-to-build-Bootstrap-3-on-Windows and it works well. Hope it helps. – Haibara Ai Jun 08 '16 at 00:33
17

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 {
    
Community
  • 1
  • 1
  • 3
    I used your _Node.js_ script and I confirm that the result is much better than the less wrapping method. Everything is working flawlessly, many thanks! – Phil Mar 21 '14 at 11:06
  • Thanks! Works with v4 too, but not perfect. It breaks modal scroll. Do you have a list of known issues & fixes? – Marius Sep 03 '17 at 09:14
12

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;
}
PoseLab
  • 1,841
  • 1
  • 16
  • 22
9

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.

Alexey
  • 151
  • 1
  • 4
  • 1
    This method got tripped up on nested @media statements, but in my particular build that was only about 8 instances. All things considered, none too shabby! Thank you. – bahoo Oct 29 '14 at 18:44
  • At last I did replace ***.bootstrap-scope}\n*** for ***}\n*** for all @media – code-gijoe Apr 16 '15 at 16:00
5

Expanding on @Andrew Homeyer answer:

I got scoped styling of boostrap working in my Reactjs app by doing the following:

  1. Went here and downloaded bootstrap source
  2. Followed the guide here to install grunt. summary:

Bootstrap uses Grunt for its build system, with convenient methods for working with the framework. It's how we compile our code, run tests, and more.

Installing Grunt: To install Grunt, you must first download and install node.js (which includes npm). npm stands for node packaged modules and is a way to manage development dependencies through node.js.

Then, from the command line: Install grunt-cli globally with npm install -g grunt-cli. Navigate to the root /bootstrap/ directory, then run npm install. npm will look at the package.json file and automatically install the necessary local dependencies listed there. When completed, you'll be able to run the various Grunt commands provided from the command line.

  1. Edited the file less/bootstrap.less from the downloaded source to the following:

.bootstrap-scope {...pasted all @imports here...}

enter image description here

  1. Ran grunt dist and copied the new dist folder into my project
    in App.js import './styling/bootstrap/css/bootstrap.min.css';.

enter image description here

Similar discussion here

jtlindsey
  • 4,346
  • 4
  • 45
  • 73
5

no need to fork/edit original bootstrap. Just wrap its imports in your file like this (sass in my case): I created the file "bootstrap-scope-limit.scss" (that I import where I need bootstrap):

.bootstrap-inside {
  @import "~bootstrap-sass/assets/stylesheets/_bootstrap.scss";
  @import "~bootstrap-sass/assets/stylesheets/bootstrap/_theme.scss";
}

important notices!

please keep in mind that bootstrap has some body styles that wouldn't be applied when using such approach. But it gives you an ability to control bootstrap with JS by adding the "bootstrap-inside" class to the element to enable or disable bootstrap for the whole page if some dynamic content has to use bootstrap or not.

another concern you should aware about: some plugins that override bootstrap styles may have problems since bootstrap styles with new class wrapper become stronger (more specific) and can't be overridden by unwrapped plugins.


for webpack case I would recommend to use this loader: https://www.npmjs.com/package/wrap-css-loader to wrap the whole bundle, that you may use as a part of your app (div?) later

Roman86
  • 1,990
  • 23
  • 22
0

Expanding on @jtlindsey answer. If you want to use bootstrap 4 you have to do some little changes.

Instead of editing less/bootstrap.less you have to edit scss/bootstrap.scss

then you have to execute this command in order:

gem install bundler
bundle install
npm run dist

And the new bootstrap.min.css is generated in the dist folder.

Here you can have more information: https://getbootstrap.com/docs/4.0/getting-started/build-tools/

0

If you want a pure JavaScript solution with no outside libraries involved, you could go this route: The general idea is to load the raw css, then parse it line by line adding in your scoped selector where necessary.

function scopeCSS(scopeSelector, rawCSS) {
  return rawCSS.split('\n').map(line => {
    var trimmedLine = line.trim()
    if (trimmedLine.startsWith('*') || trimmedLine.startsWith('/*') || trimmedLine.startsWith('@media') || trimmedLine === '' || trimmedLine.endsWith(';') || trimmedLine.endsWith('}')) {
      return line
    }
    return line.split(',').map(selector => selector.trim()).map(selector => selector === '' ? selector : `${scopeSelector} ${selector}`).join(',');
  }).join('\n')
}
const rawCSS = document.querySelector('#before').textContent // just as an example, you'd probably use a raw loader with webpack
document.querySelector('#after').textContent = scopeCSS('.bootstrap-scope', rawCSS)
#grid {
  display: grid;
  width: 100%;
  grid-template-columns: 1fr 1fr;
}
<div id="grid">
  <div id="col-1">
    <pre id="before">/*!
 * Bootstrap Grid v5.0.0-alpha1 (https://getbootstrap.com/)
 * Copyright 2011-2020 The Bootstrap Authors
 * Copyright 2011-2020 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 */
.container,
.container-fluid,
.container-sm,
.container-md,
.container-lg,
.container-xl,
.container-xxl {
  width: 100%;
  padding-right: 1rem;
  padding-left: 1rem;
  margin-right: auto;
  margin-left: auto;
}

@media (min-width: 576px) {
  .container, .container-sm {
    max-width: 540px;
  }
}

@media (min-width: 768px) {
  .container, .container-sm, .container-md {
    max-width: 720px;
  }
}

@media (min-width: 992px) {
  .container, .container-sm, .container-md, .container-lg {
    max-width: 960px;
  }
}

@media (min-width: 1200px) {
  .container, .container-sm, .container-md, .container-lg, .container-xl {
    max-width: 1140px;
  }
}

@media (min-width: 1400px) {
  .container, .container-sm, .container-md, .container-lg, .container-xl, .container-xxl {
    max-width: 1320px;
  }
}

.row {
  --bs-gutter-x: 1.5rem;
  --bs-gutter-y: 0;
  display: flex;
  flex: 1 0 100%;
  flex-wrap: wrap;
  margin-top: calc(var(--bs-gutter-y) * -1);
  margin-right: calc(var(--bs-gutter-x) / -2);
  margin-left: calc(var(--bs-gutter-x) / -2);
}

.row > * {
  box-sizing: border-box;
  flex-shrink: 0;
  width: 100%;
  max-width: 100%;
  padding-right: calc(var(--bs-gutter-x) / 2);
  padding-left: calc(var(--bs-gutter-x) / 2);
  margin-top: var(--bs-gutter-y);
}

.col {
  flex: 1 0 0%;
}
/*# sourceMappingURL=bootstrap-grid.css.map */</pre>
  </div>
  <div id="col-2">
    <pre id="after"></pre>
  </div>
</div>
Andrew
  • 255
  • 1
  • 2
  • 7
-2

The only thing I know of that is for sure worse than this script is bootstrap itself, but this is probably still going to be the best answer here for a little while:

prefix="#bs431"; cat bootstrap.css | while IFS='' read -r line; do if echo "$line" | egrep -i '(\{|,)$' | egrep -iv '\@media' | wc -l | egrep -iq '^0$'; then echo "$line"; else echo "$line" | perl -pe 's/^(\s+)?/\1'"$prefix"' /g' | perl -pe 's/, (\.|#|\[|[a-zA-Z]{1})/, '"$prefix"' \1/g'; fi; done | perl -pe 's/'"$prefix"' :root/'"$prefix"'/g' > bootstrap.scoped.css

Jan Kyu Peblik
  • 1,435
  • 14
  • 20