HTML and CSS project tutorial covering web development fundamentals. Learn to build interactive and responsive web pages.

CSS Pseudo-Classes and Pseudo-Elements Guide

📖 5 min read 🏷️ #pseudo
CSS pseudo-classes and pseudo-elements are powerful selectors that target elements based on their state, position, or specific parts of the document tree. They let you style elements without adding extra HTML classes or IDs. Mastering them unlocks cleaner, more maintainable stylesheets.
1

Understanding Pseudo-Classes vs Pseudo-Elements

Pseudo-classes use a single colon : and target elements based on state or position: :hover, :first-child, :nth-child(). They describe a special state of an existing element. Pseudo-elements use double colons :: and target specific parts of an element: ::before, ::after, ::first-line. They create virtual elements that don't exist in the HTML.

This distinction matters for standards compliance, though browsers accept single-colon for pseudo-elements too. Always use double-colon for pseudo-elements (::before) and single-colon for pseudo-classes (:hover).

2

User Action Pseudo-Classes

:hover triggers when the mouse is over an element. Use it for interactive feedback on buttons, links, and cards. :focus targets elements that receive keyboard or mouse focus — essential for accessibility. :active activates during the click moment (between mousedown and mouseup).

:visited styles links the user has already clicked. Privacy restrictions limit which properties you can style with :visited (only color-related properties). :focus-visible shows focus indicators only for keyboard navigation, not mouse clicks — great for accessible designs without ugly click outlines.

3

Structural Pseudo-Classes

:first-child and :last-child target the first and last element among siblings. :nth-child(n) targets elements by position — :nth-child(odd) for odd rows, :nth-child(3n+1) for every third starting at the first.

:first-of-type and :last-of-type work like child selectors but only consider elements of the same type. :nth-of-type() is the positional equivalent for specific element types. :only-child matches elements that are the sole child of their parent. These selectors eliminate the need for extra CSS classes in lists, grids, and tables.

4

The Power of ::before and ::after

The ::before and ::after pseudo-elements create content before or after an element's actual content. They require the content property (can be empty string ""). Use them for decorative elements, icons, tooltips, and visual enhancements without extra HTML.

Common uses: adding quotation marks around blockquotes (content: "\201C"), creating custom bullets for lists, generating tooltip arrows with CSS borders, and adding overlay effects with absolute positioning. The pseudo-elements are rendered inside the element's box, so they inherit its positioning context.

5

Form and Input Pseudo-Classes

:required and :optional style mandatory vs optional form fields. :valid and :invalid style fields based on their validation state — show green borders on valid input, red on invalid. :disabled and :enabled target fields based on their interactive state.

:checked styles selected radio buttons and checkboxes. :placeholder-shown targets inputs currently showing placeholder text. :in-range and :out-of-range work with number inputs that have min/max attributes. These selectors eliminate JavaScript validation UI code and make forms more user-friendly.

6

Negation and Other Useful Pseudo-Classes

:not() excludes elements matching a selector: .menu li:not(:last-child) adds a divider between all items except the last. You can pass any selector inside :not(). :is() groups multiple selectors into one for readability: :is(h1, h2, h3) { margin-top: 1.5em; }.

:where() works like :is() but has zero specificity — useful for base styles that should be easily overridable. :has() the parent selector, targets elements containing specific children: .card:has(img) styles cards that contain images.

7

Practical Example: Styled Navigation

nav a { position: relative; padding: 8px 16px; color: #6b7280; text-decoration: none; }
nav a:hover { color: #4f46e5; }
nav a::after { content: ''; position: absolute; bottom: 0; left: 0; width: 0; height: 2px; background: #4f46e5; transition: width 0.3s ease; }
nav a:hover::after { width: 100%; }
nav a:not(:last-child)::after { margin-right: 4px; }

This creates an underline animation on hover for each nav link, uses :not(:last-child) to avoid the separator on the last item, and combines pseudo-classes with pseudo-elements for a polished result without any HTML changes.

← Back to All Tutorials