HTML & CSS

CSS Selectors & how to learn CSS

CSS confuses people for one reason: they try to memorize properties before they understand how to point at the right element. Fix that first. This page teaches every common selector with a live demo β€” and the mindset that finally makes CSS click.

New to CSS? Read the visual lesson →

Four CSS pages β€” which one do you need?

They each do a different job. Here's where you are, and where to go next.

Mindset

Why CSS feels confusing β€” and how to fix it

CSS isn't hard because there's too much to memorize. It's confusing because three invisible systems run at once: selecting (what gets styled), the cascade (which rule wins), and layout (how boxes flow). Learn them as separate ideas and the fog lifts.

1. Select

Every rule starts by pointing at elements. Master selectors and you control exactly what changes.

2. The cascade

When two rules fight, specificity decides the winner. This is the #1 source of "why isn't my CSS working?!"

3. The box model

Every element is a box with content, padding, border, margin. Layout is just arranging boxes.

4. Layout flow

Normal flow β†’ then Flexbox & Grid. Don't jump to layout before the three ideas above click.

The best way to actually learn it

  1. One concept at a time. Don't open 20 properties at once. Learn selectors fully, then the box model, then color/typography, then layout.
  2. Type it, don't copy it. Re-typing code builds muscle memory that copy-paste never will.
  3. Use DevTools constantly. Right-click any element β†’ Inspect. Edit CSS live and watch it change β€” this is the fastest feedback loop in web dev.
  4. Steal from real sites. Inspect a site you like and read how its rules are built. CSS is more readable than you think.
  5. Build β†’ break β†’ fix. Change a value, see what happens, undo it. Breaking things on purpose teaches cause and effect.
  6. Don't memorize β€” reference. Even pros look things up. Keep a cheat sheet (like the one below) and repetition will do the memorizing for you.
The mental model: every line of CSS is "select something, then style it." selector { property: value; }. If your style isn't applying, 9 times out of 10 your selector is wrong or another rule is more specific β€” not the property.
Why it matters

What good selectors unlock

Selectors aren't trivia β€” they're leverage. The right one lets you style hundreds of elements at once, react to the user, and decorate a page without touching the HTML. Here's where they pay off on real projects every day:

Style 100 things at once

Give buttons one class and style them in a single place. Change the rule β†’ every button updates.

.btn { ... }

Readable tables & lists

Zebra-stripe rows automatically β€” no classes on each row, and it stays right when rows are added.

tr:nth-child(even) { ... }

Interactivity, no JavaScript

Hover effects, focus rings, and open/closed states β€” all from a pseudo-class.

.card:hover { ... }

Style by purpose

Target the email field, the required inputs, or the disabled buttons β€” by what they are.

input[type="email"] { ... }

Decorate without clutter

Add icons, quote marks, or counters straight from CSS instead of pasting them into every element.

.badge::before { content: "β˜…"; }

Theme everything at once

Flip one class on a parent (like .dark) and restyle everything inside it.

