postcss-media-hover-any-hover
v0.4.0
Published
PostCSS plugin wraps selectors with :hover with an @media (any-hover: hover) block
Downloads
2,654
Maintainers
Readme
postcss-media-hover-any-hover
postcss-media-hover-any-hover is a PostCSS plugin that automatically wraps CSS :hover selectors with @media (any-hover: hover) blocks.
This prevents unintended hover effects (the so-called "sticky hover" problem) on touch and hybrid devices, ensuring that hover styles are only applied on devices where a pointing device (like a mouse) is available.
- Desktop (with mouse): Hover effects are enabled
- Smartphones/Tablets: Hover effects are disabled
- Hybrid devices (e.g., Surface): Hover effects are enabled only when a mouse is connected
By using this plugin, you can achieve optimal hover behavior for each device type without sacrificing CSS maintainability.
Install
npm i -D postcss-media-hover-any-hoverPostCSS Config
postcss.config.js:
module.exports = {
plugins: [require('postcss-media-hover-any-hover')()],
};JavaScript API
const fs = require('fs');
const postcss = require('postcss');
const postcssMediaHoverAnyHover = require('postcss-media-hover-any-hover');
const css = fs.readFileSync('input.css', 'utf8');
const output = postcss().use(postcssMediaHoverAnyHover()).process(css).css;Usage
This plugin automatically wraps rules containing :hover selectors with @media (any-hover: hover).
This ensures hover effects only apply on devices that support hover functionality.
Basic Example
/* Input */
a {
&:hover {
text-decoration: underline;
}
}
/* Output */
a {
@media (any-hover: hover) {
&:hover {
text-decoration: underline;
}
}
}Multiple Selectors Example
/* Input */
a:hover,
button:hover {
color: red;
}
/* Output */
@media (any-hover: hover) {
a:hover,
button:hover {
color: red;
}
}Mixed Selectors Example
/* Input */
a {
color: blue;
&:hover {
color: red;
}
}
/* Output */
a {
color: blue;
@media (any-hover: hover) {
&:hover {
color: red;
}
}
}Nested Selectors Example
/* Input */
.container {
.button:hover {
color: blue;
}
}
/* Output */
.container {
@media (any-hover: hover) {
.button:hover {
color: blue;
}
}
}Combining with Existing Media Queries
/* Input */
@media (min-width: 768px) {
a:hover {
color: purple;
}
}
/* Output */
@media (min-width: 768px) {
@media (any-hover: hover) {
a:hover {
color: purple;
}
}
}Plugin Options
This plugin accepts the following options:
postcssMediaHoverAnyHover({
// 'any-hover' or 'hover', default: 'any-hover'
mediaFeature: 'any-hover',
// Whether to transform :hover within existing media queries, default: false
transformNestedMedia: false,
// Exclude specific selector patterns, default: []
excludeSelectors: ['.no-transform:hover'],
});mediaFeature Option
Choose between 'any-hover' (default) and 'hover':
'any-hover': Uses@media (any-hover: hover)which checks if any input device supports hover'hover': Uses@media (hover: hover)which only checks if the primary input device supports hover
// Using hover instead of any-hover
postcss().use(postcssMediaHoverAnyHover({ mediaFeature: 'hover' }));transformNestedMedia Option
Controls whether to transform :hover selectors that are already inside a media query:
// Transform :hover even when already inside media queries
postcss().use(postcssMediaHoverAnyHover({ transformNestedMedia: true }));excludeSelectors Option
Specify selectors that should not be transformed:
// Exclude specific selectors from transformation
postcss().use(
postcssMediaHoverAnyHover({
excludeSelectors: ['.no-transform:hover', '.special-button:hover'],
}),
);Performance
This plugin is optimized to work fast and efficiently:
- Early returns to avoid unnecessary processing
- Optimized regular expression patterns
- Data structures for fast lookups
- Memory usage optimization
The plugin performs well even with large CSS files. To run performance tests:
npm run perfPerformance Test Results Example
# Small CSS (145 characters)
Average execution time: 0.15ms/iteration
# Medium CSS (about 20k characters)
Average execution time: 1.81ms/iteration
# Large CSS (about 170k characters)
Average execution time: 19.99ms/iteration