What CSS :first-child is

:first-child is a CSS pseudo-class that matches an element only if it is the very first child of its parent.

Read that again: first child of its parent. Not “the first one of a kind”, not “the first one you see”, not “the first one with a class”. It’s purely about the DOM tree order.

.item:first-child {
  background: #ffe08a;
}
  
.item:first-child {
  background: #8ad9ff;
}
  
.item:first-child {
  background: #b9ffa6;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  font-family: ui-monospace, SFMono-Regular, monospace;
  padding: 16px;
}

.row {
  display: flex;
  gap: 10px;
}

.item {
  border: 2px solid #111;
  padding: 10px 12px;
  border-radius: 10px;
  background: #f2f2f2;
}
  
Item A
Item B
Item C

In that example, the first .item is also the first child of .row, so it matches .item:first-child.

:first-child in plain English

  • Parent-focused: the selector checks the element’s position among its siblings under the same parent.
  • All children count: different tags, different classes, every element counts as a child.
  • It either matches or it doesn’t: there’s no “kind of first”.
.card:first-child {
  outline: 4px solid #111;
}
  
.card:first-child {
  transform: translateY(-6px);
}
  
.card:first-child {
  box-shadow: 0px 12px 0px 0px rgba(0, 0, 0, 0.25);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.grid {
  padding: 16px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  font-family: system-ui, ui-sans-serif, sans-serif;
}

.card {
  border: 2px solid #111;
  border-radius: 14px;
  padding: 12px;
  background: #f7f7f7;
}

.card p {
  margin: 0;
}
  

Card 1 (I am the first child)

Card 2

Card 3

Card 4

Card 5

Card 6

:first-child vs :first-of-type

This is the #1 confusion. The selectors look similar, but they answer different questions:

  • :first-child means: “Am I the first child of my parent?”
  • :first-of-type means: “Am I the first of my tag name among siblings?”

Example: why :first-child “fails”

In the next playground, the first child of the parent is an h3, not a p. So p:first-child matches nothing, even though the first paragraph “feels” first.

p:first-child {
  background: #ffe08a;
}
  
p:first-of-type {
  background: #ffe08a;
}
  
p:first-of-type {
  background: #ffe08a;
}

h3:first-child {
background: #b9ffa6;
} 
*,
::before,
::after {
  box-sizing: border-box;
}

.box {
  border: 2px solid #111;
  border-radius: 14px;
  padding: 16px;
  margin: 16px;
  font-family: system-ui, ui-sans-serif, sans-serif;
  background: #f7f7f7;
}

.box > * {
  border: 1px dashed rgba(0, 0, 0, 0.35);
  padding: 8px 10px;
  border-radius: 10px;
  margin: 10px 0;
}

.box h3 {
  margin: 0;
}

.box p {
  margin: 0;
}
  

Heading

Paragraph A

Paragraph B

If you want “the first paragraph”, p:first-of-type is usually what you mean.

Learn more in the CSS Nth Of Type Interactive Tutorial.

The parent-child relationship

:first-child is all about who your parent is. If your HTML structure changes (wrappers, layout divs, headings inserted by a CMS), the match can change.

Use the direct child selector to make intent clear

Adding > is not required, but it often makes your selector clearer and safer:

  • .list > li:first-child means: “the first li that is a direct child of .list
  • .list li:first-child means: “any li that is the first child of its parent, anywhere inside .list
.list li:first-child {
  background: #ffe08a;
}
  
.list > li:first-child {
  background: #ffe08a;
}
  
.list > li:first-child {
  background: #ffe08a;
}

.list .sub li:first-child {
background: #b9ffa6;
} 
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 16px;
  font-family: system-ui, ui-sans-serif, sans-serif;
}

.list,
.sub {
  border: 2px solid #111;
  border-radius: 14px;
  padding: 12px 12px 12px 28px;
  background: #f7f7f7;
  margin: 0;
}

.list {
  margin-bottom: 14px;
}

li {
  padding: 6px 10px;
  border-radius: 10px;
}

.sub {
  margin-top: 10px;
}
  
  • Top item 1
  • Top item 2 (has a nested list)
    • Nested item A
    • Nested item B
  • Top item 3

Notice how .list li:first-child can light up nested list items too. That might be what you want, or it might be a surprise party you did not RSVP to.

