CSS Selectors Cheat Sheet With a Live Tester

Selectors tell CSS which elements a rule applies to. The core ones are type (p), class (.card), ID (#main), attribute ([type=email]), combinators (descendant space, child >, sibling + and ~), pseudo-classes such as :hover and :nth-child(2n), and pseudo-elements such as ::before. Type a selector below to see what it matches.

The selectors behind most stylesheets
SelectorMatchesSpecificity
pEvery p element0,0,1
.cardElements with class card0,1,0
#mainThe element with id main1,0,0
nav aa anywhere inside nav0,0,2
ul > lili that are direct children of ul0,0,2
a:hoverA link under the pointer0,1,1

Updated

Reading a selector table only goes so far. The tester below runs your selector through the browser's own engine against a small sample page, highlights every element it matches, explains invalid input and works out the specificity. The calculator underneath answers the eternal question of which items 3n+1 actually picks.

Basic and attribute selectors

Simple selectors
SelectorExampleMatches
**Every element
TypebuttonAll button elements
Class.priceclass contains price
ID#checkoutid is checkout
Attribute present[disabled]Has the attribute at all
Exact value[type="email"]Value is exactly email
Starts with[href^="https"]Value starts with https
Ends with[href$=".pdf"]Value ends with .pdf
Contains[class*="btn"]Value contains btn anywhere
Word in list[rel~="noopener"]Space-separated list includes the word
Language prefix[lang|="en"]en or en- followed by anything
Case-insensitive[href$=".PDF" i]Matches .pdf, .PDF and so on

Combinators

Relationships between elements
CombinatorExampleMatches
Descendant (space)nav aa at any depth inside nav
Child >ul > lili directly inside ul
Next sibling +h2 + pThe p immediately after an h2
Later siblings ~h2 ~ pEvery p after an h2 with the same parent
Selector list ,h1, h2Either one

Pseudo-classes

State, structure and logic
Pseudo-classMatches
:hoverUnder a mouse pointer
:focusHas keyboard or programmatic focus
:focus-visibleFocused and the browser judges a ring should show
:focus-withinItself or a descendant has focus
:activeBeing pressed
:visitedA link already visited (limited styling for privacy)
:checked / :disabled / :requiredForm controls in that state
:valid / :invalid / :user-invalidForm validation states; user-invalid waits for interaction
:placeholder-shownAn input currently showing its placeholder
:first-child / :last-child / :only-childPosition among siblings
:nth-child(An+B) / :nth-last-child()Position by formula, counting all siblings
:nth-of-type() / :first-of-typePosition counting only the same tag
:emptyNo children and no text
:not(x)Does not match x
:is(x, y) / :where(x, y)Matches any in the list; where has zero specificity
:has(x)Contains, or is followed by, something matching x
:rootThe html element
:targetThe element whose id is in the URL hash

Pseudo-elements and ::before / ::after recipes

Parts of an element
Pseudo-elementTargets
::before / ::afterGenerated content before or after the element's content
::first-letter / ::first-lineFirst letter or first rendered line of a block
::placeholderPlaceholder text in an input
::selectionText the user has highlighted
::markerThe bullet or number of a list item
::file-selector-buttonThe button of a file input
::backdropThe layer behind a modal dialog or fullscreen element

Required star, external link icon and a quote mark

Each decoration lives in CSS, so the HTML stays clean. content is required for both pseudo-elements.

<style>
  label.req::after { content: " *"; color: #dc2626 }
  a[href^="http"]::after { content: " \2197"; font-size: .8em }
  blockquote { position: relative; margin: 12px 0 0; padding-left: 28px; font-style: italic }
  blockquote::before { content: "\201C"; position: absolute; left: 0; top: -10px; font-size: 44px; color: #4F98FB }
</style>
<label class="req">Email</label>
<p><a href="https://example.com">Supplier price list</a></p>
<blockquote>The best bread in the neighbourhood.</blockquote>

:hover, :focus-visible and :has() together

Tab into the field to see the ring. The card turns red only while it holds an invalid email.

<style>
  .card { padding: 12px; border: 2px solid #e5e7eb; border-radius: 10px }
  .card:has(input:invalid:not(:placeholder-shown)) { border-color: #dc2626 }
  button:hover { background: #1e3a8a }
  button, input { padding: 8px; margin-top: 6px }
  button { background: #4F98FB; color: #fff; border: 0; border-radius: 8px }
  :focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px }
</style>
<div class="card"><label>Email <input type="email" placeholder="[email protected]"></label><br><button>Send</button></div>

How the browser reads a selector

Browsers match selectors from right to left. For nav ul li a the engine starts with every link on the page, then walks up to check for an li, a ul and a nav. This is why the rightmost part, called the key selector, matters most for speed, although on normal pages selector speed is rarely a real problem.

A comma makes a selector list, which is just several selectors sharing one rule. If any one of them is invalid, the whole rule is dropped, except inside :is() and :where(), which forgive invalid entries.

Pseudo-classes versus pseudo-elements

A pseudo-class, written with one colon, selects an existing element in a particular state or position: :hover, :checked, :first-child, :empty. A pseudo-element, written with two colons, creates or targets a part that is not its own element in the HTML: ::before, ::placeholder, ::selection, ::marker.

Only one pseudo-element is allowed per compound selector, and it must come last. Content added through ::before and ::after is usually read aloud by screen readers but cannot be selected or copied, so do not put essential information there.

Keeping specificity under control

Most specificity fights start when an ID or a long chain of nested selectors wins against a later, simpler rule. Flat class names, one or two levels deep at most, keep every rule easy to override.

Cascade layers (@layer) give another tool: rules in a later layer beat rules in an earlier one regardless of specificity. Resets and third-party styles can live in a low layer so your own classes always win.

How it works

  1. Type a selector into the tester, or click one of the examples to load it.
  2. Matching elements in the sample page light up, and the count updates as you type.
  3. Read the specificity score as three numbers: IDs, then classes and similar, then element types.
  4. Use the nth-child calculator to test any An+B formula against a list of up to 30 items.

Tips

Frequently asked questions

What is the difference between nth-child and nth-of-type?

:nth-child(2) counts every sibling element, then checks that the second one also matches the rest of the selector. :nth-of-type(2) counts only siblings of the same tag. So p:nth-child(2) matches nothing if the second child is an h2, while p:nth-of-type(2) still finds the second paragraph.

What is the difference between ::before and :before?

They are the same pseudo-element. The double colon was introduced to tell pseudo-elements apart from pseudo-classes, and browsers still accept the single colon for the four older ones (before, after, first-line, first-letter) for backward compatibility. Write the double colon in new code.

What does :has() do?

:has() matches an element based on what it contains or what follows it, for example .card:has(img) styles cards that hold an image, and label:has(+ input:invalid) styles a label before an invalid field. It is supported in all current major browsers, with Firefox the last to add it at the end of 2023.

How is specificity calculated?

Count three things: ID selectors; classes, attribute selectors and pseudo-classes; then type selectors and pseudo-elements. Compare from the left, so one ID beats any number of classes. :where() adds nothing, while :is(), :not() and :has() take the score of their most specific argument. Inline styles and !important sit above all of these.

Why does my :hover style not show on phones?

Touch screens have no hover state, so browsers either skip it or apply it briefly after a tap. Wrap hover-only styling in @media (hover: hover) and make sure anything revealed on hover is also reachable by tap and keyboard.

Related: CSS specificity calculator, CSS box model, CSS cursor values, HTML and CSS cheat sheet, Modern CSS features.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap