Pseudo-classes
A pseudo-class picks an element not by tag or class but by its state or position: "the element under the cursor", "the first in a list", "a checked checkbox". It is written after a selector with a colon and no space. Such styles change by themselves, without a single line of JavaScript.
Hover and press
:hover applies while the mouse cursor is over the element, and :active applies at the moment of pressing:
<style>
.button {
padding: 8px 16px;
border: 0;
border-radius: 8px;
background: rebeccapurple;
color: white;
}
.button:hover { background: indigo; }
.button:active { background: black; }
</style>
<button class="button">Hover and press</button>Phones have no cursor, so an important action must not be hidden behind :hover alone.
Focus
The element in focus is the one the user is working with right now: the field with a blinking cursor, or a button reached with the Tab key. :focus applies on any focus, while :focus-visible applies only when focus should be shown, for example when using the keyboard:
<style>
input:focus { outline: 3px solid gold; }
button:focus-visible { outline: 3px solid deeppink; }
</style>
<input placeholder="Click here">
<button>Press Tab to reach me</button>Links
Links have their own states: :link is not yet visited, :visited has been opened. They are written in the order link, visited, hover, active — otherwise a lower rule overrides an upper one:
<style>
a:link { color: royalblue; }
a:visited { color: purple; }
a:hover { color: crimson; }
</style>
<p><a href="#nowhere">Hover over the link</a></p>Position among siblings
Structural pseudo-classes pick an element by its place among its siblings — elements with the same parent:
<style>
li:first-child { font-weight: 700; }
li:last-child { color: crimson; }
li:nth-child(even) { background: whitesmoke; }
</style>
<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
<li>Fourth</li>
</ul>| Pseudo-class | What it picks |
|---|---|
:first-child | the first element |
:last-child | the last element |
:nth-child(3) | the third element |
:nth-child(odd) | odd ones: 1, 3, 5… |
:nth-child(even) | even ones: 2, 4, 6… |
:nth-child(3n) | every third one |
Exclusion
:not() picks every element except those named in the parentheses. It is handy for adding a line between items but not after the last one:
<style>
li:not(:last-child) { border-bottom: 1px solid silver; }
</style>
<ul>
<li>Apples</li>
<li>Pears</li>
<li>Plums</li>
</ul>Form field states
:checked picks a checked checkbox or radio button, and :disabled picks a disabled field:
<style>
input:checked + label { color: seagreen; font-weight: 700; }
input:disabled { background: gainsboro; }
</style>
<input type="checkbox" id="news" checked> <label for="news">Subscribe to news</label>
<p><input disabled placeholder="Unavailable"></p>The + sign picks the element that comes right after the previous one: here, the label after the checkbox. Uncheck the box and the label becomes regular.
Common mistake
:first-child checks whether the element is first among all its siblings, not among elements of its tag. If a heading comes before a paragraph, p:first-child does not match. For that there is :first-of-type — the first of its tag:
<style>
p:first-child { color: crimson; }
p:first-of-type { font-weight: 700; }
</style>
<div>
<h3>A heading</h3>
<p>Not red: h3 comes first. But bold — the first paragraph.</p>
<p>The second paragraph.</p>
</div>