CSS Tutorial
CSS Pseudo-class
A colon state such as :hover, :focus, :first-child, or :nth-child, without extra markup.
A colon, not a class
A pseudo-class is a selector that matches a state or a position. You write one colon and a name: :hover, :focus, :first-child. The HTML does not gain an extra class. The browser already knows the pointer is over the link, the input has the caret, or the item is the first child of its parent.
Attach the pseudo-class to a normal selector. a:hover is “a link, while hovered.”li:first-child is “a list item that is the first child.” No space before the colon.
| Pseudo-class | Matches when |
|---|---|
:hover | The pointer is over the element |
:focus | The element is the keyboard or click target |
:first-child | The element is the first child of its parent |
:nth-child(n) | The element is the nth child, or a pattern such as odd |
:not(selector) | The element does not match that selector |
:hover
Hover is the lightest interaction cue. Raise contrast, change the background, or underline a link. Keep the change small enough that the control still looks like the same button.
Example
<style>
body {
font-family: Georgia, serif;
margin: 1.25rem;
background: #f0f9ff;
color: #0c4a6e;
}
a.book {
display: inline-block;
background: #0284c7;
color: #fff;
text-decoration: none;
padding: 0.55rem 1.1rem;
border-radius: 999px;
}
a.book:hover {
background: #0d9488;
}
</style>
<h1>Harbor studio</h1>
<p>Open 8–16.</p>
<p><a class="book" href="#">Book a desk</a></p>Click Try it in CSS under the example. That opens /css/try. Hover the button in the preview. Phones have no hover; pair this with a visible :focus style.
:focus
Focus is how keyboard users know where they are. Do not set outline: none unless you draw a replacement that is at least as clear. A sky ring around the field is enough.
Example
<style>
body {
font-family: Georgia, serif;
margin: 1.25rem;
background: #f0f9ff;
color: #0c4a6e;
}
label {
display: block;
margin-bottom: 0.35rem;
font-weight: 700;
}
input {
font: inherit;
padding: 0.45rem 0.65rem;
border: 1px solid #7dd3fc;
border-radius: 0.45rem;
width: 16rem;
}
input:focus {
outline: 3px solid #0d9488;
outline-offset: 2px;
border-color: #0d9488;
}
</style>
<h1>Harbor studio</h1>
<label for="guest">Name for the booking</label>
<input id="guest" type="text" value="Ada">Click the field or press Tab in the live preview. The teal outline is the focus style. Keep it. The outline chapter covers the same idea for every control, not only inputs.
:first-child and :nth-child
These look at position among siblings, not at a class you typed. :first-child is the first child of the parent. :nth-child(odd) and :nth-child(even) stripe a list or a table. :nth-child(3) is only the third child.
Example
<style>
body {
font-family: Georgia, serif;
margin: 1.25rem;
background: #f0f9ff;
color: #0c4a6e;
}
ul {
list-style: none;
padding: 0;
margin: 0;
border: 1px solid #7dd3fc;
border-radius: 0.75rem;
overflow: hidden;
}
li {
padding: 0.55rem 0.85rem;
background: #fff;
}
li:nth-child(even) {
background: #e0f2fe;
}
li:first-child {
font-weight: 700;
background: #0c4a6e;
color: #fff;
}
</style>
<h1>Harbor studio</h1>
<ul>
<li>Hours this week</li>
<li>Tuesday 8–16</li>
<li>Wednesday 8–16</li>
<li>Thursday 8–14</li>
<li>Friday 8–16</li>
</ul>:nth-child counts every sibling, including headings mixed into a list parent. If the first child is an h2, li:first-child matches nothing. li:first-of-typecounts only list items when you need that.
:not()
:not() subtracts a match. Style every card except the one with a class, or every link except the current page. Put a simple selector inside the parentheses while you learn: a class, a tag, or another pseudo-class.
Example
<style>
body {
font-family: Georgia, serif;
margin: 1.25rem;
background: #f0f9ff;
color: #0c4a6e;
}
.card {
padding: 0.75rem 0.9rem;
border-radius: 0.65rem;
margin-bottom: 0.5rem;
}
.card:not(.sold) {
background: #fff;
border: 1px solid #7dd3fc;
}
.card.sold {
background: #e2e8f0;
color: #64748b;
}
</style>
<h1>Harbor studio</h1>
<div class="card">Tide chart — in stock</div>
<div class="card sold">March print — sold out</div>
<div class="card">Harbor Lane map — in stock</div>The border and white fill apply to cards that are not .sold. The sold row uses its own rule. You did not have to invent a .available class for the other two.
What to do next
Pseudo-classes pick states that already exist. The next chapter, pseudo-elements, inserts extra boxes with ::before and ::after — two colons, and a content property.
Worked examples
The short listings above show one property. These sheets paint documents you would actually ship: a lab dashboard, a clinic form, a marks table, a nav bar.
CSS does not invent meaning. HTML already said what the pieces are. Cascade, specificity, and the box model decide how they look. The numbers are classroom values — current, pH, pulse, 3-4-5 — so the style has something real to sit on.
Preview them in the CSS editor at /css/try. Change one property and watch the page paint. The tags stay the same.
Statistics
Zebra rows and a hover
:hover is a pointer state. :first-child and :nth-child(odd) are structural. No extra class required if the HTML order is already the data order.
:focus is for keyboard. Pair hover and focus when a card is a control. A table row that only highlights on hover fails on a phone with no hover.
Example
<style>
li:nth-child(odd) { background: #e0f2fe; }
li:hover { background: #ccfbf1; }
</style>
<ul>
<li>Ada 72</li>
<li>Ben 81</li>
<li>Cara 64</li>
</ul>Engineering
A live card on hover
The class is the card. :hover is the state. Keep a visible :focus-visible if the card is clickable.
Example
<style>
.card { background: #f0fdfa; padding: 0.6rem; }
.card:hover { background: #ccfbf1; }
</style>
<div class="card">12 mT — hover the logger card</div>