A pseudo-class in CSS is a keyword that is added to a selector to apply styles to an element only when it is in a special state.
Thanks to them, we can provide visual feedback when the visitor interacts with the page, achieving a dynamic experience without having to program a single line of logic (only with CSS tools).
Let’s look at the fundamental pseudo-classes to bring the interface to life. 👇
The :hover state
The
It’s the most common tool for visually indicating that an element is interactive. The syntax is very simple: you just need to add the word :hover directly to the original selector, without any whitespace.
/* Normal state of the button */
.send-button {
background-color: blue;
color: white;
}
/* State when hovering the mouse over it */
.send-button:hover {
background-color: darkblue;
cursor: pointer;
}
Be careful with mobile devices. Since a touchscreen phone doesn’t have a constantly floating “pointer”, the :hover effect often behaves strangely, staying activated after the first tap.
The :focus state
The Tab key.
It’s used extensively in forms. It’s the way to tell the user: “You are typing in this box, not the one below.”
/* Normal state of the text input */
input {
border: 2px solid gray;
}
/* State when the user clicks to start typing */
input:focus {
border: 2px solid blue;
background-color: #f0f8ff;
outline: none;
}
If you decide to remove the outline that browsers put by default on focus (the classic outline: none), you must replace it with another clearly visible style.
If you don’t, you will destroy the accessibility of your website for people who navigate using the keyboard.
The :focus-visible state
The :focus. It activates when the browser considers it appropriate to show a visible focus indicator, especially when navigating with the keyboard.
This allows us to avoid those outlines that appear when clicking with the mouse, but maintain a clear signal for those navigating the page with Tab.
button:focus-visible,
a:focus-visible {
outline: 3px solid #facc15;
outline-offset: 3px;
}
For real interfaces, :focus-visible is usually a better option than :focus when you want to design keyboard focus. It doesn’t remove accessibility, it makes it less visually intrusive.
The :active state
The
It’s very useful for visually simulating the pressing of a physical switch. It doesn’t change the page logic, it only communicates that a click is happening.
/* Combining several states on the same button */
.send-button {
background-color: green;
padding: 10px 20px;
}
.send-button:hover {
background-color: darkgreen;
}
.send-button:active {
background-color: lightgreen;
transform: scale(0.95);
}
Link states
Links have some pseudo-classes of their own dating back to the early days of the web. They are still useful for differentiating between normal, visited, or clicked links.
a:link {
color: #2563eb;
}
a:visited {
color: #6d28d9;
}
a:hover {
text-decoration-thickness: 2px;
}
a:active {
color: #dc2626;
}
The recommended order to write them is :link, :visited, :hover, :active. If you change it carelessly, the cascade can cause some styles to override others.
Today many websites don’t visually differentiate visited links, but :visited can be useful in documentation, long lists, or search results.
Form states
Forms also have states we can leverage without JavaScript. Some of the most common are :checked, :disabled, :required, :valid, and :invalid.
input:required {
border-left: 4px solid #f59e0b;
}
input:disabled {
opacity: 0.6;
cursor: not-allowed;
}
input:checked + label {
font-weight: bold;
}
input:invalid {
border-color: #dc2626;
}
This allows differentiating required fields, selected options, disabled controls, or incorrect data. Used properly, it helps the user understand what is happening.
A bit of life, not a disco
Pseudo-classes are a great tool for providing visual feedback. A button that changes on hover, an input that highlights on focus, or a checkbox that marks its label all feel better than a completely flat page.
The key is to use them to help the user understand what is happening. If everything changes color, jumps, sinks, and shines every time the mouse passes near, the interface stops communicating and starts making noise.
