Skip to content

topaxi/postcss-selector-namespace

Repository files navigation

postcss-selector-namespace Build Status Test Coverage Code Climate

Installation

$ npm install postcss-selector-namespace

Usage

var postcss = require('postcss')
var selectorNamespace = require('postcss-selector-namespace')

var output = postcss()
  .use(selectorNamespace({ selfSelector: ':--component', namespace: 'my-component' }))
  .process(require('fs').readFileSync('input.css', 'utf8'))
  .css

input.css

:--component {
  color: black;
}

:--component.danger {
  color: red;
}

h1, .h1 {
  font-weight: bold;
}

will output the following css:

.my-component {
  color: black;
}

.my-component.danger {
  color: red;
}

.my-component h1, .my-component .h1 {
  font-weight: bold;
}

Prepending :root to a selector prevents the selector from being namespaced:

:root h1 {
  font-weight: bold;
}

will output the selector without any namespace:

h1 {
  font-weight: bold;
}

SCSS support

This plugin can also process scss files and output scss again using the postcss-scss module.

var postcss = require('postcss')
var postscss = require('postcss-scss')
var selectorNamespace = require('postcss-selector-namespace')

var output = postcss()
  .use(selectorNamespace({ selfSelector: '&', namespace: 'my-component' }))
  .process(require('fs').readFileSync('input.css', 'utf8'), { syntax: postscss })
  .css
$break = 320px;

& {
  float: left;
  width: 250px;
  h1 {
    font-weight: bold;
    font-size: 32px;
  }
  @media screen and (max-width: $break-small) {
    width: 100px;
    float: none;
    h1 {
      font-size: 24px;
    }
  }
}

outputs:

$break = 320px;

.my-component {
  float: left;
  width: 250px;
  h1 {
    font-weight: bold;
    font-size: 32px;
  }
  @media screen and (max-width: $break-small) {
    width: 100px;
    float: none;
    h1 {
      font-size: 24px;
    }
  }
}

Example setup with postcss-import

Using the excellent plugin postcss-import, we can easily namespace each component with its filename.

components/my-button.css

:--namespace {
  border: 1px solid #666;
  border-radius: 3px;
}

components/my-tabs.css

:--namespace {
  display: flex;
}

.tab {
  border: 1px solid #666;
  border-bottom: none;
  border-top-radius: 3px;
  margin: 0 5px;
}

main.css

@import 'components/my-button.css';
@import 'components/my-tabs.css';

body {
  margin: 0;
  color: #333;
}

build.js

const fs = require('fs')
const path = require('path')
const postcss = require('postcss')
const postcssImport = require('postcss-import')
const postcssSelectorNamespace = require('postcss-selector-namespace')

let css = fs.readFileSync('main.css', 'utf8')

function getComponentName(filename) {
  if (/components\//.test(filename)) {
    return path.basename(filename).replace(/\.css$/, '')
  }

  return null
}

postcss()
  .use(postcssImport({
    transform(css, filename, options) {
      let componentName = getComponentName(filename)

      if (!componentName) {
        return css
      }

      return postcss()
        .use(postcssSelectorNamespace({ namespace: '.' + componentName }))
        .process(css)
        .then(result => result.css)
    }
  }))
  .process(css, { from: 'main.css' })
  .then(result => {
    console.log(result.css)
  })

node build.js outputs:

.my-button {
  border: 1px solid #666;
  border-radius: 3px;
}
.my-tabs {
  display: flex;
}
.my-tabs .tab {
  border: 1px solid #666;
  border-bottom: none;
  border-top-radius: 3px;
  margin: 0 5px;
}
body {
  margin: 0;
  color: #333;
}

Options

namespace

(default: '.self')

The selector to prepend to each selector.

selfSelector

(default: :--namespace)

The selector to use to apply rules directly to your namespace selector.

ignoreRoot

(default: true)

Selector won't be namespaced if they start with the :root pseudo-class.

rootSelector

(default: :root)

If prefixed with this selector, selectors won't be namespaced.

dropRoot

(default: true)

If true, the rootSelector will be stripped from the output.