CSS tests a way to make the `:nth-child()` selector adjustable with variables

- Reading
- 4 min
- Code
- 3
An experiment published on CSS-Tricks uses two new CSS functions, if() and sibling-index(), to recreate the logic of :nth-child() with variables that change in real time, without touching the selector.
The problem static :nth-child() never solved
Anyone who designs a card grid, a list with a periodic highlight, or a gallery with an interspersed banner has run into this: the rule that decides "every so many items, one different element" gets hardcoded into CSS as div:nth-child(5n + 8). If the product team decides, after a test, that the highlight should appear every 4 items instead of 5, someone has to open the CSS and rewrite the selector by hand.
This looks like a technical detail, but it is product friction: a visual pattern that should be an adjustable parameter (almost a design token) is buried inside CSS's most hermetic syntax. A recent post on CSS-Tricks starts from exactly this frustration and proposes recreating the entire :nth-child() using regular CSS variables, so that the numbers in the pattern can be swapped without rewriting the rule.
The two new pieces: sibling-index() and if()
The proposal depends on two still-experimental features of the CSS specification. The first is the sibling-index() function, which returns the element's position among its siblings, the same number that :nth-child() uses internally. The second is the if() syntax combined with style(), which allows testing a condition inside the value of a property itself.
In practice, this looks like:
.container {
--B: 5;
}
.container div {
background: if(style(sibling-index() = var(--B)): red; else: lightblue);
}The element in the fifth position turns red; the rest, light blue. The difference from traditional :nth-child(5) is that --B is a variable: it can come from a different class, from a data-* attribute, or in the future from a design system token, without anyone needing to touch the selector.

From fixed number to adjustable parameter
The post's author shows the same logic growing in complexity. To replace :nth-child(n + 5), just swap the equality for a comparison:
background: if(style(sibling-index() >= var(--B)): red; else: lightblue);For a range of positions (the equivalent of combining :nth-child(n + X):nth-child(-n + Y)), both variables enter the same condition:
background: if(style(var(--X) <= sibling-index() <= var(--Y)): red);The most complex case is rebuilding the generic form :nth-child(An + B), which requires using the mod() function to check whether the position is a multiple of the interval. The author himself admits a pitfall: when --A equals 0, the mod() result breaks, and he needs an extra auxiliary variable just to cover that case. It's the kind of detail that shows the difference between a syntax designed for humans to read (:nth-child()) and one rebuilt piece by piece with variables.
What this changes for those who decide the layout, not just those who write the CSS
The real gain from this technique isn't for someone who writes plain CSS once and forgets about it. It's for whoever maintains a design system where the same repeating pattern ("highlight every N cards", "divider line every M list items") recurs across different screens, with numbers that change by product, by breakpoint, or by A/B test variant.
Today, adjusting that number usually requires one of two routes: asking the front-end team to rewrite the selector, or solving it via JavaScript, recalculating classes at runtime. The second route works, but it has a perceived performance cost: JavaScript manipulating the DOM to apply style is more expensive and more prone to layout shift than a purely declarative rule resolved by the CSS engine.
In short: if these variables live as design tokens (--destaque-intervalo: 5, for example), the number that today is buried inside a CSS selector becomes something that appears in a tokens file or a theme variable, auditable and changeable without touching the selector itself. It's a small reduction in the distance between the design decision ("every how many items does the highlight appear?") and the code that executes it.
The limits: why this doesn't replace :nth-child() in everyday use
The author himself is direct about the real scope of the experiment:
You will probably never need to rely on a dynamic nth-child() selector, but I am sure you will learn one or two useful tricks along the way.
Three caveats apply for anyone considering bringing this into a real product:
- Verbosity: even the author acknowledges that the code becomes "redundant" compared to the single line of native
:nth-child(). For a simple, fixed pattern, rewriting everything withif()andstyle()is extra work with no visible gain. - Syntax maturity:
sibling-index()andif()/style()are bleeding-edge features of the CSS specification, documented by the post itself as a novelty that requires extra reading to understand the condition syntax. - Narrow use case: the technique pays off when the number defining the pattern needs to change frequently (A/B testing grid density, product variant, configurable theme). When the pattern is fixed for the component's lifetime, traditional
:nth-child()remains the simplest and most readable option.
The value of the material, then, is not "use this tomorrow in production." It's understanding that CSS is gaining a layer of conditional logic that historically only existed in JavaScript or in preprocessors, and that layout decisions currently hardcoded into selectors can gradually migrate to variables that a design and product team can inspect and adjust without depending on a code rewrite every time a visual rule changes.
Translated from the Brazilian Portuguese original · Read the original
Vercel MCP Now Displays Deploys as Interactive Cards Inside ChatGPT
Vercel added an interactive card to its MCP server that shows a deployment's status, logs, and commit directly inside ChatGPT and other MCP Apps-compatible clients, without needing to open the dashboard.