Learn more in the CSS Direct Child Selector (>) Interactive Tutorial.

Common real-world gotchas

Gotcha: wrapper elements

Many layouts add wrappers (grid items, card wrappers, CMS blocks). Suddenly the element you want is no longer a “child” of the parent you thought.

.card:first-child {
  background: #ffe08a;
}
  
.cards > .card-wrap:first-child .card {
  background: #ffe08a;
}
  
.cards > .card-wrap:first-child .card {
  background: #ffe08a;
}

.cards > .card-wrap:first-child {
outline: 4px solid #111;
} 
*,
::before,
::after {
  box-sizing: border-box;
}

.cards {
  padding: 16px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  font-family: system-ui, ui-sans-serif, sans-serif;
}

.card-wrap {
  border: 2px dashed rgba(0, 0, 0, 0.35);
  border-radius: 14px;
  padding: 10px;
  background: #fff;
}

.card {
  border: 2px solid #111;
  border-radius: 12px;
  padding: 12px;
  background: #f7f7f7;
}

.card p {
  margin: 0;
}
  

Card 1 (inside a wrapper)

Card 2

Card 3

The “first child” might be the wrapper, not the card. In that case, you target the wrapper first, then style what’s inside it.

Gotcha: mixed siblings

If your parent starts with a different element (like a heading, an image, or a “featured” banner), your selector may stop matching.

.article p:first-child {
  background: #ffe08a;
}
  
.article p:first-of-type {
  background: #ffe08a;
}
  
.article > *:first-child {
  outline: 4px solid #111;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.article {
  margin: 16px;
  padding: 16px;
  border: 2px solid #111;
  border-radius: 14px;
  background: #f7f7f7;
  font-family: system-ui, ui-sans-serif, sans-serif;
}

.article > * {
  margin: 10px 0;
  padding: 10px;
  border-radius: 10px;
  border: 1px dashed rgba(0, 0, 0, 0.35);
}

.article h3 {
  margin: 0;
}

.article p {
  margin: 0;
}
  

Title injected by the CMS

This paragraph looks “first”, but it is not the first child.

Another paragraph.

Practical patterns you’ll actually use

Remove top margin from the first item

A super common pattern: you want spacing between items, but you don’t want the first one to push the whole group down.

.stack > * {
  margin-top: 16px;
}

.stack > *:first-child {
margin-top: 0px;
} 
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 16px;
  font-family: system-ui, ui-sans-serif, sans-serif;
}

.stack {
  border: 2px solid #111;
  border-radius: 14px;
  padding: 16px;
  background: #f7f7f7;
}

.block {
  border: 2px solid #111;
  border-radius: 12px;
  padding: 12px;
  background: #fff;
}
  
First block (no top margin)
Second block
Third block

This is often nicer than adding “special” classes like .is-first in your HTML.

Style the first list item like a header

Sometimes you have a list and the first item is the “title” or “featured” entry.

.menu > li:first-child {
  font-weight: 700;
}
  
.menu > li:first-child {
  font-weight: 700;
  background: #ffe08a;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 16px;
  font-family: system-ui, ui-sans-serif, sans-serif;
}

.menu {
  list-style: none;
  padding: 0;
  margin: 0;
  border: 2px solid #111;
  border-radius: 14px;
  overflow: hidden;
}

.menu li {
  padding: 12px 14px;
  border-top: 1px solid rgba(0, 0, 0, 0.2);
  background: #f7f7f7;
}

.menu li:first-child {
  border-top: none;
}

.menu a {
  color: inherit;
  text-decoration: none;
}
  

  

This is great for “featured post”, “top product”, “latest release”, etc.

.grid > .card:first-child {
  grid-column: span 2;
}
  
.grid > .card:first-child {
  grid-column: span 2;
  background: #ffe08a;
}
  
.grid > .card:first-child {
  grid-column: span 2;
  background: #ffe08a;
  transform: translateY(-6px);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.grid {
  padding: 16px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  font-family: system-ui, ui-sans-serif, sans-serif;
}

.card {
  border: 2px solid #111;
  border-radius: 14px;
  padding: 12px;
  background: #f7f7f7;
}

.card p {
  margin: 0;
}
  

Featured card (first child)

Card 2

Card 3

Card 4

Card 5

Card 6

Combining :first-child with other selectors

1) :first-child with a class filter

