2

I've got a chunk of CSS as a string, and I want to insert it into the DOM such that it only applies to elements in a particular container. Some tools, like Polymer, for example, rewrite CSS selectors so they only apply within a limited scope. How can I do something similar so that, when I insert this CSS into the DOM, it won't change all elements on the page?

To make it more concrete, imagine the following HTML and CSS from an external source:

<style>p { font-size: 20px; }</style>
<p>Boo.</p>

I want to insert these elements into a #container element, but I don't want to change the font-size for all <p> elements. I'd like to rewrite all the selectors inside that <style> element so they only apply within #container (p -> #container p, etc.). How?

Jeremie
  • 646
  • 8
  • 24
Trevor Dixon
  • 23,216
  • 12
  • 72
  • 109

1 Answers1

3

Use https://github.com/reworkcss/css to parse the CSS, then alter selectors, and finally stringify:

const CSS = require('css');

function scopeCSS(css, scope) {
  const ast = CSS.parse(css);
  for (let rule of ast.stylesheet.rules) {
    if (rule.type == 'rule') {
      rule.selectors = rule.selectors.map(selector => `${scope} ${selector}`);
    }
  }
  return CSS.stringify(ast);
}

scopeCSS('div { color: black; }', '#foo');
// #foo div {
//   color: black;
// }

http://requirebin.com/?gist=trevordixon/839d0674531dafa98fb95ae51474245e

Trevor Dixon
  • 23,216
  • 12
  • 72
  • 109
  • 1
    interesting so with this you can change any behaviour of css selector?can be more specific of how exactly you are achieveing this behaviour – Geeky Nov 30 '16 at 21:44
  • 1
    reworkcss/css is a CSS parser that takes in CSS as a string and returns an object describing the stylesheet. By modifying that object and passing it back to `CSS.stringify`, I can reliably change all the selectors. – Trevor Dixon Dec 01 '16 at 12:42