
The :nth-child() of S Selector: Complex Child Matching Made Simple
CSS has long had :nth-child() for selecting elements by position, but it had a critical limitation: it couldn’t filter by type. The new :nth-child(An+B of S) syntax changes everything.
The Problem with Traditional :nth-child()
The classic :nth-child() counts all children, regardless of type:
/* Selects the 3rd child, whatever it is */
li:nth-child(3) {
color: red;
} But what if you have mixed content?
<ul>
<li class="featured">Item 1</li>
<li>Item 2</li>
<div class="ad">Advertisement</div>
<li class="featured">Item 3</li>
<li>Item 4</li>
</ul> /* This WON'T select the 3rd <li> - it selects the <div>! */
li:nth-child(3) {
color: red;
} Enter :nth-child() of S
The new syntax lets you filter before counting:
/* Select the 3rd element that matches .featured */
:nth-child(3 of .featured) {
color: red;
}
/* Select every 2nd <li> element (ignoring other elements) */
:nth-child(2n of li) {
background: #f0f0f0;
} Syntax Breakdown
:nth-child(An + B of selector) {
/* styles */
} - An+B: The nth-child formula (e.g.,
2n,3n+1,odd,even) - of selector: Filter to only count elements matching this selector
Practical Use Cases
1. Striped Lists with Mixed Content
/* Zebra stripes only on article items, ignoring ads */
:nth-child(odd of .article-item) {
background: #f9f9f9;
}
:nth-child(even of .article-item) {
background: white;
} 2. Featured Item Highlighting
/* Highlight every 3rd featured product */
:nth-child(3n of .product.featured) {
border: 3px solid gold;
box-shadow: 0 0 20px rgba(255, 215, 0, 0.3);
} 3. Category-Specific Styling
/* Style every other tech article differently */
:nth-child(even of [data-category="tech"]) {
border-left: 4px solid #0066cc;
}
/* Different styling for design articles */
:nth-child(odd of [data-category="design"]) {
border-left: 4px solid #ff6b6b;
} 4. Grid Layouts with Ads
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
/* Every 3rd product spans full width, but don't count ads */
:nth-child(3n of .product) {
grid-column: 1 / -1;
display: grid;
grid-template-columns: 300px 1fr;
} Advanced Techniques
Complex Selectors
You can use any selector in the of clause:
/* Select every 2nd element with both classes */
:nth-child(2n of .card.highlighted) {
transform: scale(1.05);
}
/* Select every 3rd element that's NOT disabled */
:nth-child(3n of :not([disabled])) {
opacity: 0.8;
}
/* Select every 4th link within navigation */
nav :nth-child(4n of a:not(.external)) {
margin-right: 2rem;
} Combining with Other Pseudo-Classes
/* First 3 featured items */
:nth-child(-n + 3 of .featured) {
font-size: 1.25rem;
font-weight: bold;
}
/* Last 2 available items */
:nth-child(n + 1 of .item:not(.sold-out)):nth-last-child(-n + 2) {
border: 2px solid orange;
} Dynamic Color Patterns
/* Create a 3-color pattern, only for published posts */
:nth-child(3n + 1 of .post.published) {
border-left: 4px solid #ff6b6b;
}
:nth-child(3n + 2 of .post.published) {
border-left: 4px solid #4ecdc4;
}
:nth-child(3n of .post.published) {
border-left: 4px solid #ffe66d;
} Real-World Example: Blog Archive
.blog-archive {
display: grid;
gap: 2rem;
}
/* Regular posts get standard layout */
.post {
padding: 1.5rem;
border-radius: 8px;
background: white;
}
/* Every 5th published post (ignoring drafts and ads) gets featured layout */
:nth-child(5n of .post[data-status="published"]) {
grid-column: 1 / -1;
display: grid;
grid-template-columns: 400px 1fr;
gap: 2rem;
padding: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
/* First 3 published posts get "new" badge */
:nth-child(-n + 3 of .post[data-status="published"]) {
position: relative;
}
:nth-child(-n + 3 of .post[data-status="published"])::before {
content: "NEW";
position: absolute;
top: 1rem;
right: 1rem;
padding: 0.25rem 0.75rem;
background: #ff6b6b;
color: white;
border-radius: 4px;
font-size: 0.75rem;
font-weight: bold;
}
/* Alternate backgrounds for regular published posts */
:nth-child(odd of .post[data-status="published"]:not(:nth-child(5n))) {
background: #f8f9fa;
} Browser Support
The :nth-child(An+B of S) syntax has good modern browser support:
- Chrome/Edge: ✅ v111+ (March 2023)
- Firefox: ✅ v113+ (May 2023)
- Safari: ✅ v9+ (2015) - Safari was the pioneer!
Current global support is ~85%, making it production-ready with fallbacks.
Progressive Enhancement
Provide graceful degradation:
/* Fallback: stripe all items */
.item:nth-child(odd) {
background: #f0f0f0;
}
/* Enhanced: stripe only products, ignore ads */
@supports selector(:nth-child(1 of .product)) {
.item:nth-child(odd) {
background: transparent; /* Reset fallback */
}
:nth-child(odd of .product) {
background: #f0f0f0;
}
} Performance Considerations
:nth-child() of S is more performant than alternatives:
/* ❌ Requires JavaScript or complex CSS */
.product:not(.ad):nth-child(odd) {
/* This doesn't work as expected */
}
/* ✅ Native, performant solution */
:nth-child(odd of .product) {
background: #f0f0f0;
} Common Pitfalls
1. Selector Specificity
/* The 'of' selector doesn't add specificity beyond the pseudo-class */
:nth-child(2 of .item) {
/* Specificity: (0, 1, 1) */
}
.item:nth-child(2) {
/* Specificity: (0, 1, 1) - same! */
} 2. Counting Restarts
The counting is per parent:
<div class="group">
<div class="item">1</div>
<div class="item">2</div>
</div>
<div class="group">
<div class="item">1</div>
<!-- Counting restarts here -->
<div class="item">2</div>
</div> /* Selects the first .item in EACH group */
:nth-child(1 of .item) {
font-weight: bold;
} 3. Empty Parent Handling
/* Won't select anything if no .featured items exist */
:nth-child(1 of .featured) {
color: red;
} Comparing with :nth-of-type()
These are different:
/* :nth-of-type() - selects by element tag */
li:nth-of-type(3) {
/* 3rd <li> element */
}
/* :nth-child() of - selects by selector */
:nth-child(3 of .featured) {
/* 3rd element matching .featured */
} :nth-child() of S is more flexible because it works with classes, attributes, and complex selectors.
Real-World Example: Product Grid
.products {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 2rem;
}
/* Highlight every 6th in-stock product with a "Featured" badge */
:nth-child(6n of .product:not(.out-of-stock)) {
position: relative;
border: 2px solid gold;
}
:nth-child(6n of .product:not(.out-of-stock))::before {
content: "⭐ Featured";
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
background: gold;
padding: 0.25rem 1rem;
border-radius: 12px;
font-size: 0.875rem;
font-weight: bold;
}
/* Every 12th in-stock product gets full-width treatment */
:nth-child(12n of .product:not(.out-of-stock)) {
grid-column: 1 / -1;
display: grid;
grid-template-columns: 300px 1fr;
}
/* First 3 new arrivals get a badge */
:nth-child(-n + 3 of .product.new-arrival) {
position: relative;
}
:nth-child(-n + 3 of .product.new-arrival)::after {
content: "NEW";
position: absolute;
top: 1rem;
right: 1rem;
background: #ff6b6b;
color: white;
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: bold;
} Debugging Tips
Use browser DevTools to test selectors:
// Test in console
document.querySelectorAll(":nth-child(3n of .product)"); Firefox DevTools shows which elements match in real-time.
Combining with Container Queries
.container {
container-type: inline-size;
}
/* At small sizes, every 2nd card */
@container (max-width: 600px) {
:nth-child(2n of .card) {
background: #f0f0f0;
}
}
/* At large sizes, every 3rd card */
@container (min-width: 600px) {
:nth-child(3n of .card) {
background: #f0f0f0;
}
} Accessibility Considerations
Visual patterns should have semantic meaning:
/* ✅ Good: Use with proper ARIA labels */
:nth-child(5n of .product) {
border: 3px solid gold;
} <article class="product" aria-label="Featured product">
<!-- content -->
</article> Don’t rely solely on visual patterns to convey information.
Conclusion
The :nth-child(An+B of S) selector fills a critical gap in CSS, enabling sophisticated child selection that was previously impossible or required JavaScript. It’s particularly powerful for:
- Mixed content layouts
- Category-based styling
- Grid patterns with filtered items
- Dynamic list styling
With solid browser support and clear syntax, it’s ready for production use today. Start using it to create more robust, maintainable selectors.
Browser support snapshot
Live support matrix for css-nth-child-of from
Can I Use.
Show static fallback image

Source: caniuse.com









