A pseudo-class lets you style an element in a special state, like when a user hovers over it or when a link has been visited.
- When a user moves the mouse over an element
- Style links differently if they are visited or not
- When an element gets keyboard focus
Common Types of Pseudo-Class Selectors
- root
- first-child
- last-child
- nth-child
- :nth-last-child(n)
- :nth-of-type(n)
- :nth-last-of-type(n)
- only-child
- only-of-type
- first-of-type
- last-of-type
- empty
- not
- target
- lang
- is
- Link states
- Form states
:root
The :rootselector targets the root element of the document (usually <html>).
style.css
/* Style the root element */
:root {
--main-color: blue;
font-size: 18px;
}
body {
color: var(--main-color);
}:first-child
Selects the first child element among its siblings.
style.css
/* Style the first child element */
p:first-child {
color: red;
}index.html
<div>
<p>This paragraph is first child and will be red.</p>
<p>This paragraph is not first child.</p>
</div>:last-child
Selects the last child element of its parent.
style.css
ul li:last-child {
font-weight: bold;
}index.html
<ul>
<li>First item</li>
<li>Last item (bold)</li>
</ul>Note
:nth-child(n)
Selects the nth child (of any type) among its parent's children. Accepts patterns like 2n, odd, even, and 3n-1.
style.css
/* Even children */
ul li:nth-child(even) {
background-color: #e0f7fa;
}
/* Odd children */
ul li:nth-child(odd) {
background-color: #fce4ec;
}
/* Every 2nd child */
ul li:nth-child(2n) {
font-style: italic;
}
/* Custom pattern: 3n-1 */
ul li:nth-child(3n-1) {
font-weight: bold;
color: seagreen;
}index.html
<ul>
<li>Item 1 (1st child)</li>
<li>Item 2 (2nd child)</li>
<li>Item 3 (3rd child)</li>
<li>Item 4 (4th child)</li>
<li>Item 5 (5th child)</li>
<li>Item 6 (6th child)</li>
</ul>:nth-last-child(n)
Selects the nth child from the end among its siblings. Useful with patterns like 2n, odd, even, 3n-1.
style.css
/* Even children from end */
ul li:nth-last-child(even) {
background-color: #ffecb3;
}
/* Odd children from end */
ul li:nth-last-child(odd) {
background-color: #e1bee7;
}
/* Every 2nd child from end */
ul li:nth-last-child(2n) {
text-decoration: underline;
}
/* Custom pattern: 3n-1 from end */
ul li:nth-last-child(3n-1) {
border: 1px dashed #777;
}index.html
<ul>
<li>Item 1 (1st child)</li>
<li>Item 2 (2nd child)</li>
<li>Item 3 (3rd child)</li>
<li>Item 4 (4th child)</li>
<li>Item 5 (5th child)</li>
<li>Item 6 (6th child)</li>
</ul>:nth-of-type(n)
Selects the nth element of a specific type among its siblings. Counts only siblings of the same type. You can use formulas like 2n (even), 2n+1 (odd), 3n-1, etc.
style.css
/* Even paragraphs */
section p:nth-of-type(even) {
background-color: #f1f1f1;
}
/* Odd paragraphs */
section p:nth-of-type(odd) {
color: darkblue;
}
/* Every 2nd element (same as even) */
section p:nth-of-type(2n) {
font-weight: bold;
}
/* Custom pattern: 3n-1 */
section p:nth-of-type(3n-1) {
text-decoration: underline;
color: seagreen;
}index.html
<section>
<p>Paragraph 1 (1st child)</p>
<p>Paragraph 2 (2nd child)</p>
<p>Paragraph 3 (3rd child)</p>
<spam>ignored</spam>
<p>Paragraph 4 (4th child)</p>
<p>Paragraph 5 (5th child)</p>
</section>:nth-last-of-type(n)
Selects the nth element of a specific type counting from the end among its siblings. Counts only siblings of the same type. Like :nth-of-type, formulas like 2n, odd, or 3n-1 can be used.
style.css
/* Even paragraphs from end */
section p:nth-last-of-type(even) {
background-color: #ffe4e1;
}
/* Odd paragraphs from end */
section p:nth-last-of-type(odd) {
color: maroon;
}
/* Every 2nd element from end */
section p:nth-last-of-type(2n) {
font-style: italic;
}
/* Custom pattern: 3n-1 from end */
section p:nth-last-of-type(3n-1) {
border-bottom: 1px dashed black;
}index.html
<section>
<p>Paragraph 1 (1st child)</p>
<p>Paragraph 2 (2nd child)</p>
<p>Paragraph 3 (3rd child)</p>
<spam>ignored</spam>
<p>Paragraph 4 (4th child)</p>
<p>Paragraph 5 (5th child)</p>
</section>:only-child
Selects an element that is the only child of its parent, regardless of type. If the parent has more than one child element, none will be selected.
style.css
/* Style any element that is the only child of its parent */
p:only-child {
color: white;
background-color: #ff7043;
padding: 10px;
border-radius: 5px;
}
div > h2:only-child {
font-style: italic;
border-bottom: 2px solid #673ab7;
}index.html
<div>
<p>This paragraph is the only child of its parent div and will be styled.</p>
</div>
<div>
<h2>This h2 is the only child inside this div and will be styled.</h2>
</div>
<div>
<p>First paragraph</p>
<p>Second paragraph</p>
</div>:only-of-type
Selects an element that is the only one of its type among its siblings. If there are multiple elements of the same type, none will be selected.
style.css
/* Style the only <p> element among siblings */
p:only-of-type {
color: white;
background-color: #4caf50;
padding: 10px;
border-radius: 5px;
}
/* Style the only <h2> element among siblings */
h2:only-of-type {
font-style: italic;
border-bottom: 2px solid #2196f3;
}index.html
<div>
<p>This paragraph is the only paragraph inside this div and will be styled.</p>
<h2>This heading is the only h2 here and will be styled.</h2>
<span>Some other element</span>
</div>
<div>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
<h2>Heading 1</h2>
<h2>Heading 2</h2>
</div>:empty
Selects elements that have no children at all (no text or elements).
style.css
div:empty {
background-color: yellow;
border: 1px solid orange;
}index.html
<div></div> <!-- Will be yellow -->
<div> </div> <!-- Not empty because of space -->
<div><p>Not empty</p></div>Note
:not(selector)
Selects elements that do NOT match the selector inside :not().
style.css
p:not(.special) {
color: gray;
}index.html
<p>This paragraph is gray.</p>
<p class="special">This paragraph is not gray.</p>:lang(language-code)
Selects elements based on their language attribute.
style.css
p:lang(en) {
color: blue;
}
p:lang(fr) {
color: red;
}index.html
<p lang="en">This paragraph is blue.</p>
<p lang="fr">This paragraph is red.</p>:is(selector-list)
Matches any element matching one of the selectors inside :is(). Makes writing complex selectors easier.
style.css
/* Select h1 or h2 inside #container */
#container :is(h1, h2) {
color: green;
}index.html
<div id="container">
<h1>Heading 1</h1>
<h2>Heading 2</h2>
<p>Paragraph</p>
</div>Link states: :link, :visited, :hover, :active
These pseudo-classes are used to style links in different states:
- :link - Unvisited links
- :visited - Visited links
- :hover - When mouse is over the link
- :active - When the link is being clicked
style.css
a:link {
color: blue;
}
a:visited {
color: purple;
}
a:hover {
color: red;
background-color: yellow;
}
a:active {
color: orange;
}index.html
<a href="https://example.com">Example Link</a>:target
Styles the element targeted by the URL fragment (the part after #).
style.css
p:target {
background-color: lightgreen;
font-weight: bold;
}index.html
<a href="#section1">Go to Section 1</a>
<p id="section1">This paragraph is highlighted when targeted.</p>Form states
:focus styles an element when it has keyboard or mouse focus.
style.css
#txt:focus {
outline: none;
border: 1px solid brown;
}index.html
<input type="text" id="txt" />:focus-visible styles an element only when focus is visible, usually during keyboard navigation (Tab key). This prevents focus rings from appearing when clicking with a mouse.
style.css
input:focus-visible {
outline: 3px solid dodgerblue;
background: #eef7ff;
}index.html
<input type="text" placeholder="Focus-visible example" />:focus-within applies styling to a parent element whenany of its children receive focus.
Useful for styling input wrappers, forms, and card components.
style.css
.input-group {
padding: 10px;
border: 2px solid #ccc;
border-radius: 6px;
}
.input-group:focus-within {
border-color: mediumseagreen;
background: #f0fff4;
}:checked styles a checkbox or radio button when it is checked.
style.css
input[type="checkbox"]:checked {
box-shadow: 0 0 0 3px red;
}index.html
<input type="checkbox" id="cricket" />
<label for="cricket">Cricket</label>:enabled styles enabled form elements, :disabled styles disabled ones, and :required styles required inputs.
style.css
input[type="text"]:enabled {
background-color: pink;
}
input[type="text"]:disabled {
background-color: red;
}
input[type="text"]:required {
background-color: green;
}index.html
<input type="text" /> <!-- enabled -->
<input type="text" disabled /> <!-- disabled -->
<input type="text" required /> <!-- required -->:read-only styles inputs that cannot be edited, and :read-write styles inputs that can be edited.
style.css
input[type="email"]:read-only {
background-color: gray;
}
input[type="email"]:read-write {
background-color: plum;
}index.html
<input type="email" />
<input type="email" readonly />:valid styles inputs that match their validation pattern, and :invalid styles those that don’t.
style.csS
input[type="text"]:invalid {
border-color: red;
}
input[type="text"]:valid {
border-color: rgb(15, 225, 15);
}index.html
<input type="text" placeholder="Enter Username" pattern="[a-z]*" />:default styles the initially selected radio button or select option.
style.css
input[type="radio"]:default {
box-shadow: 0 0 0 3px green;
}
option:default {
color: red;
}index.html
<input type="radio" name="gender" id="male" checked />
<label for="male">Male</label>
<input type="radio" name="gender" id="female" />
<label for="female">Female</label>
<select>
<option value="">select</option>
<option value="C">C</option>
<option value="C++">C++</option>
<option value="Java">Java</option>
<option value="CSS" selected>CSS</option>
</select>🌐 What Are Pseudo-Class Selectors?
A pseudo-class represents a special *state* of an element. It allows you to style elements not just by what they are, but byhow they behave, how users interact with them, or how they are positioned in the document tree.
Note
✔ They are dynamic → change as the user interacts
✔ They DO NOT modify the HTML
📦 Categories of Pseudo-Class Selectors
| Category | Description | Examples |
|---|---|---|
| User Action | Triggered by user interaction | :hover, :active, :focus |
| UI States | Form state or validation | :checked, :disabled, :valid |
| Structural | Element position in DOM tree | :first-child, :nth-child() |
| Linguistic | Based on language or text | :lang() |
| Time-based | Animation timing state | :playing, :paused |
| Negation | Matches elements NOT fitting a selector | :not() |
🎯 1. User Action Pseudo-Classes
:hover
Triggered when the mouse pointer is over an element.
Hover Example
button:hover {
background: blue;
color: white;
}:active
Applies while the element is being clicked or activated.
Active Example
button:active {
transform: scale(0.98);
}:focus
Triggered when an element (input, button) receives keyboard focus.
Focus Example
input:focus {
outline: 2px solid blue;
}🔐 2. Form & UI State Pseudo-Classes
:checked
Checked Example
input[type="checkbox"]:checked {
accent-color: green;
}:disabled
Disabled Example
button:disabled {
opacity: 0.5;
}:valid and :invalid
Validation Example
input:valid {
border-color: green;
}
input:invalid {
border-color: red;
}🏗️ 3. Structural Pseudo-Classes
:first-child
Targets the first child of its parent.
First-child
p:first-child {
font-weight: bold;
}:last-child
Last-child
p:last-child {
color: gray;
}:nth-child()
Matches elements based on a mathematical pattern.
nth-child
li:nth-child(2n) {
background: #f0f0f0;
}Styles every even element.
:nth-of-type()
nth-of-type
p:nth-of-type(3) {
color: purple;
}:only-child
only-child
div:only-child {
border: 1px solid red;
}🗣️ 4. Linguistic Pseudo-Class
:lang()
Lang Example
p:lang(en) {
font-style: italic;
}⏯️ 5. Time-Based Pseudo-Classes
:playing / :paused
Matches media elements in playback states.
Media State
video:playing {
outline: 3px solid green;
}🚫 6. Negation Pseudo-Class — :not()
Selects every element that does NOT match a given selector.
not() Example
button:not(.primary) {
background: #ddd;
}🧪 Practical Examples
✔ Button Interactive States
Button States
button:hover:not(:disabled) {
background: black;
color: white;
}✔ Form Enhancements
Valid + Focus Styling
input:focus:valid {
border-color: lime;
}✔ Striped Table Rows
Table Stripe
tr:nth-child(odd) {
background: #fafafa;
}✔ Navbar Active State
Active Link
nav a.active,
nav a:hover {
color: red;
}🧠 Specificity Reminder
| Selector Type | Specificity |
|---|---|
| Pseudo-class (e.g., :hover) | 0-1-0 |
| Class | 0-1-0 |
| ID | 1-0-0 |
Note
⚠️ Common Mistakes
- ❌ Using :hover styles without :focus → inaccessible for keyboard users
- ❌ Overusing structural pseudo-classes — can harm performance
- ❌ Confusing :nth-child() with :nth-of-type()
- ❌ Forgetting that :not() still counts towards specificity
Note
🔥 Summary
CSS pseudo-class selectors allow developers to style elements based on state, structure, interaction, and more. Mastering them unlocks dynamic, flexible, and user-friendly UI designs.
Learn more →MDN Docs 📘