The State of Modern CSS in 2026
Every few months the CSS spec quietly ships something that would have been a JavaScript library two years ago. The latest batch is no exception. Between Firefox 154 shipping sibling-index(), the infinity keyword maturing inside calc(), and container scroll-state queries finally landing in Chromium, front-end developers have a genuinely different toolbox than they did in 2024.
This roundup walks through the four features worth wiring into your next project — with runnable code, not just spec quotes. If you've been tracking the framework-level shifts too, our React Conf 2025 takeaways on the compiler and React 19.2 are a good companion read.
Before you copy-paste anything below, check caniuse.com — Baseline status is moving fast in 2026 and half of these features are still gated behind flags in Safari.

Feature 1: sibling-index() for Animations
sibling-index() returns the 1-based position of an element among its siblings. Combined with sibling-count() and calc(), it lets you stagger animations without touching JavaScript or hardcoding delays.
/* Stagger a list of cards without JS */
.card {
/* Each card gets a delay proportional to its position */
animation-delay: calc((sibling-index() - 1) * 120ms);
animation: fadeUp 400ms ease-out both;
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
Firefox 154 ships this on 2026-08-18, which flips it to Baseline: Newly Available. The killer demo circulating in the community is a knockout-bracket layout built entirely in CSS — no SVG, no JS state machine.
/* Alternate direction based on odd/even position */
.bracket-node {
--dir: calc(mod(sibling-index(), 2));
translate: calc(var(--dir) * 24px) 0;
}
The pattern to internalize: position is now a first-class CSS value. If you're still writing Array.from(nodes).forEach((el, i) => el.style.setProperty('--i', i)) in 2026, you can delete that shim.
Feature 2: The infinity Keyword in calc()
calc() now accepts infinity and -infinity as first-class numeric values. At first glance this looks like a novelty — until you realize it collapses several edge-case hacks into one keyword.
The most elegant use case is corner-shape:
/* These two are equivalent */
corner-shape: square;
corner-shape: superellipse(infinity);
/* And these two */
corner-shape: notch;
corner-shape: superellipse(-infinity);
In other words, infinity lets you express "rounded to the theoretical limit" — which visually means not rounded at all. This is useful for design systems that want a single --radius token that can be dialed from soft to sharp without branching logic.
Other practical uses:
- Clamping values where you want "no upper bound" without a magic number like
9999px. - Building responsive type scales where the upper clamp is intentionally unbounded.
- Expressing
min-content-style constraints insidecalc()chains.
It's the kind of primitive that feels academic until you need it, then it's the only clean answer.

Feature 3: Container Scroll-State Queries (stuck:)
Container scroll-state queries let a child element respond to its own scroll state — including whether it's stuck to a position: sticky boundary.
.sticky-header {
container-type: scroll-state;
}
@container scroll-state(stuck: top) {
.sticky-header .title {
font-size: 0.875rem;
box-shadow: 0 2px 8px rgb(0 0 0 / 0.08);
}
}
This replaces the classic IntersectionObserver + class-toggle pattern that every design system has reimplemented at least three times. No observer, no scroll listener, no layout thrash.
Browser support reality check: Chromium-only as of this writing. Neither Safari nor Firefox has shipped it. Ship it behind @supports (container-type: scroll-state) and treat the fallback as the baseline experience.
Feature 4: Should Lazy Loading Be the Default?
A recurring debate in the community: loading="lazy" is opt-in, but the wrong default for most pages. Lea Verou's argument is that we need a way to flip the default at the stylesheet level — something like:
/* Hypothetical — not shipping yet */
img, video { loading: lazy; }
…with per-element loading="eager" overrides. This would require a CSS property that maps to an HTML attribute, which is exactly what the old CAS (Cascading Attribute Sheets) proposal tried to solve. It's not shipping, but it's the right conversation to have.
Comparison: What to Adopt Now vs. Later
| Feature | Chrome | Firefox | Safari | Baseline? | Adopt now? |
|---|---|---|---|---|---|
sibling-index() | ✅ | 154 (Aug 2026) | ⚠️ Partial | Newly Available | Yes, with fallback |
infinity in calc() | ✅ | ✅ | ⚠️ Behind flag | Limited | Progressive enhancement |
@container scroll-state(stuck:) | ✅ | ❌ | ❌ | No | Behind @supports |
loading as CSS property | ❌ | ❌ | ❌ | No | Watch only |
A Note on Chrome's 14-Year Roadmap
Chrome's public roadmap now extends to Chrome 499 — roughly 14 years of scheduled milestones. That's not a bug; it's a deliberate signal that the platform is committing to long-horizon stability. For framework authors and design-system maintainers, this means the spec surface you target today will still be relevant when you're maintaining the same codebase in 2040.
If you're interested in how long-lived libraries evolve under this kind of stability pressure, our analysis of TRL v1.0's post-training library design philosophy is a good parallel read.

Limitations and Caveats
- Baseline ≠ safe. "Newly Available" means the newest version of each engine supports it. Users on last-year's Safari or Firefox won't see your
sibling-index()animations — they'll just see static elements. Always design the no-animation state first. infinityis not a substitute for design tokens. Using it to express "no rounding" is elegant, but don't let it leak into your token system as a semantic value. Keep--radius-sharp: 0and map tosuperellipse(infinity)only inside the component that needs it.- Scroll-state queries are Chromium-only. Don't build critical UX on them. A
stuckheader that only shrinks in Chrome is a progressive enhancement, not a feature. - The lazy-loading default debate is a spec problem, not a framework problem. No amount of Next.js config will fix it. Follow the CSSWG discussions if you care.
Where to Go Next
- Audit your animation code for the
--icustom property pattern. Most of it becomessibling-index()— a one-line win. - Prototype a
corner-shapecomponent with a single--radiustoken that dials from soft to sharp. This is the fastest way to internalizeinfinity. - Wrap your sticky headers in
@supports (container-type: scroll-state)and ship the enhancement. Fallback is the current behavior — no risk. - Follow the CSSWG on the lazy-loading default question. If it lands, it changes how every image-heavy site is built.
Modern CSS in 2026 is less about new syntax and more about deleting JavaScript. Every feature above exists because someone got tired of writing an observer. That's the through-line worth tracking.