Understanding the :hover Pseudo‑Class in CSS

The :hover pseudo‑class is a widely used feature in CSS (and one which even most junior developers should be familiar with). It is a simple yet powerful tool for creating visual feedback, allowing us to apply styles to an element in response to user interaction (when they hover their mouse over it). However, despite being a fairly rudimentary part of CSS, there are still aspects of :hover that can trip up even more seasoned developers.
These challenges often include understanding how :hover interacts within specificity rules, handling hover states across different devices (like touchscreens), and its place in the LoVe HAte rule for link pseudo‑classes (which dictates the order of styling to ensure consistent behaviour).
What is a Pseudo‑Class?
Starting with the absolute basics... In CSS a pseudo‑class is a keyword that we can add to your selector that specifies a special state of that element. Pseudo‑classes allow us to style elements based on things like user interactions, element position, or structural state without needing to add additional classes or IDs in the HTML.
Pseudo‑classes help us create dynamic effects in CSS by recognising and styling states like hovered, focused, or visited.
The syntax for a pseudo‑class in CSS looks something like this:
selector:pseudo-class {
/* styles for the pseudo-class go here */
}Understanding the :hover Pseudo‑Class
The :hover pseudo‑class allows us to apply styles when the user hovers their cursor over the element we've targetted with our selector, for example, a button, link, or image. This allows us to provide visual feedback to the user, making the element more interactive and offering a little enhancement to the user experience.
Here's a basic example showing how we might use :hover on a button:
button {
background-color: #005f73;
color: white;
}
button:hover {
background-color: #276b2a;
}Here, hovering changes the button's background from blue (#005f73) to dark green (#276b2a), whilst its text stays white. Moving the cursor away restores the blue background. Both colour pairs keep the text readable against its background.
:hover within Context of Other Pseudo‑Classes
In front‑end development, we would commonly use :hover in combination with other interaction‑based pseudo‑classes, especially when styling links. When doing so, there are four key pseudo‑classes that often work alongside one another:
:linktargets unvisited links. This is generally the default state for a linkable element like an anchor (<a>).:visitedtargets links that have been visited.:activetargets an element ‑ usually a link ‑ when it is being activated or clicked upon.:hovertargets an element when the user hovers their cursor over it.
The LoVe HAte Ordering for Link Pseudo‑Classes
When several link‑state rules set the same property, their order can decide which declaration wins. In the example below, the selectors have equal specificity, so the later matching rule wins. This is the cascade at work; it is not inheritance between pseudo‑classes.
I've long lost the original article I read on the matter twenty years ago, but the crux is that it follows the ordering of the letters in LOVE HATE (this is how I first learned it myself), representing :link, :visited, :hover, and :active, in that order.
That ordering is a useful way to choose which colour takes priority when states overlap. Here's how it breaks down:
:link(L) matches an unvisited link. It does not match a link that is already visited.:visited(V) matches a visited link. The same link cannot match both:linkand:visitedat once, so their relative order does not resolve a conflict between those two states.:hover(H) can overlap either link state. Placing its rule later lets its colour win when the selectors have equal specificity.:active(A) can overlap:hoverduring activation. Placing it last gives its colour priority in this equal‑specificity example. When activation ends, the other matching rules determine the colour again.
Each of these pseudo‑classes contributes the same specificity. Clicking is not inherently more specific than hovering: we express the intended priority through the rule order. Here's the example:
a:link {
color: blue;
}
a:visited {
color: purple;
}
a:hover {
color: red;
}
a:active {
color: green;
}In this example:
- The link starts blue (
:link). - Once visited, it turns purple (
:visited). - When hovered, it turns red (
:hover), overriding both the:linkand:visitedstates. - Whilst being clicked, it becomes green (
:active), temporarily overriding all the other states, but particularly the:hoverstate, since it is likely that the user's cursor is still hovering over the element whilst clicking upon it.
The LoVe HAte sequence allows us to make sure that the styles we intend are applied in a way that reflects the user's current interaction priorities, maintaining usability and visual consistency.
A Practical Example of :hover with a Button
So, with the more nitty‑gritty technical stuff out of the way, let's look at a more practical example, maybe even some code you might use yourself. For this, buttons are a great example because adding hover effects to buttons can make them feel more responsive and offer a visual guide to the user as they interact with the page.
button {
background-color: #005f73;
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #003f4f;
}Here, the button changes from #005f73 to the darker #003f4f, with white text in both states. Both pairs exceed a text contrast ratio of 4.5:1. The transition property animates the background change. The recording below shows that transition with a lighter starting colour; use the darker values in the example for the text contrast:
The Importance of :hover and Accessibility
Whilst :hover can improve interactivity and visual feedback in our application, it's essential to remember accessibility. As you might appreciate, hover effects only work for users who are using a mouse or similar pointing device. Keyboard users, for example, won't see :hover styles at all.
With that in mind, it's generally good practice to combine :hover and :focus pseudo‑classes together so that both pointer and keyboard users receive our interactive visual cues.
For example:
button:hover,
button:focus {
background-color: #003f4f;
}The button now uses #003f4f for both :hover and :focus, with white text. Keep the browser's visible focus outline too, so keyboard users can tell where they are on the page.
Wrapping Up
Use :hover as an enhancement for pointing devices, not as the only way to reveal information or reach an action. Pair interactive hover styles with :focus and make the default state usable on touch screens. A hover effect should confirm an interaction, not create one that keyboard and touch users cannot discover.