Hover-Capable Device Detection
Use the hover media feature to conditionally apply CSS hover effects. Prevents sticky hover states on touchscreens.
Detailed Explanation
Conditional Hover Effects with @media (hover: hover)
One of the most common CSS frustrations on mobile is the "sticky hover" problem -- hover styles that persist after a tap because touch devices emulate hover. The hover media feature solves this cleanly.
The Query
@media (hover: hover) {
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.link:hover {
color: var(--primary);
text-decoration: underline;
}
}
The Sticky Hover Problem
On touch devices, tapping an element triggers:
:hover(sticks until you tap elsewhere):focus:activeclickevent
This means hover styles remain visible after the tap, confusing users. Wrapping hover styles in @media (hover: hover) ensures they only apply on devices where hover is a natural interaction.
Best Practice Pattern
/* Base interactive state (all devices) */
.card {
transition: box-shadow 0.2s ease;
cursor: pointer;
}
/* Active state (works on all devices) */
.card:active {
transform: scale(0.98);
}
/* Hover state (mouse/trackpad only) */
@media (hover: hover) {
.card:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}
}
/* Focus state (keyboard navigation) */
.card:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
}
any-hover vs hover
hover: hover-- Checks the primary input deviceany-hover: hover-- True if any input device supports hover
A laptop with a touchscreen reports hover: hover (primary is trackpad) and any-hover: hover. A tablet with a Bluetooth mouse reports hover: none (primary is touch) but any-hover: hover.
Use Case
Use this query to prevent sticky hover states on touch devices and only show hover effects when the user genuinely has a hover-capable input device like a mouse or trackpad.