@morev/bem/no-detached-entity-extensions
Requires extensions of a BEM entity selector to be declared within that entity.
An entity extension is selector material added to a complete BEM entity selector without introducing a relational target.
.block {
&__title {}
&__title:hover {}
&__title::placeholder {}
&__title[aria-current='true'] {}
&__title.is-active {}
&__title--active {}
&__title--theme--dark {}
}.block {
&__title {
&:hover {}
&::placeholder {}
&[aria-current='true'] {}
&.is-active {}
&--active {}
&--theme--dark {}
}
}Motivation
Finding a BEM entity should be enough to understand how it looks and behaves. When its extensions are declared separately, its styles are scattered across sibling declarations. Changing or removing the entity then requires searching for every selector that extends it.
Keeping extensions inside their entity gives its local styles one predictable location. You can read its states together, update them alongside the base styles, and remove them with the entity without leaving detached selectors behind.
SCSS modifiers
SCSS can concatenate a modifier suffix with &. The rule therefore requires modifiers to be nested within their base block or element:
.block {
// Block modifiers belong inside the block.
&--compact {}
&__button {
// Element modifiers belong inside the element.
&--active {}
// Modifier values can be declared directly inside the base entity.
&--theme--dark {}
// Nesting values inside the modifier is also allowed.
&--theme {
&--light {}
}
}
}Native CSS
Native CSS nesting cannot concatenate a modifier suffix with &. Standalone modifier selectors are therefore allowed:
.block__button--active {
&:hover {}
}
.block__button--theme--dark {}Extensions that native CSS can express with nesting still have to be nested:
.block__button--active:hover {} .block__button--active {
&:hover {}
}Rule options
All options are optional and come with recommended default values.
export default {
plugins: ['@morev/stylelint-plugin'],
rules: {
'@morev/bem/no-detached-entity-extensions': true,
},
};export default {
plugins: ['@morev/stylelint-plugin'],
rules: {
'@morev/bem/no-detached-entity-extensions': [true, {
separators: {
element: '__',
modifier: '--',
modifierValue: '--',
},
messages: {
detached: (extension, owner) =>
`Move ${extension} into ${owner}.`,
},
}],
},
};Show full type of the options
type PrimaryOption = true;
type SecondaryOption = {
/**
* Custom message functions for rule violations.
*/
messages?: {
/**
* Custom message for a BEM entity extension declared outside its required owner.
*
* @param extension Complete resolved extension selector.
* @param owner BEM entity that must own the extension.
*
* @returns The error message to report.
*/
detached?: (extension: string, owner: string) => string;
};
/**
* Object that defines BEM separators used to distinguish blocks, elements, modifiers, and modifier values.
*
* @default { element: '__', modifier: '--', modifierValue: '--' }
*/
separators?: Partial<Separators>;
};Show info about Stylelint-wide options
Every rule in this plugin also supports the standard Stylelint per-rule options (disableFix, severity, url, reportDisables, and message), even though they are not explicitly reflected in the type definitions to avoid unnecessary noise.
Note: the message option is technically available, but its use is discouraged: each rule already provides a typed messages object, <!-- eslint-disable-line -- Global ID --> which not only offers IDE autocompletion but also supports multiline strings and automatically handles indentation.
For more information, see the official Stylelint configuration docs.
separators
The rule supports different naming conventions for BEM entities by allowing you to configure the separators between block elements, modifiers, and modifier values.
This flexibility ensures compatibility with all popular BEM styles described in the official [BEM methodology naming convention][bem-guide] or even custom ones.
Available separators
| Option | Default | Description |
|---|---|---|
element | __ | Separator between block and element. |
modifier | -- | Separator between block/element and modifier name. |
modifierValue | -- | Separator between modifier name and modifier value. |
messages
The rule provides built-in error messages for all violations it detects.
You can customize them using the messages option. This can be useful to:
- Adjust the tone of voice to match your team's style;
- Translate messages into another language;
- Provide additional project-specific context or documentation links.
INFO
You don't need to override all message functions — or any of them at all.
Overrides the default detached(extension, owner) message.
extensionis the complete resolved selector compound;owneris the BEM entity within which that extension must be declared.
Example
export default {
plugins: ['@morev/stylelint-plugin'],
rules: {
'@morev/bem/no-detached-entity-extensions': [true, {
messages: {
detached: (extension, owner) =>
`⛔ Move "${extension}" into "${owner}".`,
},
}],
},
};