https://github.com/hiro0218/postcss-media-hover-any-hover
PostCSS plugin wraps selectors with :hover with at-media (hover: hover) block
https://github.com/hiro0218/postcss-media-hover-any-hover
postcss postcss-plugin
Last synced: 16 days ago
JSON representation
PostCSS plugin wraps selectors with :hover with at-media (hover: hover) block
- Host: GitHub
- URL: https://github.com/hiro0218/postcss-media-hover-any-hover
- Owner: hiro0218
- License: mit
- Created: 2024-05-01T15:29:41.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2024-12-09T13:51:19.000Z (10 months ago)
- Last Synced: 2025-02-01T03:03:57.991Z (9 months ago)
- Topics: postcss, postcss-plugin
- Language: JavaScript
- Homepage: https://www.npmjs.com/package/postcss-media-hover-any-hover
- Size: 61.5 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# postcss-media-hover-any-hover
[](https://www.npmjs.com/package/postcss-media-hover-any-hover)
[](https://www.npmjs.com/package/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 connectedBy using this plugin, you can achieve optimal hover behavior for each device type without sacrificing CSS maintainability.
## Install
```bash
npm i -D postcss-media-hover-any-hover
```### PostCSS Config
`postcss.config.js`:
```js
module.exports = {
plugins: [require('postcss-media-hover-any-hover')()],
};
```### JavaScript API
```js
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
```css
/* Input */
a {
&:hover {
text-decoration: underline;
}
}/* Output */
a {
@media (any-hover: hover) {
&:hover {
text-decoration: underline;
}
}
}
```### Multiple Selectors Example
```css
/* Input */
a:hover,
button:hover {
color: red;
}/* Output */
@media (any-hover: hover) {
a:hover,
button:hover {
color: red;
}
}
```### Mixed Selectors Example
```css
/* Input */
a {
color: blue;
&:hover {
color: red;
}
}/* Output */
a {
color: blue;
@media (any-hover: hover) {
&:hover {
color: red;
}
}
}
```### Nested Selectors Example
```css
/* Input */
.container {
.button:hover {
color: blue;
}
}/* Output */
.container {
@media (any-hover: hover) {
.button:hover {
color: blue;
}
}
}
```### Combining with Existing Media Queries
```css
/* 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:
```js
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```js
// 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:
```js
// Transform :hover even when already inside media queries
postcss().use(postcssMediaHoverAnyHover({ transformNestedMedia: true }));
```#### `excludeSelectors` Option
Specify selectors that should not be transformed:
```js
// 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 optimizationThe plugin performs well even with large CSS files. To run performance tests:
```bash
npm run perf
```### Performance 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
```