Table of Contents
- What Are Pseudo-Classes?
- Common Pseudo-Classes with Examples
- Advanced Pseudo-Classes
- What Are Pseudo-Elements?
- Common Pseudo-Elements with Examples
- Advanced Pseudo-Elements
- Key Differences Between Pseudo-Classes and Pseudo-Elements
- Practical Use Cases and Best Practices
- Conclusion
- References
What Are Pseudo-Classes?
Pseudo-classes are CSS selectors that target elements based on their state, position in the DOM, or relationship to other elements. They allow you to style elements dynamically—for example, when a user hovers over a button, when a form input is focused, or when an element is the first child of its parent.
Pseudo-classes are denoted by a single colon (:) followed by the class name (e.g., :hover, :first-child). They can be combined with other selectors to target specific elements under certain conditions.
Common Pseudo-Classes with Examples
Let’s explore some of the most widely used pseudo-classes and their practical applications:
1. :hover
Targets an element when the user hovers over it with a mouse or pointer.
Example: Styling a button on hover
.button {
padding: 10px 20px;
background: #4CAF50;
color: white;
border: none;
transition: background 0.3s;
}
.button:hover {
background: #45a049; /* Darken the button on hover */
cursor: pointer;
}
2. :active
Targets an element when it is being activated (e.g., clicked or tapped).
Example: Styling a button when clicked
.button:active {
background: #3d8b40; /* Further darken when active */
transform: scale(0.98); /* Slight shrink effect */
}
3. :focus
Targets an element when it gains keyboard focus (e.g., when a user tabs to an input field). Critical for accessibility.
Example: Styling a form input on focus
.input-field {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
.input-field:focus {
outline: none;
border-color: #2196F3; /* Blue border on focus */
box-shadow: 0 0 5px rgba(33, 150, 243, 0.5);
}
4. :first-child and :last-child
Target the first or last child element of a parent.
Example: Styling the first and last items in a list
ul li {
padding: 8px;
border-bottom: 1px solid #eee;
}
ul li:first-child {
border-top: 1px solid #eee; /* Add top border to first item */
}
ul li:last-child {
border-bottom: none; /* Remove bottom border from last item */
}
5. :nth-child(n)
Targets elements based on their position in a parent. The n can be a number, keyword (even, odd), or formula (e.g., 2n for even indices).
Example: Zebra striping a table
.table-row {
padding: 12px;
}
.table-row:nth-child(odd) {
background: #f9f9f9; /* Light gray for odd rows */
}
.table-row:nth-child(even) {
background: #ffffff; /* White for even rows */
}
6. :checked
Targets radio buttons, checkboxes, or option elements when they are selected.
Example: Styling a custom checkbox
/* Hide default checkbox */
.custom-checkbox input {
display: none;
}
/* Style the label as a custom checkbox */
.custom-checkbox label {
position: relative;
padding-left: 25px;
cursor: pointer;
}
/* Create a custom checkbox appearance */
.custom-checkbox label::before {
content: "";
position: absolute;
left: 0;
width: 18px;
height: 18px;
border: 2px solid #ccc;
border-radius: 3px;
}
/* Show a checkmark when checked */
.custom-checkbox input:checked + label::before {
background: #2196F3;
border-color: #2196F3;
}
.custom-checkbox input:checked + label::after {
content: "✓";
position: absolute;
left: 5px;
color: white;
font-size: 12px;
}
Advanced Pseudo-Classes
These pseudo-classes unlock more complex styling logic:
1. :has()
(New in CSS Selectors Level 4) Targets an element that contains another element. This is a game-changer for parent styling!
Example: Style a card if it contains an image
.card {
padding: 15px;
border: 1px solid #ddd;
}
/* If .card has an img, add a blue border */
.card:has(img) {
border-color: #2196F3;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
2. :is()
Simplifies complex selector lists by grouping multiple selectors. It matches any of the selectors in its argument list.
Example: Styling headings consistently
/* Without :is() */
h1 em, h2 em, h3 em, h4 em {
color: #ff5722;
}
/* With :is() */
:is(h1, h2, h3, h4) em {
color: #ff5722; /* Applies to <em> in any heading */
}
3. :where()
Similar to :is(), but with lower specificity, making it easier to override styles.
Example: Base styles with low specificity
/* :where() has 0 specificity, so it can be overridden */
:where(ul, ol) li {
margin: 5px 0;
}
/* This will override the above */
.nav-list li {
margin: 8px 0;
}
4. :not()
Targets elements that do not match a given selector (the “negation” pseudo-class).
Example: Style all list items except the last one
li:not(:last-child) {
margin-bottom: 10px; /* Add space between items except the last */
}
What Are Pseudo-Elements?
Pseudo-elements are CSS selectors that target specific parts of an element or create virtual elements that don’t exist in the DOM. They allow you to style fragments of content, such as the first line of a paragraph, the first letter of a heading, or add decorative content before/after an element.
Pseudo-elements are denoted by a double colon (::) followed by the element name (e.g., ::before, ::first-line). The double colon syntax was introduced in CSS3 to distinguish pseudo-elements from pseudo-classes (though single colons still work for backward compatibility).
Common Pseudo-Elements with Examples
Pseudo-elements act as “virtual” elements, enabling you to inject content or style specific parts of an element:
1. ::before and ::after
Create virtual elements before or after the content of the selected element. They require the content property (even if it’s an empty string).
Example: Adding an icon before a link
.external-link {
color: #2196F3;
text-decoration: none;
}
.external-link::after {
content: " ↗"; /* Add an arrow icon after the link */
font-size: 0.8em;
}
/* Example with an image */
.pdf-link::before {
content: url("pdf-icon.png"); /* Add a PDF icon before the link */
margin-right: 5px;
}
2. ::first-line
Styles the first line of a block-level element (e.g., a paragraph).
Example: Styling the first line of a blog post
.blog-intro::first-line {
font-weight: bold;
font-size: 1.1em;
color: #333;
}
3. ::first-letter
Styles the first letter of a block-level element (often used for drop caps).
Example: Drop cap for a paragraph
.article-paragraph::first-letter {
font-size: 3em;
float: left;
color: #ff5722;
margin-right: 5px;
line-height: 0.8;
}
4. ::selection
Styles text that the user has selected (e.g., highlighted with the mouse).
Example: Custom selection color
::selection {
background: #ffeb3b; /* Yellow background for selected text */
color: #333;
}
/* For Firefox */
::-moz-selection {
background: #ffeb3b;
color: #333;
}
Advanced Pseudo-Elements
These pseudo-elements unlock more specialized styling:
1. ::marker
Styles the marker (e.g., bullet, number) of list items (<li>).
Example: Custom bullet style for a list
ul li::marker {
color: #2196F3; /* Blue bullets */
font-size: 1.2em;
content: "• "; /* Override default bullet */
}
ol li::marker {
font-weight: bold; /* Bold numbers for ordered lists */
}
2. ::placeholder
Styles the placeholder text of form inputs (e.g., <input>, <textarea>).
Example: Custom placeholder styling
.input-field::placeholder {
color: #999;
font-style: italic;
opacity: 0.7; /* Reduce opacity for better contrast */
}
3. ::file-selector-button
Styles the “Browse” button in a file input (<input type="file">).
Example: Custom file input button
input[type="file"]::file-selector-button {
padding: 8px 16px;
background: #2196F3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
input[type="file"]::file-selector-button:hover {
background: #1976D2;
}
Key Differences Between Pseudo-Classes and Pseudo-Elements
It’s easy to confuse pseudo-classes and pseudo-elements, so let’s clarify their core differences:
| Feature | Pseudo-Classes | Pseudo-Elements |
|---|---|---|
| Purpose | Target elements based on state/position. | Target parts of an element or create virtual elements. |
| Syntax | Single colon (:) (e.g., :hover). | Double colon (::) (e.g., ::before). |
| DOM Presence | No new elements created. | Creates virtual elements (not in the DOM). |
| Example Use Cases | Hover states, focused inputs, first-child. | First letter, before/after content, selection. |
| Combining with Others | Can be combined with other selectors. | Only one pseudo-element per selector. |
Practical Use Cases and Best Practices
Use Cases
- Form Styling: Use
:focus,:checked, and::placeholderto enhance form usability. - Dynamic Interactions:
:hover,:active, and:has()for responsive UI (e.g., tooltips, parent styling). - Content Decoration:
::before/::afterfor icons, quotes, or decorative dividers. - Typography:
::first-letter,::first-linefor editorial styling. - Accessibility:
:focusensures keyboard users can navigate your site.
Best Practices
- Browser Compatibility: Check support for newer features like
:has()(supported in modern browsers) and::markerusing tools like Can I Use. - Double Colons for Pseudo-Elements: Use
::for pseudo-elements (e.g.,::before) and:for pseudo-classes (e.g.,:hover) to follow CSS3 standards. - Avoid Overusing Complex Selectors: Overly specific selectors (e.g.,
div:has(p:only-child)) can hurt performance. - Test Accessibility: Ensure
:focusstyles are visible for keyboard users, and::selectiondoesn’t harm readability.
Conclusion
Pseudo-classes and pseudo-elements are powerful tools that extend CSS beyond static styling, enabling dynamic, interactive, and visually rich web interfaces. By mastering them, you can reduce HTML clutter, enhance user experience, and write more maintainable code.
From styling hover states to creating custom form controls or adding decorative content, these selectors unlock endless possibilities. Experiment with :has() for parent styling, ::before/::after for creative content, and :focus for accessibility—your users (and future self) will thank you!