CSS Pseudo-classes and Pseudo-elements
Pseudo-classes select an element in a particular state. Pseudo-elements select a part of an element or create a generated piece of content. They let you add useful detail without extra HTML.
Pseudo-classes
A pseudo-class starts with one colon. Common examples include :hover, :focus, :active, and :disabled.
.button:hover { background-color: #1d4ed8;}
.button:focus-visible { outline: 3px solid #fbbf24; outline-offset: 3px;}
input:disabled { cursor: not-allowed; opacity: 0.6;}Use :focus-visible for keyboard focus styling. It avoids showing a focus ring after most pointer clicks while preserving a clear indicator for keyboard users.
Structural pseudo-classes
Structural pseudo-classes select elements based on their position among siblings.
.menu li:first-child { border-top: 1px solid #cbd5e1;}
.menu li:nth-child(odd) { background-color: #f8fafc;}
.card:not(.featured) { box-shadow: none;}Prefer a meaningful class when the styling represents an important concept, such as .featured. Use structural selectors for genuinely repeated patterns, such as alternating table rows.
Pseudo-elements
Pseudo-elements conventionally use two colons. ::before and ::after create generated content, while ::first-letter and ::selection style a specific part of text.
.external-link::after { content: " ↗"; font-size: 0.85em;}
.article p::first-letter { color: #2563eb; font-size: 2.5em; font-weight: 700;}
::selection { color: #ffffff; background-color: #2563eb;}::before and ::after need a content value to appear. Empty content is valid when the pseudo-element is used as a decorative shape.
.tag { position: relative;}
.tag::before { position: absolute; inset: 0; z-index: -1; content: ""; border-radius: inherit; background: #dbeafe;}Combining states
Pseudo-classes can be combined to describe a precise state.
a:hover:not(.button) { text-decoration-thickness: 2px;}
input:invalid:focus { border-color: #dc2626;}Keep interactive states consistent: hover should add feedback, focus should be visible, and disabled controls should not appear actionable.