Detailed explanation
The :hover, :focus, and :active pseudo-classes select elements based on their current interaction state rather than a class added to the HTML. :hover means a pointing device is over an element; :focus means the element currently receives focused input; and :active means the element is being activated, typically while the primary mouse button is held down on it. These states provide different kinds of feedback: availability, input location, and a pressed response. They are not mutually exclusive, so a button can match all three at once.
The browser tracks these states and updates which CSS selectors match as the user interacts. For example, button:hover { background: #ddd; } changes the background while the pointer is over the button. Hover does not require a click and does not necessarily move focus. It is also unreliable as the only interaction signal on touchscreens, where hover may be unavailable or behave differently. Essential instructions, controls, and content should therefore not depend exclusively on hover.
Focus usually moves through interactive elements with Tab or Shift+Tab, but it can also be set by clicking or calling an element’s focus() method. Clicking behaviour varies by browser and platform. Native buttons, links with href, and form controls are generally focusable; an ordinary div is not normally keyboard-focusable. Focus remains until it moves elsewhere or is removed. A visible indicator helps keyboard users identify their current location. The related :focus-visible pseudo-class matches when the browser determines a focus indicator should be shown, commonly during keyboard navigation. For example, button:focus-visible { outline: 2px solid blue; } provides that indicator without treating every pointer interaction identically.
The :active state is temporary, not a permanent “selected” state. For example, button:active { transform: scale(0.98); } gives a brief pressed effect. Keyboard activation can also trigger :active, depending on the element, key, and browser. If multiple states set the same property, normal cascade rules resolve the conflict; these pseudo-classes have equal specificity. Interviewers look for correct timing, awareness of overlapping states, and accessible styling rather than merely three definitions.
Key points to remember
- :hover matches pointer-over interaction; it does not require clicking.
- :focus identifies the element with focus, whether reached by keyboard, pointer, or script.
- :active represents a temporary activation state, typically while the primary mouse button is held down.
- :focus-visible uses browser heuristics to show focus indicators when appropriate.
- States can overlap; competing declarations are resolved through the CSS cascade.
Common mistakes candidates make
- Describing :active as the current page, a selected tab, or a state that persists after clicking.
- Assuming :focus happens only through keyboard navigation or is equivalent to hover.
- Removing focus outlines without an accessible replacement, or making essential functionality hover-only.
Likely follow-up questions
What is the difference between :focus and :focus-visible?
:focus matches whenever an element has focus. :focus-visible additionally depends on the browser’s judgement that a visible indicator is appropriate, commonly for keyboard navigation and text-input interactions.
What happens if :hover and :active set different background colours?
Both selectors can match simultaneously, so the normal cascade decides which declaration wins. For equally specific rules in the same cascade context, the later declaration wins; placing the active rule after the hover rule can make the pressed colour appear.
How do you style a tab that stays selected after clicking?
Use persistent application state, such as an appropriate aria-selected attribute on a tab, and style it with an attribute selector. :active only describes temporary activation and does not remember which tab was selected.
Interview tip: Explain the three states as pointer-over, focused input, and pressed, then demonstrate their timing on a native button.
