For years, we've handled UI state by toggling classes in JavaScript.
User selects a date range? Loop through elements → add/remove classes.
It works. But it tightly couples visual state to DOM manipulation.
With the emerging CSS Range Syntax, we can rethink that pattern.
Instead of asking:
"Which classes should JS toggle?"
We can ask:
"What if CSS evaluates the condition itself?"
Let's walk through a concrete example.
The Traditional Pattern: JavaScript Controls Visual State
Imagine a calendar where users select a start and end date.
A typical implementation looks like this:
days.forEach(day => {
const value = Number(day.dataset.day);
if (value >= start && value <= end) {
day.classList.add('in-range');
} else {
day.classList.remove('in-range');
}
});
JavaScript:
- reads values from the DOM
- performs the comparison
- mutates classes
- controls visual state
Problems?
- JS depends on DOM structure
- Refactoring markup can break logic
- State and presentation are coupled
There’s nothing inherently wrong with this approach — it’s simply imperative.
A CSS-First Alternative
What if JavaScript only updated state values?
calendar.style.setProperty('--day-start', start);
calendar.style.setProperty('--day-end', end);
Each day exposes its numeric value:
<td class="day day-now" style="--day: 12">12</td>
Now CSS performs the comparison.
With Range Syntax:
.day-now {
background-color: if(
style(--day-start <= --day <= --day-end): #8b0000;
else: rgba(255, 255, 255, 0.05);
);
}
No loops, no class toggling, no DOM mutation.
JavaScript updates state. CSS evaluates presentation.
Live Demo (CSS Range Syntax)
In this version, JavaScript only updates state values. CSS evaluates the range condition directly. Requires Chrome 142+ (experimental support).
Try changing the start and end values in the JS panel and see how the UI responds.
Why This Is Architecturally Interesting
This shifts how we divide concerns:
JavaScript
- handles interaction
- updates state values
CSS
- evaluates visual conditions
- renders based on state
The logic that determines how something looks lives where it belongs – in CSS.
DOM Refactors Become Less Fragile
In the traditional approach, JS might depend on:
document.querySelectorAll('.calendar .row .day');
Restructure the DOM, and your logic may break.
In the CSS-driven model, JS doesn't care about structure. It only sets --day-start and --day-end.
As long as each day exposes --day, styling works.
This reduces structural coupling.
Browser Support – And a Practical Fallback
Range Syntax is still emerging and not fully supported in stable
browsers yet.
But the architectural pattern doesn't depend on it.
Today, we can emulate similar logic using clamp() and arithmetic with custom properties.
Example fallback:
.day-now {
--gte-start: clamp(0, calc(var(--day) - var(--day-start) + 1), 1);
--lte-end: clamp(0, calc(var(--day-end) - var(--day) + 1), 1);
--in-range: calc(var(--gte-start) * var(--lte-end));
background-image: linear-gradient(
rgba(139, 0, 0, var(--in-range)),
rgba(139, 0, 0, var(--in-range))
);
}
What's happening:
- Values below zero clamp to
0 - Values above one clamp to
1 - Multiplication simulates a logical AND
- The result controls opacity
It's more verbose than Range Syntax – but fully workable today.
Range Syntax mainly improves clarity.
Live Demo (Clamp Fallback)
Same architectural pattern — implemented today using clamp().
Notice that JavaScript still only updates state values.
Trade-Offs and Constraints
This pattern isn't universally better.
Things to consider:
Readability
Some teams may find class toggling clearer than CSS arithmetic.
Debugging
Conditional logic inside CSS may be less familiar to developers used to imperative control flow.
Large-scale state
If UI state becomes deeply interdependent or complex, JavaScript may
still be the better coordination layer.
The key isn’t “replace JS with CSS.” It's redefining which layer owns what.
A Broader Direction
Custom properties, container queries, :has(), scroll-driven animations, advanced attr() – CSS keeps gaining expressive power.
Range Syntax fits into that trajectory.
It doesn't eliminate JavaScript.
But it reduces how often we need it for visual state management.
And that's worth rethinking.
Top comments (0)