In a perfect world, CSS might have a magical :nth-of-class() selector that lets you target “the 2nd
element with class .class”.
In the real world, CSS does not have a direct :nth-of-class pseudo-class.
But don’t close the tab yet — CSS Selectors Level 4 quietly gave us the next best thing:
the selector list argument for :nth-child() and :nth-last-child().
This is the equivalent of “nth-of-class”, except it works with any selector list, not just a
class.
It looks like this:
-
:nth-child(N of .class)— “the Nth matching child from the start” -
:nth-last-child(N of .class)— “the Nth matching child from the end”
Browser support is around 92.2% at the time of writing. You can check the latest support data here: caniuse.com/css-nth-child-of.
In this tutorial, we’ll use :nth-child(N of .class) and :nth-last-child(N of .class) to
build “nth-of-class” behavior,
step by step, with interactive playgrounds.
What Is :nth-child(… of S)?
The classic :nth-child() counts all siblings (all children of the same parent) and selects the
one(s) that match a pattern.
The upgraded version adds an optional of S clause (where S is a selector list).
That clause tells the browser:
“Only count the children that match this selector list.”
Example:
:nth-child(1 of .class) means “the first child among the children that have class
.class”.
Any non-.class siblings are ignored for the counting step.
.row > :nth-child(1 of .tag) {
outline: 4px solid #111;
}
.row > .tag:nth-child(1) {
outline: 4px solid #111;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 14px;
border: 3px solid #111;
border-radius: 16px;
background: #f6f6f6;
}
.pill {
padding: 8px 10px;
border-radius: 999px;
border: 2px solid #111;
background: #fff;
font-size: 14px;
}
.tag {
background: #eaf4ff;
}
.note {
background: #fff3d6;
}
.hint {
margin-top: 10px;
font-size: 14px;
line-height: 1.4;
}
.note (not .tag) .tag #1 .note (not .tag) .tag #2 .tag #3 .note (not .tag)Snippet 1 uses
:nth-child(1 of .tag)→ outlines the first.tag.Snippet 2 uses
.tag:nth-child(1)→ only matches if the.tagis literally the first child.
CSS “First Of Class” With :nth-child(1 of .class)
The most common “nth-of-class” request is:
“Select the first element with class .item among its siblings.”
That’s exactly:
:nth-child(1 of .item)
.list > :nth-child(1 of .item) {
background: #111;
color: #fff;
transform: translateY(-2px);
}
.list > :nth-child(2 of .item) {
background: #111;
color: #fff;
transform: translateY(-2px);
}
.list > :nth-child(3 of .item) {
background: #111;
color: #fff;
transform: translateY(-2px);
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.list {
display: grid;
gap: 10px;
border: 3px solid #111;
border-radius: 16px;
padding: 14px;
background: #f6f6f6;
}
.card {
padding: 12px 14px;
border-radius: 14px;
border: 2px solid #111;
background: #fff;
transition: transform 200ms ease;
}
.ad {
border-style: dashed;
background: #fff3d6;
}
.kicker {
font-size: 13px;
opacity: 0.8;
}
Not an item.adItem.item #1Not an item.adItem.item #2Item.item #3
CSS “Last Of Class” With :nth-last-child(1 of .class)
To select the last matching class among siblings, use :nth-last-child() with the same of
clause.
:nth-last-child(1 of .item)→ last.item:nth-last-child(2 of .item)→ second-to-last.item
.list > :nth-last-child(1 of .item) {
background: #111;
color: #fff;
}
.list > :nth-last-child(2 of .item) {
background: #111;
color: #fff;
}
.list > :nth-last-child(3 of .item) {
background: #111;
color: #fff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.list {
display: grid;
gap: 10px;
border: 3px solid #111;
border-radius: 16px;
padding: 14px;
background: #f6f6f6;
}
.card {
padding: 12px 14px;
border-radius: 14px;
border: 2px solid #111;
background: #fff;
}
.ad {
border-style: dashed;
background: #fff3d6;
}
.kicker {
font-size: 13px;
opacity: 0.8;
}
Item.item #1Not an item.adItem.item #2Item.item #3Not an item.adItem.item #4
Odd And Even “Of Class”
You can use odd and even as the N part.
The key is that the odd/even counting happens on the filtered set (the elements matching the selector
list).
.grid > :nth-child(odd of .tile) {
background: #111;
color: #fff;
}
.grid > :nth-child(even of .tile) {
background: #111;
color: #fff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 10px;
border: 3px solid #111;
border-radius: 16px;
padding: 14px;
background: #f6f6f6;
}
.tile {
padding: 16px 12px;
border-radius: 14px;
border: 2px solid #111;
background: #fff;
text-align: center;
}
.gap {
padding: 16px 12px;
border-radius: 14px;
border: 2px dashed #111;
background: #fff3d6;
text-align: center;
font-size: 14px;
}
.tile #1not .tile.tile #2.tile #3not .tile.tile #4.tile #5.tile #6not .tile
The Real Power: An+B “Of Class”
When you write An+B, you’re describing a repeating pattern:
- A is the step size
- B is the starting offset
- n is 0, 1, 2, 3…
Example:
3n→ 3, 6, 9, 12…3n+1→ 1, 4, 7, 10…4n-1→ 3, 7, 11, 15…
With the of .class clause, those sequences apply to “the filtered set”.
.row > :nth-child(3n of .badge) {
background: #111;
color: #fff;
}
.row > :nth-child(3n + 1 of .badge) {
background: #111;
color: #fff;
}
.row > :nth-child(4n - 1 of .badge) {
background: #111;
color: #fff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 14px;
border: 3px solid #111;
border-radius: 16px;
background: #f6f6f6;
}
.badge,
.spacer {
padding: 8px 10px;
border-radius: 999px;
border: 2px solid #111;
background: #fff;
font-size: 14px;
}
.badge {
background: #eaf4ff;
}
.spacer {
background: #fff3d6;
border-style: dashed;
}
.badge #1 not .badge .badge #2 .badge #3 not .badge .badge #4 .badge #5 .badge #6 not .badge .badge #7
Ranges “Of Class”
Ranges are where this feature becomes ridiculously useful.
You can select “the first 3 of class”, “2 through 5 of class”, and more, using the same math you’d use with regular
:nth-child().
Selecting The First N Of A Class
-n + 3 means “1, 2, 3”.
So :nth-child(-n + 3 of .item) selects the first three matching .item elements.
:nth-child(n + 4 of .item) selects all .item elements starting from the fourth one.
.stack > :nth-child(-n + 3 of .item) {
background: #111;
color: #fff;
}
.stack > :nth-child(n + 4 of .item) {
background: #111;
color: #fff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.stack {
display: grid;
gap: 10px;
border: 3px solid #111;
border-radius: 16px;
padding: 14px;
background: #f6f6f6;
}
.item,
.other {
padding: 12px 14px;
border-radius: 14px;
border: 2px solid #111;
background: #fff;
}
.other {
border-style: dashed;
background: #fff3d6;
}
.kicker {
font-size: 13px;
opacity: 0.8;
}
Not counted
.otherCounted
.item #1Counted
.item #2Not counted
.otherCounted
.item #3Counted
.item #4Counted
.item #5
Selecting A Middle Range Of A Class
Want “items 2 through 4” (within the filtered set)? Combine two conditions:
n + 2→ 2, 3, 4, 5…-n + 4→ 1, 2, 3, 4
Put together:
:nth-child(n + 2 of .item):nth-child(-n + 4 of .item)
.stack > :nth-child(n + 2 of .item):nth-child(-n + 4 of .item) {
background: #111;
color: #fff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.stack {
display: grid;
gap: 10px;
border: 3px solid #111;
border-radius: 16px;
padding: 14px;
background: #f6f6f6;
}
.item,
.other {
padding: 12px 14px;
border-radius: 14px;
border: 2px solid #111;
background: #fff;
}
.other {
border-style: dashed;
background: #fff3d6;
}
.kicker {
font-size: 13px;
opacity: 0.8;
}
Counted
.item #1Not counted
.otherCounted
.item #2Counted
.item #3Counted
.item #4Not counted
.otherCounted
.item #5
Not Just A Class: Selector Lists
The of S part accepts a selector list — meaning you can use commas, combine classes, or
even use more complex selectors.
This is why it’s “nth-of-anything”, not just “nth-of-class”.
Example: Count .chip And .tag Together
.row > :nth-child(2 of .chip, .tag) {
background: #111;
color: #fff;
}
.row > :nth-child(3 of .chip, .tag) {
background: #111;
color: #fff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 14px;
border: 3px solid #111;
border-radius: 16px;
background: #f6f6f6;
}
.chip,
.tag,
.note {
padding: 8px 10px;
border-radius: 999px;
border: 2px solid #111;
background: #fff;
font-size: 14px;
}
.chip {
background: #eaf4ff;
}
.tag {
background: #e7ffe8;
}
.note {
background: #fff3d6;
border-style: dashed;
}
.note (ignored) .chip #1 .tag #2 .note (ignored) .chip #3 .tag #4
Example: “Of .item.featured”
You can filter to a more specific subset, like “items that are both .item and .featured”.
.list > :nth-child(1 of .item.featured) {
background: #111;
color: #fff;
}
.list > :nth-child(2 of .item.featured) {
background: #111;
color: #fff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.list {
display: grid;
gap: 10px;
border: 3px solid #111;
border-radius: 16px;
padding: 14px;
background: #f6f6f6;
}
.item {
padding: 12px 14px;
border-radius: 14px;
border: 2px solid #111;
background: #fff;
}
.featured {
box-shadow: 0 0 0 3px #111 inset;
}
.kicker {
font-size: 13px;
opacity: 0.8;
}
Not featured
.itemFeatured
.item.featured #1Not featured
.itemFeatured
.item.featured #2Featured
.item.featured #3
Patterns From The End With :nth-last-child(… of .class)
Everything you learned about patterns and ranges works from the end too. This is great for things like: “highlight the last 2 tags”, “hide all but the last 3 chips”, or “style the second-to-last warning”.
.row > :nth-last-child(-n + 2 of .tag) {
background: #111;
color: #fff;
}
.row > :nth-last-child(2 of .tag) {
background: #111;
color: #fff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 14px;
border: 3px solid #111;
border-radius: 16px;
background: #f6f6f6;
}
.tag,
.other {
padding: 8px 10px;
border-radius: 999px;
border: 2px solid #111;
background: #fff;
font-size: 14px;
}
.tag {
background: #eaf4ff;
}
.other {
background: #fff3d6;
border-style: dashed;
}
.tag #1 not .tag .tag #2 .tag #3 not .tag .tag #4 .tag #5
Why “Nth Of Class” Might Not Work
When :nth-child(N of .class) “does nothing”, it’s almost always one of these:
-
You’re not selecting the direct children.
:nth-child()always works on siblings (children of the same parent). If you meant “any descendant”, you’ll need a different structure or selector. -
Your selector is missing the parent relationship.
Example:
.list :nth-child(1 of .item)can match lots of nested stuff. Often you want.list > :nth-child(1 of .item)to only count direct children. Learn more in the CSS Direct Child Selector (>) Interactive Tutorial. -
You used the old pattern by accident:
.item:nth-child(2)is not “2nd item”, it’s “an item that is also the 2nd child”. Theofclause is the crucial part. -
Browser support.
If you’re testing in a browser that doesn’t support the
ofclause, the whole selector is invalid and won’t match. Check: caniuse.com/css-nth-child-of. -
Your “filtered set” has fewer matches than you think.
If there is only one
.item, then:nth-child(2 of .item)can’t match anything.
A Quick Debug Playground
This playground shows a classic mistake and the fix.
.panel :nth-child(2 of .box) {
outline: 4px solid #111;
}
.panel > .row > :nth-child(2 of .box) {
outline: 4px solid #111;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.panel {
border: 3px solid #111;
border-radius: 16px;
padding: 14px;
background: #f6f6f6;
display: grid;
gap: 10px;
}
.row {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.box {
width: 120px;
height: 70px;
border: 2px solid #111;
border-radius: 14px;
display: grid;
place-items: center;
background: #eaf4ff;
}
.other {
width: 120px;
height: 70px;
border: 2px dashed #111;
border-radius: 14px;
display: grid;
place-items: center;
background: #fff3d6;
font-size: 14px;
}
.nested {
border: 2px solid #111;
border-radius: 14px;
padding: 10px;
background: #fff;
}
.box #1other.box #2.box #3nested boxnested box
Snippet 1 uses .panel :nth-child(...) (descendants), so it may unexpectedly match nested content.
Snippet 2 uses .panel > .row > :nth-child(...) (direct children), which is usually what you want
for “nth-of-class” patterns.
Practical “Nth Of Class” Recipes
Highlight First And Last Of A Class
This is a super common UI pattern: first/last tag gets special styling.
.tags > :nth-child(1 of .tag) {
background: #111;
color: #fff;
}
.tags > :nth-last-child(1 of .tag) {
background: #111;
color: #fff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.tags {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 14px;
border: 3px solid #111;
border-radius: 16px;
background: #f6f6f6;
}
.tag,
.sep {
padding: 8px 10px;
border-radius: 999px;
border: 2px solid #111;
background: #eaf4ff;
font-size: 14px;
}
.sep {
background: #fff3d6;
border-style: dashed;
}
Zebra Striping On Only One Class
Imagine a list where some rows are “ads” or “notes” and you want striping only on real data rows.
That’s exactly what odd of .row and even of .row are for.
.table > :nth-child(odd of .data-row) {
background: #111;
color: #fff;
}
.table > :nth-child(even of .data-row) {
background: #111;
color: #fff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.table {
border: 3px solid #111;
border-radius: 16px;
overflow: hidden;
}
.data-row,
.note-row {
display: grid;
grid-template-columns: 1.2fr 1fr 0.8fr;
gap: 10px;
padding: 12px 14px;
border-bottom: 2px solid #111;
background: #fff;
}
.note-row {
background: #fff3d6;
border-bottom-style: dashed;
font-size: 14px;
}
.cell {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.head {
background: #f6f6f6;
font-weight: 700;
}
NameRoleStatusAdaEngineerActiveThis row is not data. It should not affect zebra striping.BeaDesignerActiveCamWriterPausedAnother non-data row.DeePMActive
Fallback Ideas When of S Isn’t Available
If you need a fallback for older browsers, CSS alone can’t fully replicate “nth-of-class” counting without the
of clause.
Here are the most common strategies:
-
Add wrapper elements so the siblings you care about become the only children in a container (then
normal
:nth-child()works). -
Add helper classes server-side (or in your template system) like
.is-first,.is-last,.is-odd,.is-even. - Use JavaScript to mark items at runtime when content is truly dynamic.
In modern projects, you’ll often be fine using the real thing and letting older browsers get the “unstyled” version. Use it as a progressive enhancement.
Cheat Sheet
-
First of class:
:nth-child(1 of .item) -
Last of class:
:nth-last-child(1 of .item) -
Second of class:
:nth-child(2 of .item) -
Second-to-last of class:
:nth-last-child(2 of .item) -
Odd of class:
:nth-child(odd of .item) -
Even of class:
:nth-child(even of .item) -
First 3 of class:
:nth-child(-n + 3 of .item) -
2 through 4 of class:
:nth-child(n + 2 of .item):nth-child(-n + 4 of .item) -
Last 2 of class:
:nth-last-child(-n + 2 of .item) -
Count multiple selectors together:
:nth-child(2 of .chip, .tag)
CSS Nth Of Class Conclusion
The of S clause is a game-changer for CSS selectors, finally giving us the ability to select “the nth of
a class” without extra markup or JavaScript.
It’s a bit more verbose than we’d like, but the power and flexibility it provides are well worth it.
And that’s your “nth-of-class” selector — not as a separate pseudo-class, but as a much more powerful upgrade to
:nth-child().
Learn even more about :nth-child() and the of S clause in the CSS Nth Child (N of Selector) Interactive Tutorial.
Just starting with :nth-child? Check out the CSS Nth Child Interactive Tutorial.
