Skip to content
Laviyx Learn

Try

CSS course menu

CSS Pseudo-classes: :hover, :focus, :nth-child, :not and More

Learn CSS pseudo-classes with live examples: user action states (:hover, :focus, :focus-visible, :active), link states, form states (:checked, :disabled, :invalid, :user-invalid), structural pseudo-classes (:first-child, :nth-child, :nth-of-type) and :not().

Beginner6 min readUpdated
On this page

A pseudo-class selects elements in a particular state or position: a link being hovered, a field with an invalid value, the first item in a list. Pseudo-classes start with a single colon.

User action states#

CSS
.btn {
  padding: 8px 16px;
  background: #2f5fe0;
  color: white;
  border: none;
  border-radius: 6px;
}
.btn:hover { background: #1f45b0; }
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 3px solid orange; outline-offset: 2px; }
HTML
<button class="btn">Hover, click, or Tab to me</button>
Result
Pseudo-classWhen it matches
:hoverThe pointer is over the element
:activeThe element is being pressed
:focusThe element has keyboard focus (or was clicked, for many elements)
:focus-visibleThe element has focus and the browser decides a focus ring should be shown, typically for keyboard users
:focus-withinThe element or anything inside it has focus
CSS
a:link { color: #2f5fe0; }
a:visited { color: #7a3fb8; }
a:hover { text-decoration: none; }
HTML
<p><a href="https://example.com/never-visited-page-123">A link</a></p>
Result

If you style several link states, keep the order :link, :visited, :hover, :active (some people remember it as "LoVe HAte"), because later rules win when specificity is equal. For privacy, browsers only let :visited change colours.

Form states#

CSS
input:disabled { background: #eee; }
input:checked + label { font-weight: bold; color: green; }
input:required { border-left: 4px solid #2f5fe0; }
input:user-invalid { border-color: red; outline: 1px solid red; }
HTML
<p><input type="checkbox" id="t1"> <label for="t1">Tick me</label></p>
<p><input type="text" placeholder="Disabled" disabled></p>
<p><input type="email" placeholder="Type an invalid email, then click away" required size="35"></p>
Result
  • :checked matches ticked checkboxes, selected radio buttons and selected options.
  • :disabled / :enabled, :required / :optional, :read-only.
  • :valid / :invalid match by validation state immediately, even before the user types.
  • :user-invalid only matches after the user has interacted with the field, which is friendlier. It is supported in current versions of all major browsers.
  • :placeholder-shown matches while the placeholder is visible.

Structural pseudo-classes#

These select elements by their position among siblings:

CSS
li:first-child { color: green; }
li:last-child { color: crimson; }
li:nth-child(even) { background: #f0f0f0; }
HTML
<ul>
  <li>First</li>
  <li>Second</li>
  <li>Third</li>
  <li>Fourth</li>
  <li>Fifth (last)</li>
</ul>
Result
Pseudo-classMatches
:first-child, :last-childFirst or last child of its parent
:only-childThe only child
:nth-child(3)The third child
:nth-child(odd), :nth-child(even)Alternating children
:nth-child(3n)Every third child (3, 6, 9...)
:nth-child(n+4)The fourth child onwards
:nth-last-child(2)Second from the end
:first-of-type, :nth-of-type(2)Same idea, counting only elements of the same type
:emptyElements with no children or text

nth-child versus nth-of-type#

p:nth-child(2) means "the second child, if it is a p". p:nth-of-type(2) means "the second p, ignoring other element types":

CSS
p:nth-child(2) { background: #fde2e2; }
p:nth-of-type(2) { outline: 2px solid #2f5fe0; }
HTML
<div>
  <h3>Title</h3>
  <p>First paragraph (second child: pink)</p>
  <p>Second paragraph (second p: outlined)</p>
</div>
Result

:not()#

:not() matches elements that do not match the selector inside:

CSS
.tags span:not(:last-child)::after { content: ", "; }
HTML
<p class="tags"><span>html</span><span>css</span><span>javascript</span></p>
Result

Commas appear after every tag except the last. :not() accepts a list of selectors, for example :not(.active, .disabled).

The newer :is(), :where() and :has() pseudo-classes are covered in Modern Selectors.

Practice#

Practice exercise

Style a table so its body rows are zebra-striped (every even row light grey), a row turns light yellow when hovered, and the first column is bold.

Show solution
CSS
table { border-collapse: collapse; }
td { padding: 6px 12px; }
tbody tr:nth-child(even) { background: #f2f2f2; }
tbody tr:hover { background: #fff8c4; }
td:first-child { font-weight: bold; }
HTML
<table>
  <tbody>
    <tr><td>Asha</td><td>92</td></tr>
    <tr><td>Ravi</td><td>87</td></tr>
    <tr><td>Meera</td><td>95</td></tr>
    <tr><td>Kiran</td><td>78</td></tr>
  </tbody>
</table>
Result

Key takeaway#

Finished this lesson?