DEV Community

Cover image for Rethinking UI State: CSS Range Syntax vs Class Toggling
Alexander Pershin
Alexander Pershin

Posted on

Rethinking UI State: CSS Range Syntax vs Class Toggling

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');
  }
});
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Each day exposes its numeric value:

<td class="day day-now" style="--day: 12">12</td>
Enter fullscreen mode Exit fullscreen mode

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);
  );
}
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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))
  );
}
Enter fullscreen mode Exit fullscreen mode

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)