Pseudo Class selectors

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
  1. root
  2. first-child
  3. last-child
  4. nth-child
  5. :nth-last-child(n)
  6. :nth-of-type(n)
  7. :nth-last-of-type(n)
  8. only-child
  9. only-of-type
  10. first-of-type
  11. last-of-type
  12. empty
  13. not
  14. target
  15. lang
  16. is
  17. Link states
  18. 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

This only works if the element has a parent container.

: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

Even spaces count as content, so element is not empty.

: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>
🎭 CSS Pseudo-Class Selectors — Complete Tutorial

🌐 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.

>>“Pseudo-classes let you style based on conditions — hover states, focus, form validation, child position, and more.”

Note

✔ Pseudo-classes begin with :
✔ They are dynamic → change as the user interacts
✔ They DO NOT modify the HTML

📦 Categories of Pseudo-Class Selectors

CategoryDescriptionExamples
User ActionTriggered by user interaction:hover, :active, :focus
UI StatesForm state or validation:checked, :disabled, :valid
StructuralElement position in DOM tree:first-child, :nth-child()
LinguisticBased on language or text:lang()
Time-basedAnimation timing state:playing, :paused
NegationMatches 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 TypeSpecificity
Pseudo-class (e.g., :hover)0-1-0
Class0-1-0
ID1-0-0

Note

Pseudo-class selectors have the same weight as class selectors.

⚠️ 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

✔ Include :focus in all interactive UI styles for accessibility.

🔥 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.

>>“Pseudo-classes bring CSS to life — they react to users and DOM structure.”

Learn more →MDN Docs 📘