Skip to main content

Why JavaScript Should Mutate State Instead of Styles

Exploring quell-light.js and how using data-* attributes instead of CSS classes for state management creates more robust and maintainable codebases.

AI-written
Inewgen
08 Sep 2026Source: Dev.to4 min read (0 views)
Share
Why JavaScript Should Mutate State Instead of Styles

Stock photo for illustration only, not from the actual event

Font size
  • Avoid using inline styles or traditional classes to manage behavioral states.
  • Convention-based class naming like is- or has- introduces long-term code fragility.
  • The quell-light.js library advocates for explicit data-* attributes like data-q-active.
  • Strictly separate behavioral state from visual styling handled entirely by CSS.

A dropdown needs to open. A modal needs to appear. A nav link needs to show which section the reader is currently in. The blunt route is to manipulate element.style.display directly, deciding state and appearance in the same line without leaving anything for CSS to own. Most experienced developers already avoid this approach. Instead, a more common pattern relies on classes such as classList.toggle('is-open'). This represents a genuine improvement, as CSS determines what .is-open looks like while JavaScript merely toggles its presence.

However, complications arise because .is-open shares the exact same namespace as every other styling hook in the codebase. Nothing in the syntax distinguishes it as state rather than appearance. A teammate reviewing the stylesheet six months later has no programmatic way to tell .is-open apart from .card-title other than relying on naming conventions and trusting that the convention has been consistently maintained.

Convention-based boundaries, such as team agreements on prefixes like is- or has- for state classes, function well within disciplined teams where original members remain present. Yet, this approach remains inherently fragile as projects scale, team members rotate, and codebases grow beyond individual memory. Shifting these boundaries directly into syntax helps eliminate ambiguity and long-term maintenance overhead.

The quell-light.js library moves beyond mere convention by explicitly declaring its mandate: it is not a component library, it renders nothing, and its sole responsibility is behavioral stabilization. The most intriguing aspect is not that it avoids inline styles—which most serious codebases already do—but rather what it chooses to use instead of traditional state classes.

State should be represented explicitly as state within the syntax itself, rather than relying on informal conventions. While classes can accomplish this, data-* attributes like data-q-active achieve it with greater clarity because data attributes carry no competing styling meaning. They cannot be mistaken for utility classes, drawn into unrelated specificity conflicts, or broken by inconsistent naming habits across contributors. The complete state vocabulary of quell-light.js includes data-q-toggle, data-q-active, data-q-dismiss, data-q-spy, and data-q-current, describing exclusively what is true rather than how things appear.

Never miss the latest news?

Subscribe to get news summaries by email - not often enough to be annoying.

โฆษณา

frontend web development code editor laptop screen

Stock photo for illustration only, not from the actual event

The disclosure modules within the library prove that this discipline holds up under stringent accessibility requirements. Accessible toggles require both ARIA states and stylable hooks, and the file maintains these as two explicit attributes rather than overloading a single class. Focus management enforces strict Tab and Shift+Tab traps within open dialogs while tracking return focus points upon closure—representing true state while leaving visual rendering entirely to CSS. Similarly, the scroll watcher utilizes IntersectionObserver rather than raw scroll listeners to determine active sections through an aria-current attribute.

Ultimately, quell-light.js reinforces the boundary that convention-backed rules are only as durable as team memory, whereas boundaries encoded directly into syntax eliminate that dependency entirely.

Source: Dev.to

Comments

Leave a Comment
0/2000

Found something wrong in this article? Report an issue with this article