You wrote :nth-child(3n + 5) in a stylesheet. Now a design token says it should be every fourth item from the second. So you edit the selector. Then the selector in the media query. Then the one in the component variant.
The An + B numbers live inside the selector, and a selector can't read a custom property. That's the itch Temani Afif scratches in a new CSS-Tricks article. He rebuilds the :nth-child() patterns as value-level logic, using sibling-index(), the if() function with style() queries, and mod(). Once the numbers are custom properties, you can change them from anywhere the cascade reaches.
From selector to condition
Every example in the article uses the same markup: a .container with a run of plain div children. Nothing to see there. The interesting part is where the logic moves.
sibling-index() gives an element its position among its siblings as a number. Feed that number to a style() query inside if() and you get a per-element condition that sits in the declaration, not the selector. Here's the single-element case, the equivalent of :nth-child(5):
.container { --B: 5; } .container div { background: if(style(sibling-index() = --B): red; else: lightblue); } Every div matches the selector. Each one then works out its own background. The fifth one ends up red.
Swap = for >= and you have :nth-child(n + B), everything from position B onward. Swap it for <= and you have :nth-child(-n + B), the first B items. Same rule, one character changed. The article also builds a range version with two variables, an --X and a --Y, that marks every item between the two positions.
The modulo trick
Step patterns need arithmetic. :nth-child(An) means "every Ath element", which is a polite way of saying the index divides cleanly by A. That's what mod() checks:
.container div { background: if(style(mod(sibling-index(), var(--A)) = 0): red); } Set --A: 3 on the container and every third child goes red. Afif also guards the case where --A is negative, adding a second condition joined with and so the rule only fires when var(--A) > 0.
Then he goes for the full An + B. That one takes a couple of helper custom properties. The first is computed with calc() from the index, --B and --A. The second combines sign() and mod() to test whether that value is a whole number and not negative. There's also a branch for when --A is zero. I won't paste it here. Go read it with devtools open and watch the helper values change from item to item. It's the best part of the article.
Why this is worth your time
Here's my take. The real change is not "you can do :nth-child() without :nth-child()." It's that position-based styling becomes data.
A selector is fixed at authoring time. A custom property cascades. It inherits, it gets overridden by a more specific rule, and it can be set inline on one container. Afif names this as the main advantage: you adjust the selection through variables in real time without rewriting a selector. He also argues the conditional reads more naturally than An + B notation. I agree. sibling-index() >= --B tells you what it does. n + B makes you work it out.
The trade-off is real too. The selector version can apply a whole rule block at once. The if() version works one declaration at a time, so styling three properties means three if() calls, or one custom property you branch on. It's more verbose. You get flexibility in exchange.
Before you ship it
Afif is upfront here, and you should take it seriously. These are draft CSS features and browser support is limited. Treat this as a lab technique for now. Where :nth-child() does the job with fixed numbers, keep using :nth-child(). It works everywhere and needs no explaining in code review.
He ends with homework: build the :nth-last-child() version using sibling-count(). Try it. Counting from the end means working out how far each item sits from the last sibling, and that exercise will teach you more about these functions than another demo would.