.dark .title { color: #fff; }

The pattern behind all six: write the rule once, point it at the right elements with a selector, and it applies everywhere β€” now and to anything you add later. That's why selectors are the highest-leverage thing to learn first.
Foundation

The anatomy of a rule

Before the selectors, lock in the shape of every CSS rule. Everything else is a variation on this.

.button {              /* selector β€” WHAT to style */
  color: white;       /* declaration: property + value */
  background: #8b5cf6;
  padding: 10px 18px;
}                       /* the { } is the "declaration block" */
Selectors 1

Basic selectors

The four you'll use every single day β€” plus how to group them. These point at elements by their tag, a class you add, or a unique id.

Live β€” same markup, three selectors

A plain paragraph β€” styled by its type (p).

A paragraph with class="d-highlight" β€” styled by class.

A paragraph with id="dSpecial" β€” styled by id.

/* Type β€” every <p> on the page */
p { color: #cbd5e1; }

/* Class β€” any element with class="d-highlight" (reusable) */
.d-highlight { border-left: 3px solid #8b5cf6; }

/* ID β€” the ONE element with id="dSpecial" (unique per page) */
#dSpecial { border-left: 3px solid #ec4899; }

/* Universal β€” literally everything */
* { box-sizing: border-box; }

/* Grouping β€” apply the same rule to several selectors */
h1, h2, h3 { font-family: sans-serif; }
Class vs ID: use classes for almost everything β€” they're reusable. Reserve id for one-off anchors or JS hooks. IDs are also very specific, which causes the override headaches you'll see below.
Selectors 2

Combinators β€” selecting by relationship

These target elements based on where they sit relative to others. The highlighted chips below show exactly what each one grabs.

Live β€” purple = matched by .box > span
.box (parent)
direct child span direct child span
nested span (NOT a direct child of outer .box)
/* Descendant (space) β€” ANY .box span, nested at any depth */
.box span { color: tomato; }

/* Child (>) β€” only DIRECT children */
.box > span { font-weight: bold; }

/* Adjacent sibling (+) β€” the element IMMEDIATELY after */
h2 + p { margin-top: 0; }

/* General sibling (~) β€” ALL following siblings */
h2 ~ p { color: gray; }
Selectors 3

Attribute selectors

Target elements by their HTML attributes and values β€” great for styling form fields and links without adding extra classes.

Live β€” inputs colored by their type, links decorated by href
secure link (gets a lock) guide.pdf (gets a doc icon)
/* Exact match */
input[type="email"]    { border-color: #8b5cf6; }
input[type="password"] { border-color: #ec4899; }

/* Starts with  ^=   /  Ends with  $=   /  Contains  *= */
a[href^="https"]::before { content: "πŸ”’ "; }
a[href$=".pdf"]::after   { content: " πŸ“„"; }
a[href*="example"]       { color: teal; }

/* Just "has the attribute at all" */
[disabled] { opacity: 0.5; }
Selectors 4

Pseudo-classes β€” state & position

A pseudo-class (one colon :) styles an element in a particular state (hovered, focused) or position (first, even, nth). Hover the button and tab into it to see the states fire.

Live β€” interact with these
  • First item β€” :first-child
  • Even row β€” :nth-child(even)
  • Odd row
  • Even row β€” :nth-child(even)
  • Last item β€” :last-child
/* State β€” react to the user */
button:hover  { background: #8b5cf6; }
button:focus  { outline: 3px solid rgba(139,92,246,.4); }
a:visited     { color: purple; }
input:checked { accent-color: green; }

/* Structural β€” react to position */
li:first-child      { font-weight: bold; }
li:last-child       { color: pink; }
li:nth-child(even)  { background: #22222b; }   /* zebra stripes */

/* :not() β€” everything EXCEPT the match */
li:not(.active) { opacity: 0.7; }
Selectors 5

Pseudo-elements β€” style part of an element

A pseudo-element (two colons ::) styles a piece of an element, or injects decorative content that isn't in your HTML. ::before and ::after are the workhorses.

Live β€” content & styling added by CSS
Featured
A blockquote with a big quote mark added by ::before β€” no extra HTML.

The first letter of this paragraph is enlarged into a drop cap using ::first-letter, a classic magazine effect that takes one line of CSS and zero changes to the markup itself.

/* Inject content before/after β€” needs the content property */
.badge::before { content: "β˜…"; color: #fbbf24; }
.card::after  { content: ""; display: block; clear: both; }

/* Style a PART of the text */
p::first-letter { font-size: 2.6rem; font-weight: 800; color: #ec4899; float: left; }
p::first-line   { font-weight: bold; }
::selection      { background: #8b5cf6; }   /* highlighted text */
::placeholder    { color: #71717a; }
The big one

Specificity β€” why your CSS "isn't working"

When two rules target the same element, the more specific one wins β€” not necessarily the last one. Read specificity as three columns: IDs, classes, types. Higher column wins, left to right.

Reading the weight of a selector
p
001
.note
010
nav .note p
012
#header .note
110
ID = 1,0,0 class / attr / pseudo-class = 0,1,0 type / pseudo-element = 0,0,1
The rule of thumb: an #id beats any number of .classes, which beat any number of types. So #header .note (1,1,0) beats nav .note p (0,1,2). When stuck, stop adding !important β€” lower your specificity instead. Keep selectors flat and class-based and these fights mostly disappear.
Cheat Sheet

Every common selector at a glance

Bookmark this. You don't memorize selectors β€” you look them up until they stick.

SelectorMatches
Basic
*Every element
pAll <p> elements (by tag/type)
.classElements with that class
#idThe one element with that id
a, b, cGrouping β€” each of a, b, and c
Combinators
A BB inside A (descendant, any depth)
A > BB that is a direct child of A
A + BB immediately after A (adjacent sibling)
A ~ BAll B siblings after A
Attribute
[attr]Has the attribute
[attr="x"]Attribute equals x
[attr^="x"]Starts with x
[attr$="x"]Ends with x
[attr*="x"]Contains x
Pseudo-classes (state)
:hoverPointer is over it
:focusElement has keyboard focus
:activeBeing clicked
:checkedChecked checkbox / radio
:disabledDisabled form control
Pseudo-classes (structural)
:first-childFirst child of its parent
:last-childLast child of its parent
:nth-child(n)The nth child (e.g. even, 3, 2n+1)
:not(x)Everything except x
Pseudo-elements
::beforeInjected content before the element
::afterInjected content after the element
::first-letterFirst letter (drop caps)
::first-lineFirst line of text
::selectionText the user highlighted
::placeholderPlaceholder text in an input
Practice

Use CSS to its full capacity

Knowing the selectors is half of it. These habits separate people who fight CSS from people who steer it.

Do

  • Style with classes β€” reusable and low-specificity
  • Keep selectors flat (1–2 levels), not div ul li a span
  • Use DevTools to inspect and edit live
  • Let :hover/:focus handle states instead of JS
  • Group shared rules; reach for :nth-child & ::before before adding markup

Don't

  • Reach for !important β€” fix specificity instead
  • Over-use #id selectors for styling
  • Write deep, brittle selector chains
  • Add a class for something a pseudo-class already does
  • Memorize blindly β€” reference, repeat, and it sticks
"OK, I can target it β€” but which property do I use?" That's the other half of CSS. Head to CSS Recipes β€” How Do I…? for goal-first answers (full-width hero, background color, fonts, centering…). Then High-Level CSS for the cascade, Detailed CSS for every property, and Layout once this clicks.