Style hover, focus, and active states differently

4 months ago from , Owner

I’ve been styling :hover, :focus, and :active states the same way for years. I can’t remember when I started styling this way. Here’s the code I always use:

// Not the best approach. I'll explain why in this article .selector { &:hover, &:focus, &:active { // Styles here } }

As I paid more attention to keyboard accessibility (and therefore paying more attention to focus), I began to think we should not style hover, focus, and active states the same way.

Hover, focus, and active states should be styled different.

There’s a simple reason: They’re different states!

Today, I want to show you a magical way to style all three states effortlessly.

Let’s start with :hover.

Styling hover states

:hover triggers when a user brings their mouse over an element.

Hover states are usually represented by a change in background-color (and/or color). The difference in states doesn’t have to be obvious because users already know they hovered on something.

button { background-color: #dedede; } button:hover { background-color: #aaa; }

On hover, button darkens slightly.

Styling focus states

:focus activates when an element receives focus. Elements can receive focus in two ways:

  1. When users tab into a focusable element
  2. When users click on a focusable element

Focusable elements are:

  1. Links (<a>)
  2. Buttons (<button>)
  3. Form elements (input, textarea, etc.)
  4. Elements with tabindex

Here are a few important points to note:

  1. Users cannot tab into an element with tabindex="-1", but they can click on it. The click triggers focus.
  2. On Safari and Firefox (Mac), clicks do not focus the <button> element. More info here.
  3. When you click on a link (<a>), focus remains on the link until you lift your finger from your mouse. When you lift your finger, the focus gets redirected elsewhere if the href points to a valid id on the same page.

For focus, we’re more concerned about users tabbing into elements than clicking on elements.

When a user hits tab, they don’t know where the focus will go to. They can only guess. This is why we need a prominent change a user’s attention attention to the focused element.

The default focus style is okay most of the time. If you want to design your own focus, think about these four things:

  1. Adding an outline
  2. Creating animations with movement
  3. Changing background-color
  4. Changing color

Since background-color and color changes often accompany :hover, it makes sense that outlines or animations should accompany :focus.

You can use a combination of outline, border, and box-shadow properties to create nice focus styles. I share how to do this in “Creating a custom focus style”.

button { background-color: #dedede; } button:hover { background-color: #aaa; } button:focus { outline: none; box-shadow: 0 0 0 3px lightskyblue; }

Focus a button with Tab. When focused, shows an outline with box-shadow.

Styling active states

When you interact with things in real life, you expect some sort of feedback. For example, if you push a button, you expect the button to get pressed.

This feedback is useful on websites too. You can style the “push button” moment with :active. :active triggers when you interact with an element. Interacting here means:

  1. Holding down your left mouse button on an element (even non-focusable ones)
  2. Holding down the Space key (on buttons)
button:active { background-color: #333; border-color: #333; color: #eee; }

Changes background-color and color when user holds their left mouse button down on the button.

Two weird things to take note of:

  1. Holding down Space triggers :active on buttons, but holding down Enter doesn’t.
  2. Enter triggers links but it doesn’t create create an active state. Space doesn’t trigger links at all.

Links have a default active style. They turn red when they get clicked.

By default, links turn red when they get clicked.

The relationship between active and focus

When you hold down the left mouse button on a focusable element, you trigger the active state. You also trigger the focus state at the same time.

When you release the left mouse button, focus remains on the element