Security News
ESLint is Now Language-Agnostic: Linting JSON, Markdown, and Beyond
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
postcss-bem-linter
Advanced tools
A PostCSS plugin to lint BEM-style CSS.
BEM-style describes CSS that follows a more-or-less strict set of conventions determining what selectors can be used. Typically, these conventions require that classes begin with the name of the component (or "block") that contains them, and that all characters after the component name follow a specified pattern. Original BEM methodology refers to "blocks", "elements", and "modifiers"; SUIT refers to "components", "descendants", and "modifiers". You might have your own terms for similar concepts.
With this plugin, you can check the validity of selectors against a set of BEM-style conventions. You can use preset patterns (SUIT and BEM, currently) or insert your own. The plugin will register warnings if it finds CSS that does not follow the specified conventions.
npm install postcss-bem-linter
Version 1.0.0+ is compatible with PostCSS 5+. (Earlier versions are compatible with PostCSS 4.)
This plugin registers warnings via PostCSS. Therefore, you'll want to use it with a PostCSS runner that prints warnings (e.g. gulp-postcss
) or another PostCSS plugin that prints warnings (e.g. postcss-reporter
).
Throughout this document, terms like "selector", "selector sequence", and "simple selector" are used according to the definitions in the Selectors Level 3 spec.
Default mode:
ComponentName
.Weak mode:
Prior to 0.5.0, this plugin checked two other details: that :root
rules only contain custom-properties; and that the :root
selector is not grouped or combined with other selectors. These checks can now be performed by stylelint. So from 0.5.0 onwards, this plugin leaves that business to stylelint to focus on its more unique task.
bemLinter([pattern[, options]])
Patterns consist of regular expressions, and functions that return regular expressions, which describe valid selector sequences.
Keep in mind:
.Component.is-open
, your regular expression needs to allow for this chaining..Component:first-child.is-open
, you should use .Component.is-open:first-child
.
The former will trigger a warning unless you've written a silly complicated regular expression.The following preset patterns are available:
'suit'
(default), as defined here.
Options:
namespace
: a namespace to prefix valid classes, as described
in the SUIT docs'bem'
, as defined here.You can use a preset pattern and its options in two ways:
options
object as the second,
e.g. bemLinter('suit', { namespace: 'twt' })
.preset
property and,
if needed, presetOptions
, e.g. bemLinter({ preset: 'suit', presetOptions: { namespace: 'twt' })
.'suit'
is the default pattern; so if you do not pass any pattern
argument,
SUIT conventions will be enforced.
You can define a custom pattern by passing as your first and only argument an object with the following properties:
componentName
(optional): A regular expression describing valid component names.
Default is /[-_a-zA-Z0-9]+/
.componentSelectors
: Either of the following:
initial
and combined
. Both methods accept a
component name and return a regular expression. initial
returns a description of valid
initial selector sequences — those occurring at the beginning of a selector, before any
combinators. combined
returns a description of valid selector sequences allowed after combinators.
utilitySelectors
: A regular expression describing valid utility selectors. This will be use
if the stylesheet defines a group of utilities, as explained below.So you might call the plugin in any of the following ways:
// use 'suit' conventions
bemLinter();
bemLinter('suit');
bemLinter('suit', { namespace: 'twt' });
bemLinter({ preset: 'suit', presetOptions: { namespace: 'twt' }});
// use 'bem' conventions
bemLinter('bem');
// define a RegExp for component names
bemLinter({
componentName: /[A-Z]+/
});
// define a single RegExp for all selector sequences, initial or combined
bemLinter({
componentSelectors: function(componentName) {
return new RegExp('^\\.' + componentName + '(?:-[a-z]+)?$');
}
});
// define separate `componentName`, `initial`, `combined`, and `utilities` RegExps
bemLinter({
componentName: /[A-Z]+/,
componentSelectors: {
initial: function(componentName) {
return new RegExp('^\\.' + componentName + '(?:-[a-z]+)?$');
},
combined: function(componentName) {
return new RegExp('^\\.combined-' + componentName + '-[a-z]+$');
}
},
utilitySelectors: /^\.util-[a-z]+$/
});
The plugin will only run if it finds special comments that define a named component or a group of utilities.
These definitions can be provided in two syntaxes: concise and verbose.
/** @define ComponentName */
or /** @define utilities */
/* postcss-bem-linter: define ComponentName */
or /* postcss-bem-linter: define utilities */
.Weak mode is turned on by adding ; weak
to a definition,
e.g. /** @define ComponentName; weak */
or /* postcss-bem-linter: define ComponentName; weak */
.
Concise syntax:
/** @define MyComponent */
:root {
--MyComponent-property: value;
}
.MyComponent {}
.MyComponent-other {}
Verbose syntax:
/** postcss-bem-linter: define FancyComponent */
:root {
--FancyComponent-property: value;
}
.FancyComponent {}
.FancyComponent-other {}
Weak mode:
/** @define MyComponent; weak */
:root {
--MyComponent-property: value;
}
.MyComponent {}
.MyComponent .other {}
Utilities:
/** @define utilities */
.u-sizeFill {}
.u-sm-horse {}
If a component is defined and the component name does not match your componentName
pattern,
the plugin will throw an error.
It's recommended that you keep each defined group of rules in a distinct file, with the definition at the top of the file. If, however, you have a good reason for multiple definitions within a single file, you can do that.
Successive definitions override each other. So the following works:
/* @define Foo */
.Foo {}
/* @define Bar */
.Bar {}
/* @define utilities */
.u-something {}
You can also deliberately end the enforcement of a definition with the following special comments:
/* @end */
or /* postcss-bem-linter: end */
.
/* @define Foo */
.Foo {}
/* @end */
.something-something-something {}
One use-case for this functionality is when linting files after concatenation performed by a CSS processor like Less or Sass, whose syntax is not always compatible with PostCSS. See issue #57.
If you need to ignore a specific selector but do not want to ignore the entire stylesheet
or end the enforcement of a definition,
you can do so by preceding the selector with this comment: /* postcss-bem-linter: ignore */
.
/** @define MyComponent */
.MyComponent {
display: flex;
}
/* postcss-bem-linter: ignore */
.no-flexbox .Component {
display: block;
}
This will cause the linter to ignore only the very next selector.
Pass your individual CSS files through the plugin. It will register warnings for
conformance failures, which you can print to the console using
postcss-reporter
or relying
on a PostCSS runner (such as gulp-postcss
).
var postcss = require('postcss');
var bemLinter = require('postcss-bem-linter');
var reporter = require('postcss-reporter');
files.forEach(function(file) {
var css = fs.readFileSync(file, 'utf-8');
postcss()
.use(bemLinter())
.use(reporter())
.process(css)
.then(function(result) { .. });
});
Install the dependencies.
npm install
Run the tests.
npm test
Watch and automatically re-run the unit tests.
npm start
1.0.1 (August 30, 2015)
FAQs
A BEM linter for postcss
The npm package postcss-bem-linter receives a total of 102,702 weekly downloads. As such, postcss-bem-linter popularity was classified as popular.
We found that postcss-bem-linter demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
Security News
Members Hub is conducting large-scale campaigns to artificially boost Discord server metrics, undermining community trust and platform integrity.
Security News
NIST has failed to meet its self-imposed deadline of clearing the NVD's backlog by the end of the fiscal year. Meanwhile, CVE's awaiting analysis have increased by 33% since June.