Important to note: .thing:first-child means “an element with class thing that is also first child”. It does not mean “the first element with class thing”.

.tag:first-child {
  background: #ffe08a;
}
  
.tag:first-of-type {
  background: #ffe08a;
}
  
.tag:first-child {
  background: #ffe08a;
}

.note {
outline: 4px solid #111;
} 
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 16px;
  font-family: system-ui, ui-sans-serif, sans-serif;
}

.row {
  border: 2px solid #111;
  border-radius: 14px;
  padding: 12px;
  background: #f7f7f7;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.row > div {
  border: 2px solid #111;
  padding: 8px 10px;
  border-radius: 999px;
  background: #fff;
}

.note {
  border-style: dashed;
}
  
I am first child, but not .tag
Tag A
Tag B
Tag C

If you want “the first .tag”, you might want .tag:first-of-type only if tags are the same element type.

If you really want “the first .tag” regardless of element type, you will need :nth-child(N of S). Learn more in the CSS Nth Child (N of Selector) Interactive Tutorial.

2) :first-child with :not()

You can exclude certain cases. For example, style the first child unless it’s a “disabled” item.

.list > li:first-child:not(.disabled) {
  background: #b9ffa6;
}
  
.list > li:first-child:not(.disabled) {
  background: #b9ffa6;
  font-weight: 700;
}
  
.list > li:first-child:not(.disabled) {
  background: #b9ffa6;
  font-weight: 700;
}

.list > li.disabled {
opacity: 0.5;
} 
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 16px;
  font-family: system-ui, ui-sans-serif, sans-serif;
}

.list {
  margin: 0;
  padding: 0;
  list-style: none;
  border: 2px solid #111;
  border-radius: 14px;
  overflow: hidden;
  background: #f7f7f7;
}

.list li {
  padding: 12px 14px;
  border-top: 1px solid rgba(0, 0, 0, 0.2);
}

.list li:first-child {
  border-top: none;
}
  
  • First item is disabled
  • Second item
  • Third item

In this case, nothing is highlighted because the first item is disabled, and the selector is specifically about the first child.

Learn more about the :not() pseudo-class in the CSS :not Interactive Tutorial.

Debugging when :first-child “doesn’t work”

When a :first-child selector doesn’t match, it’s almost always one of these:

  1. You’re not targeting the correct parent. Your element is inside a wrapper you forgot about.
  2. Something else is the actual first child (a heading, an image, a hidden element, etc.).
  3. Your selector is too broad and matches something unexpected (nested lists are a classic).

A mini checklist

  • Inspect the element in DevTools and look at its parent.
  • Look at the parent’s children in order. What is child number one?
  • Try temporarily styling :first-child on all children: .parent > *:first-child.
  • Add > to ensure you’re selecting direct children when you intend to.
.parent > *:first-child {
  outline: 4px solid #111;
}
  
.parent > *:first-child {
  outline: 4px solid #111;
}

.parent > * {
background: #f2f2f2;
} 
.parent > *:first-child {
  outline: 4px solid #111;
}

.parent > * {
  background: #f2f2f2;
}

.parent > *:first-child {
  background: #ffe08a;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.parent {
  margin: 16px;
  padding: 16px;
  border: 2px solid #111;
  border-radius: 14px;
  background: #fff;
  font-family: system-ui, ui-sans-serif, sans-serif;
}

.parent > * {
  border: 2px solid #111;
  border-radius: 12px;
  padding: 12px;
  margin: 10px 0;
}
  
Child 1

Child 2 (a paragraph)

Child 3

That “debug selector” (.parent > *:first-child) is one of the fastest ways to see what’s actually first.

Quick reference

  • .box:first-child selects .box only if it is the first child of its parent.
  • .parent > :first-child selects the parent’s first direct child, whatever it is.
  • p:first-child selects a p only if the parent’s first child is a p.
  • p:first-of-type selects the first p among siblings, even if it’s not the first child.

Wrap-up

:first-child is simple, but it’s very literal. It doesn’t guess what you meant. It looks at the parent, looks at child number one, and says: “Is this you?” If yes, you get the style. If not, better luck next selector.

Once you start thinking in parent-child relationships (and you make peace with wrappers), :first-child becomes one of those small CSS tools you’ll reach for constantly.

Learn even more about :nth-child in the CSS Nth Child Interactive Tutorial.