๐ฆ Stylelint Plugin Defensive CSS
A Stylelint plugin to enforce defensive CSS best practices.
Getting Started
Before getting started with the plugin, you must first have Stylelint version 14.0.0 or greater installed
To get started using the plugin, it must first be installed.
npm i stylelint-plugin-defensive-css --save-dev
yarn add stylelint-plugin-defensive-css --dev
With the plugin installed, the individual rule(s) can be added to the project's Stylelint configuration.
Rules / Options
The plugin provides multiple rules that can be toggled on and off as needed.
- Accidental Hover
- Background-Repeat
- Custom Property Fallbacks
- Flex Wrapping
- Scroll Chaining
- Vendor Prefix Grouping
Accidental Hover
We use hover effects to provide an indication to the user that an element is clickable or active. That is fine for devices that have a mouse or a trackpad. However, for mobile browsing hover effects can get confusing.
Enable this rule in order to prevent unintentional hover effects on mobile devices.
{
"rules": {
"plugin/use-defensive-css": [true, { "accidental-hover": true }]
}
}
โ Passing Examples
@media (hover: hover) {
.btn:hover {
color: black;
}
}
/* Will traverse nested media queries */
@media (hover: hover) {
@media (min-width: 1px) {
.btn:hover {
color: black;
}
}
}
/* Will traverse nested media queries */
@media (min-width: 1px) {
@media (hover: hover) {
@media (min-width: 100px) {
.btn:hover {
color: black;
}
}
}
}
โ Failing Examples
.fail-btn:hover {
color: black;
}
@media (min-width: 1px) {
.fail-btn:hover {
color: black;
}
}
Background Repeat
Oftentimes, when using a large image as a background, we tend to forget to account for the case when the design is viewed on a large screen. That background will repeat by default.
Enable this rule in order to prevent unintentional repeating background.
{
"rules": {
"plugin/use-defensive-css": [true, { "background-repeat": true }]
}
}
โ Passing Examples
div {
background: url('some-image.jpg') repeat black top center;
}
div {
background: url('some-image.jpg') black top center;
background-repeat: no-repeat;
}
โ Failing Examples
div {
background: url('some-image.jpg') black top center;
}
div {
background-image: url('some-image.jpg');
}
Custom Property Fallbacks
CSS variables are gaining more and more usage in web design. There is a method that we can apply to use them in a way that doesnโt break the experience, in case the CSS variable value was empty for some reason.
Enable this rule in order to require fallbacks values for custom properties.
{
"rules": {
"plugin/use-defensive-css": [true, { "custom-property-fallbacks": true }]
}
}
โ Passing Examples
div {
color: var(--color-primary, #000);
}
โ Failing Examples
div {
color: var(--color-primary);
}
Option | Description |
---|---|
ignore | Pass an array of regular expressions and/or strings to ignore linting specific custom properties. |
{
"rules": {
"plugin/use-defensive-css": [
true,
{ "custom-property-fallbacks": [true, { "ignore": [/hel-/, "theme-"] }] }
]
}
}
The ignore
array can support regular expressions and strings. If a string is
provided, it will be translated into a RegExp like new RegExp(string)
before
testing the custom property name.
โ Passing Examples
div {
/* properties with theme- are ignored */
color: var(--theme-color-primary);
/* properties with hel- are ignored */
padding: var(--hel-spacing-200);
}
Flex Wrapping
CSS flexbox is one of the most useful CSS layout features nowadays. Itโs
tempting to add display: flex
to a wrapper and have the child items ordered
next to each other. The thing is when there is not enough space, those child
items wonโt wrap into a new line by default. We need to either change that
behavior with flex-wrap: wrap
or explicitly define nowrap
on the container.
Enable this rule in order to require all flex rows to have a flex-wrap value.
{
"rules": {
"plugin/use-defensive-css": [true, { "flex-wrapping": true }]
}
}
โ Passing Examples
div {
display: flex;
flex-wrap: wrap;
}
div {
display: flex;
flex-wrap: nowrap;
}
div {
display: flex;
flex-direction: row-reverse;
flex-wrap: wrap-reverse;
}
div {
display: flex;
flex-flow: row wrap;
}
div {
display: flex;
flex-flow: row-reverse nowrap;
}
โ Failing Examples
div {
display: flex;
}
div {
display: flex;
flex-direction: row;
}
div {
display: flex;
flex-flow: row;
}
Scroll Chaining
Have you ever opened a modal and started scrolling, and then when you reach the end and keep scrolling, the content underneath the modal (the body element) will scroll? This is called scroll chaining.
Enable this rule in order to require all scrollable overflow properties to have an overscroll-behavior value.
{
"rules": {
"plugin/use-defensive-css": [true, { "scroll-chaining": true }]
}
}
โ Passing Examples
div {
overflow-x: auto;
overscroll-behavior-x: contain;
}
div {
overflow: hidden scroll;
overscroll-behavior: contain;
}
div {
overflow: hidden; /* No overscroll-behavior is needed in the case of hidden */
}
div {
overflow-block: auto;
overscroll-behavior: none;
}
โ Failing Examples
div {
overflow-x: auto;
}
div {
overflow: hidden scroll;
}
div {
overflow-block: auto;
}
Vendor Prefix Grouping
It's not recommended to group selectors that are meant to work with different browsers. For example, styling an input's placeholder needs multiple selectors per the browser. If we group the selectors, the entire rule will be invalid, according to w3c.
Enable this rule in order to require all vendor-prefixed selectors to be split into their own rules.
{
"rules": {
"plugin/use-defensive-css": [true, { "vendor-prefix-grouping": true }]
}
}
โ Passing Examples
input::-webkit-input-placeholder {
color: #222;
}
input::-moz-placeholder {
color: #222;
}
โ Failing Examples
input::-webkit-input-placeholder,
input::-moz-placeholder {
color: #222;
}