Course outline · 0% complete

0/30 lessons0%

Course overview →

States: hover, focus, and pseudo-classes

lesson 4-4 · ~9 min · 13/30

Styling states, not just elements

Every polished interface responds before any JavaScript runs. Buttons highlight under the cursor, links change once visited, and inputs glow when selected. Controls that give no feedback leave users unable to tell what is clickable, so browsers track each element's current state and CSS can select on it.

A pseudo-class is a selector suffix written with a colon, matching an element only while it is in a given state.

a:hover { color: darkgoldenrod; }     /* cursor is over it */
button:active { background: gray; }   /* being pressed right now */
input:focus { border-color: black; }  /* selected for typing */
a:visited { color: purple; }          /* already followed */
Pseudo-classMatches while
:hovera pointer is over the element
:activethe element is being pressed
:focusthe element is selected for input
:visitedthe link has been followed

In the lesson 4-3 specificity math, a:hover scores one type plus one class-level part, giving (0,1,1), because pseudo-classes count in the classes bucket.

Focus is not optional

:hover is cosmetic and :focus is accessibility. Keyboard users move through a page with the Tab key, and the focus outline, the ring the browser draws around the focused control, is the only way they can see where they are. Removing it makes the page unusable without a mouse.

The rule working teams follow is never to write outline: none unless an equally visible focus style replaces it in the same rule:

button:focus {
  outline: 2px solid #d4af37;  /* replaced, not removed */
  outline-offset: 2px;
}

Two structural pseudo-classes round out the daily set, selecting by position rather than state:

li:first-child { font-weight: bold; }    /* first li in its parent */
tr:nth-child(even) { background: #eee; } /* zebra-striped table rows */
Pseudo-classSelects by
:hover, :focus, :activestate
:first-child, :nth-child()position

The zebra-striping example is the most common real use of :nth-child, and it also shows why the positional group is worth knowing. It removes the need to tag alternating rows with classes in the HTML.

The state a Tab key creates

A user tabbing through a form lands on inputs matched by :focus.

:focus matches the element currently selected for input, whether it arrived there by click or by Tab. :hover requires a pointer over the element, which a keyboard user never produces.

Input method:hover matches:focus matches
mouse pointeryesonly after a click
Tab keynoyes

That asymmetry is why a form styled with :hover alone is invisible to navigate by keyboard. Both states are usually worth styling, and only one of them is required.

A button with real interaction states

A base style plus two state rules, one cosmetic and one required.

CSS

.cta {
  background: #d4af37;
  color: black;
  border: none;
  padding: 10px 24px;
  border-radius: 20px;
  font-size: 1rem;
}

.cta:hover {
  background: black;
  color: #d4af37;
}

.cta:focus {
  outline: 3px solid black;
  outline-offset: 2px;
}
RuleApplies whenPurpose
.ctaalwaysthe base look
.cta:hoverpointer over itfeedback for mouse users
.cta:focusfocusedfeedback for keyboard users

Pseudo-classes attach to the selector with a colon and no space. outline and outline-offset are two separate declarations, with the offset pushing the ring away from the edge so it stays visible against the button's own background.

Keeping a visible focus style is an accessibility requirement rather than a decoration, since without it keyboard users cannot tell where they are.

The pointer-only state

The pseudo-class is :hover.

It matches while a pointer is over the element and stops matching the moment the pointer leaves. It is the state a mouse creates and a keyboard cannot.

StateCreated by
:hovera pointer
:focusa click or the Tab key
:activea press in progress

Because it has no keyboard equivalent, :focus styles must exist alongside it. Touch devices complicate this further, since a tap can leave an element in a hover state until something else is tapped.