css-pseudo-clases-hover-focus-active

CSS Pseudo-classes for Hover Focus and Active States

  • 5 min

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 pseudo-class is activated at the exact moment the mouse pointer is positioned over the element, without needing to click.

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;
}
Copied!

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 state comes into action when an element receives the browser’s input focus, either by clicking on it or by navigating sequentially with the keyboard 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;
}
Copied!

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 pseudo-class is a more refined version of :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;
}
Copied!

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 pseudo-class represents that exact and fleeting instant when the user is pressing the main mouse button, just before releasing it.

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);
}
Copied!

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;
}
Copied!

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;
}
Copied!

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.