CSS Pseudo-classes: The Art of Dynamic Styles and User Interaction â
Understanding Pseudo-classes â
Pseudo-classes are special selectors in CSS that select elements based on their state or position, rather than the element's attributes themselves. You can think of pseudo-classes as "style switches that activate under specific conditions."
Pseudo-classes use single colon : syntax, such as :hover, :first-child, :checked, etc. They allow us to implement rich interactive effects and precise style control without modifying HTML structure or using JavaScript.
/* Basic syntax */
selector:pseudo-class {
property: value;
}
/* Example */
button:hover {
background-color: #3498db;
}User Behavior Pseudo-classes â
These pseudo-classes respond to user actions and are core tools for creating interactive experiences.
:hover - Mouse Hover â
Triggered when the mouse pointer hovers over an element:
/* Basic hover effect */
a:hover {
color: #e74c3c;
text-decoration: underline;
}
/* Button hover */
.button:hover {
background-color: #2980b9;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
}
/* Card hover */
.card:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
.card:hover img {
transform: scale(1.05);
}
/* Navigation menu */
.nav-item:hover .dropdown-menu {
display: block;
opacity: 1;
visibility: visible;
}:active - Active State â
The momentary state when an element is clicked:
button:active {
transform: scale(0.98);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.link:active {
color: #c0392b;
}
/* Ripple effect foundation */
.ripple:active::after {
animation: ripple-animation 0.6s ease-out;
}:focus - Focus State â
When an element receives focus (usually form elements or focusable elements):
/* Input box focus */
input:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
textarea:focus {
border-color: #3498db;
background-color: #f8f9fa;
}
/* Accessibility enhancement */
.button:focus {
outline: 2px solid #3498db;
outline-offset: 2px;
}
/* Focus visibility control */
.nav-link:focus-visible {
outline: 2px dashed #3498db;
outline-offset: 4px;
}:focus-within - Internal Focus â
Triggered when the element itself or any of its descendants receives focus:
/* Form group focus highlight */
.form-group:focus-within {
background-color: #f8f9fa;
border-left: 3px solid #3498db;
padding-left: 17px;
}
/* Search box container */
.search-container:focus-within {
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.2);
}
.search-container:focus-within .search-icon {
color: #3498db;
}
/* Sidebar highlight */
.sidebar:focus-within {
border-right: 2px solid #3498db;
}Structural Pseudo-classes â
Structural pseudo-classes select elements based on their position in the DOM tree.
:first-child and :last-child â
/* First child element */
li:first-child {
border-top: none;
}
.card:first-child {
margin-top: 0;
}
/* Last child element */
li:last-child {
border-bottom: none;
}
.section:last-child {
margin-bottom: 0;
}
/* Combined usage */
.breadcrumb li:first-child::before {
content: none; /* Remove separator from first item */
}:nth-child() - Flexible Position Selection â
nth-child() is one of the most powerful structural pseudo-classes, supporting multiple parameters:
/* Select even items */
tr:nth-child(even) {
background-color: #f8f9fa;
}
/* Select odd items */
tr:nth-child(odd) {
background-color: white;
}
/* Select the 3rd item */
li:nth-child(3) {
color: #e74c3c;
}
/* Select first 3 items */
li:nth-child(-n + 3) {
font-weight: bold;
}
/* Start from the 4th item */
li:nth-child(n + 4) {
opacity: 0.7;
}
/* Every 3 items in a group, select the first one */
.item:nth-child(3n + 1) {
clear: left;
}
/* Practical case: grid layout */
.gallery-item:nth-child(4n) {
margin-right: 0; /* Last item in each row doesn't need right margin */
}
/* Advanced: select middle items */
li:nth-child(n + 3):nth-child(-n + 7) {
color: #3498db; /* Select items 3 to 7 */
}:nth-of-type() â
Similar to :nth-child(), but only counts elements of the same type:
/* Select all even paragraphs */
p:nth-of-type(even) {
background-color: #f8f9fa;
}
/* Select first 2 h2 headings */
h2:nth-of-type(-n + 2) {
color: #e74c3c;
font-size: 2em;
}
/* Practical application: article styling */
article p:nth-of-type(1) {
font-size: 1.2em; /* First paragraph lead-in effect */
font-style: italic;
}:only-child and :only-of-type â
/* Only child element */
.container p:only-child {
margin: 0; /* No margin needed when only one paragraph */
}
/* Only element of this type */
li:only-of-type {
list-style: none; /* Remove bullet when list has only one item */
}
/* Practical scenario */
.button-group button:only-child {
border-radius: 4px; /* All corners rounded when only one button */
}:empty - Empty Element â
Selects elements that have no children (including text nodes):
/* Hide empty containers */
.notification:empty {
display: none;
}
/* Empty state prompt */
.list:empty::before {
content: "No data available";
color: #999;
font-style: italic;
}
/* Empty table cells */
td:empty {
background-color: #f5f5f5;
}
td:empty::after {
content: "-";
color: #ccc;
}Form Pseudo-classes â
These pseudo-classes are specifically for handling various states of form elements.
:disabled and :enabled â
/* Disabled state */
input:disabled,
button:disabled {
background-color: #e9ecef;
color: #6c757d;
cursor: not-allowed;
opacity: 0.6;
}
/* Enabled state */
input:enabled {
background-color: white;
cursor: text;
}
/* Disabled button doesn't respond to hover */
button:disabled:hover {
background-color: #e9ecef;
transform: none;
}:checked - Checked State â
Used for radio buttons, checkboxes, and <option> elements:
/* Custom checkbox */
input[type="checkbox"]:checked + label::before {
background-color: #3498db;
border-color: #3498db;
}
input[type="checkbox"]:checked + label::after {
content: "â";
color: white;
position: absolute;
left: 4px;
top: 0;
}
/* Toggle switch effect */
.switch input:checked + .slider {
background-color: #3498db;
}
.switch input:checked + .slider::before {
transform: translateX(20px);
}
/* Tabs */
.tab-input:checked + .tab-label {
background-color: white;
border-bottom-color: white;
color: #3498db;
}
.tab-input:checked ~ .tab-content {
display: block;
}:required and :optional â
/* Required field indicator */
input:required {
border-left: 3px solid #e74c3c;
}
input:required + label::after {
content: " *";
color: #e74c3c;
}
/* Optional field */
input:optional {
border-left: 3px solid #95a5a6;
}:valid and :invalid â
Based on HTML5 form validation rules:
/* Valid input */
input:valid {
border-color: #27ae60;
}
input:valid + .icon::before {
content: "â";
color: #27ae60;
}
/* Invalid input */
input:invalid {
border-color: #e74c3c;
}
input:invalid + .error-message {
display: block;
color: #e74c3c;
font-size: 0.875em;
margin-top: 4px;
}
/* Avoid showing error in initial state */
input:invalid:not(:focus):not(:placeholder-shown) {
border-color: #e74c3c;
}:in-range and :out-of-range â
Used for input boxes with min and max attributes:
input[type="number"]:in-range {
border-color: #27ae60;
background-color: #d5f4e6;
}
input[type="number"]:out-of-range {
border-color: #e74c3c;
background-color: #fadbd8;
}:placeholder-shown â
When the input box displays placeholder text:
input:placeholder-shown {
border-color: #ddd;
}
/* Floating label effect */
input:not(:placeholder-shown) + label {
transform: translateY(-24px) scale(0.85);
color: #3498db;
}Link Pseudo-classes â
Pseudo-classes specifically for <a> elements:
/* Unvisited link */
a:link {
color: #3498db;
}
/* Visited link */
a:visited {
color: #9b59b6;
}
/* Link pseudo-class order is important: LVHA */
/* :link - :visited - :hover - :active */
/* Practical application */
.nav-link:link,
.nav-link:visited {
color: #2c3e50;
text-decoration: none;
}
.nav-link:hover {
color: #3498db;
}
.nav-link:active {
color: #2980b9;
}Negation Pseudo-class â
The :not() selector allows you to exclude specific elements:
/* All li except the last one have bottom border */
li:not(:last-child) {
border-bottom: 1px solid #ddd;
}
/* All buttons except disabled ones can be hovered */
button:not(:disabled):hover {
background-color: #2980b9;
}
/* Exclude specific class */
input:not(.no-border) {
border: 1px solid #ddd;
}
/* Complex combination */
a:not([href^="http"]):not([href^="mailto"]) {
/* Only select internal links */
color: #2c3e50;
}
/* Multiple conditions */
input:not(:disabled):not(:read-only):focus {
background-color: #f8f9fa;
}Other Practical Pseudo-classes â
:target - URL Fragment Target â
When the URL fragment identifier matches an element's ID:
/* Highlight anchor target */
:target {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding-left: 16px;
animation: highlight 2s ease;
}
@keyframes highlight {
from {
background-color: #fff3cd;
}
to {
background-color: transparent;
}
}
/* Modal */
.modal:target {
display: block;
opacity: 1;
visibility: visible;
}:root - Root Element â
Selects the root element of the document, typically used to define CSS variables:
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--font-size-base: 16px;
--spacing-unit: 8px;
}
.button {
background-color: var(--primary-color);
padding: calc(var(--spacing-unit) * 2);
}Practical Case Studies â
Smart Form Validation â
.form-field {
position: relative;
margin-bottom: 20px;
}
/* Initial state */
.form-field input {
border: 2px solid #ddd;
padding: 12px;
transition: all 0.3s;
}
/* Focus state */
.form-field input:focus {
border-color: #3498db;
outline: none;
}
/* Valid input */
.form-field input:valid:not(:placeholder-shown) {
border-color: #27ae60;
padding-right: 40px;
}
.form-field input:valid:not(:placeholder-shown) + .status-icon::after {
content: "â";
color: #27ae60;
}
/* Invalid input */
.form-field input:invalid:not(:focus):not(:placeholder-shown) {
border-color: #e74c3c;
}
.form-field input:invalid:not(:focus):not(:placeholder-shown) ~ .error {
display: block;
}
/* Required field indicator */
.form-field input:required ~ label::after {
content: " *";
color: #e74c3c;
}Responsive Card Grid â
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
/* First card in each row */
.card:nth-child(4n + 1) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
/* Hover effect */
.card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
}
/* Other cards reduce opacity except hovered one */
.card-grid:hover .card:not(:hover) {
opacity: 0.7;
}Pure CSS Accordion â
.accordion-item input[type="checkbox"] {
display: none;
}
.accordion-header {
cursor: pointer;
padding: 15px;
background-color: #f8f9fa;
border-bottom: 1px solid #ddd;
}
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
input[type="checkbox"]:checked ~ .accordion-content {
max-height: 500px;
}
input[type="checkbox"]:checked ~ .accordion-header {
background-color: #3498db;
color: white;
}
input[type="checkbox"]:checked ~ .accordion-header::after {
transform: rotate(180deg);
}Summary â
Pseudo-classes are one of the most powerful tools in CSS. Mastering pseudo-classes allows you to create rich interactive effects without relying on JavaScript, making user interfaces more dynamic and friendly.
Key Points:
- User Behavior Pseudo-classes: Respond to user actions, create interactive experiences
:hover- Mouse hover state:active- Active state:focus- Focus state:focus-within- Internal focus state
- Structural Pseudo-classes: Select based on element position in DOM tree
:first-child,:last-child- First and last child elements:nth-child(),:nth-of-type()- Flexible position selection:only-child,:empty- Special state elements
- Form Pseudo-classes: Specifically handle various states of form elements
:disabled,:enabled- Disabled/enabled state:checked- Checked state:valid,:invalid- Validation state:placeholder-shown- Placeholder display state
- Negation Pseudo-class:
:not()excludes specific elements - Practical Applications: Smart form validation, responsive cards, pure CSS interactive components