CSS Pseudo-classes: The Magic of Element States ✨
Welcome to Professor McGonagall's Transfiguration class! Today, we'll master CSS Pseudo-classes - the magical art of styling elements based on their state or position. Just like a wizard's spell can change based on circumstances, elements can change their appearance based on different states! 🧙♂️
Understanding Pseudo-classes 📚
User Action Pseudo-classes 🖱️
How do elements respond to user interactions?
Like a magical object responding to touch, these pseudo-classes react to user actions.
.magical-button {
background: #540099;
color: white;
transition: all 0.3s ease;
/* Mouse hover */
&:hover {
background: #6600cc;
transform: translateY(-2px);
}
/* Active state (clicking) */
&:active {
transform: translateY(1px);
}
/* Focus state (keyboard navigation) */
&:focus {
outline: 3px solid rgba(84, 0, 153, 0.5);
outline-offset: 2px;
}
/* Focus within (container) */
&:focus-within {
background: #f0e6ff;
}
}
Form State Pseudo-classes 📝
How do we style form elements based on their state?
Like a potion changing colors based on its ingredients, form elements can change based on their state.
.magical-input {
border: 2px solid #ccc;
transition: all 0.3s ease;
/* When input has content */
&:not(:placeholder-shown) {
border-color: #540099;
}
/* Valid input state */
&:valid {
border-color: #00994d;
background: rgba(0, 153, 77, 0.1);
}
/* Invalid input state */
&:invalid {
border-color: #cc0000;
background: rgba(204, 0, 0, 0.1);
}
/* Disabled state */
&:disabled {
background: #f5f5f5;
cursor: not-allowed;
opacity: 0.7;
}
/* Required fields */
&:required {
border-left-width: 4px;
}
/* Read-only state */
&:read-only {
background: #f8f8f8;
cursor: default;
}
/* Optional fields */
&:optional {
border-style: dashed;
}
}
Structural Pseudo-classes 🏗️
How do we select elements based on their position?
Like selecting specific students in a line, these pseudo-classes target elements based on their position.
.spell-list {
/* First child */
li:first-child {
font-weight: bold;
border-top: none;
}
/* Last child */
li:last-child {
border-bottom: none;
}
/* Specific position */
li:nth-child(3) {
color: #540099;
}
/* Every other item */
li:nth-child(odd) {
background: rgba(84, 0, 153, 0.1);
}
/* Every third item */
li:nth-child(3n) {
margin-bottom: 2em;
}
/* First of type */
p:first-of-type {
font-size: 1.2em;
}
/* Empty elements */
li:empty {
display: none;
}
}
Link State Pseudo-classes 🔗
How do we style links in different states?
Like a magic portal changing colors as you use it, links can have different states.
.magical-link {
/* Unvisited link */
&:link {
color: #540099;
text-decoration: none;
}
/* Visited link */
&:visited {
color: #800080;
}
/* Mouse over link */
&:hover {
color: #6600cc;
text-decoration: underline;
}
/* Active (clicking) link */
&:active {
color: #cc00ff;
}
}
Advanced Selectors 🎯
Combining Pseudo-classes
Complex State Selection
.magical-form {
/* Valid input that's not empty */
input:valid:not(:placeholder-shown) {
border-color: green;
}
/* First required input */
input:required:first-of-type {
border-left: 4px solid red;
}
/* Hover state on enabled buttons */
button:enabled:hover {
transform: scale(1.05);
}
}
Negation Pseudo-class (:not)
Excluding Elements
.spell-list {
/* All items except the last */
li:not(:last-child) {
border-bottom: 1px solid #eee;
}
/* All inputs except disabled ones */
input:not(:disabled) {
background: white;
}
/* Everything except certain classes */
*:not(.exclude):not(.another-exclude) {
margin-bottom: 1rem;
}
}
Common Patterns 📝
1. Interactive Cards
Card Interactions
.magical-card {
transition: all 0.3s ease;
/* Hover effects */
&:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
/* Focus for accessibility */
&:focus-within {
outline: 3px solid #540099;
outline-offset: 2px;
}
/* Disabled state */
&.disabled {
opacity: 0.7;
&:hover {
transform: none;
box-shadow: none;
}
}
}
2. Form Validation
Form States
.magical-form {
/* Label states */
label {
&:has(+ input:required)::after {
content: "*";
color: red;
}
&:has(+ input:valid) {
color: green;
}
}
/* Input states */
.input-group {
position: relative;
input {
border: 2px solid #ccc;
&:focus {
border-color: #540099;
outline: none;
}
&:valid:not(:placeholder-shown) {
border-color: green;
& + .validation-icon::before {
content: "✓";
color: green;
}
}
&:invalid:not(:placeholder-shown) {
border-color: red;
& + .validation-icon::before {
content: "✕";
color: red;
}
}
}
}
}
3. Navigation Menus
Navigation States
.magical-nav {
/* Current page */
.nav-link:is(.active, [aria-current="page"]) {
color: #540099;
font-weight: bold;
}
/* Dropdown behavior */
.dropdown {
display: none;
.nav-item:hover > & {
display: block;
}
}
/* Mobile states */
@media (max-width: 768px) {
.nav-item:not(:last-child) {
border-bottom: 1px solid #eee;
}
}
}
Practical Tasks 📚
Task 1: Create an Interactive Form
Task
Create a form with comprehensive state styling:
- Input states (focus, valid, invalid)
- Required fields
- Disabled states
- Custom checkboxes and radio buttons
Answer
.magical-form {
/* Form layout */
display: grid;
gap: 1.5rem;
/* Form groups */
.form-group {
position: relative;
/* Labels */
label {
display: block;
margin-bottom: 0.5rem;
color: #333;
&:has(+ input:required)::after {
content: "*";
color: #cc0000;
margin-left: 0.25rem;
}
}
/* Inputs */
input:not([type="checkbox"], [type="radio"]) {
width: 100%;
padding: 0.75rem;
border: 2px solid #ccc;
border-radius: 4px;
transition: all 0.3s ease;
&:hover:not(:disabled) {
border-color: #666;
}
&:focus {
outline: none;
border-color: #540099;
box-shadow: 0 0 0 3px rgba(84, 0, 153, 0.2);
}
&:valid:not(:placeholder-shown) {
border-color: #00994d;
& ~ .validation-icon::before {
content: "✓";
color: #00994d;
}
}
&:invalid:not(:placeholder-shown) {
border-color: #cc0000;
& ~ .validation-icon::before {
content: "✕";
color: #cc0000;
}
}
&:disabled {
background: #f5f5f5;
cursor: not-allowed;
opacity: 0.7;
}
}
/* Custom checkbox */
.checkbox-wrapper {
position: relative;
padding-left: 2rem;
input[type="checkbox"] {
position: absolute;
opacity: 0;
& + label::before {
content: "";
position: absolute;
left: 0;
top: 0.25rem;
width: 1.25rem;
height: 1.25rem;
border: 2px solid #ccc;
border-radius: 4px;
transition: all 0.3s ease;
}
&:checked + label::before {
background: #540099;
border-color: #540099;
}
&:checked + label::after {
content: "✓";
position: absolute;
left: 0.25rem;
top: 0.25rem;
color: white;
font-size: 0.875rem;
}
&:focus + label::before {
box-shadow: 0 0 0 3px rgba(84, 0, 153, 0.2);
}
&:disabled + label {
opacity: 0.7;
cursor: not-allowed;
}
}
}
}
}
Task 2: Create an Interactive Menu System
Task
Create a multi-level navigation menu with:
- Hover states
- Active states
- Current page indication
- Mobile responsiveness
Answer
.magical-menu {
/* Base styles */
--menu-bg: white;
--menu-hover: #f5f0ff;
--menu-active: #540099;
/* Menu container */
.nav {
display: flex;
gap: 1rem;
@media (max-width: 768px) {
flex-direction: column;
}
/* Menu items */
.nav-item {
position: relative;
/* Link styles */
> .nav-link {
display: block;
padding: 0.5rem 1rem;
color: #333;
text-decoration: none;
transition: all 0.3s ease;
&:hover {
background: var(--menu-hover);
}
&:is(.active, [aria-current="page"]) {
color: var(--menu-active);
font-weight: 500;
}
/* Dropdown indicator */
&:has(+ .dropdown)::after {
content: "▼";
margin-left: 0.5rem;
font-size: 0.75em;
}
}
/* Dropdown menu */
.dropdown {
position: absolute;
top: 100%;
left: 0;
min-width: 200px;
background: var(--menu-bg);
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
opacity: 0;
visibility: hidden;
transform: translateY(10px);
transition: all 0.3s ease;
@media (max-width: 768px) {
position: static;
box-shadow: none;
transform: none;
display: none;
}
.nav-item {
.nav-link {
padding: 0.75rem 1rem;
&:hover {
background: var(--menu-hover);
}
}
}
}
/* Show dropdown on hover */
&:hover > .dropdown {
opacity: 1;
visibility: visible;
transform: translateY(0);
@media (max-width: 768px) {
display: block;
}
}
/* Active state */
&:active > .nav-link {
transform: translateY(1px);
}
}
}
}
Additional Study Materials 📖
References 📚
Conclusion 🎉
Remember, young style wizards:
- Pseudo-classes are powerful state managers
- Consider accessibility in your states
- Test all possible state combinations
- Keep selectors specific but not too complex
- Maintain consistent state behavior
Dumbledore's Final Words
"Like a wizard's spell changing based on intention and circumstance, pseudo-classes allow our elements to respond dynamically to user interaction and context. Use them wisely!" 🧙