DEV Community

Cover image for A New Stack for Turning HTML & CSS Into an Application Layer
iDev-Games
iDev-Games

Posted on

A New Stack for Turning HTML & CSS Into an Application Layer

(State.js, Motion.js, Gravity.js, Cursor.js, Keys.js — a browser‑native ecosystem)

For years, frontend development has revolved around one assumption:

If you want interactivity, state, animation, or physics — you need a framework.

React, Vue, Svelte, Solid… all incredible tools, but they all share the same core idea:

  • JS owns the state
  • JS owns the rendering
  • JS owns the reactivity
  • JS owns the lifecycle

HTML and CSS are treated as passive output layers.

But what if that assumption is wrong?

What if the browser already has the primitives we need — and we just haven’t been using them as a unified system?

That’s the question that led me to build a small ecosystem of libraries that all share one philosophy:

Expose browser signals to CSS.

Let HTML declare behaviour.

Let CSS render it.

Use JS only as the glue.

This post introduces that ecosystem.


Why This Stack Exists

Modern frontend development is powerful — but also heavy.

  • Bundlers
  • Virtual DOMs
  • Component compilers
  • State libraries
  • Effect systems
  • Re-renders
  • Signals
  • Hooks
  • Stores

All to build things the browser can already do.

Meanwhile, CSS has quietly evolved into a reactive rendering engine:

  • CSS variables
  • calc()
  • transitions
  • animations
  • container queries
  • view transitions
  • scroll-driven animations
  • :has()
  • @property
  • custom easing

And HTML has evolved into a declarative configuration surface:

  • attributes
  • custom elements
  • dataset
  • templates
  • inert
  • popovers

So the question became:

What if we treat HTML + CSS as the application layer,

and JavaScript as the runtime that feeds them state?

That’s the foundation of this stack.


The Stack (Browser‑Native, Declarative, Modular)

Each library does one thing, exposes one set of signals, and stays tiny.


1. State.jsCSS‑Reactive State Engine

State.js exposes reactive state directly to CSS variables.

<div id="player"
     data-state
     data-state-watch="health,score"
     data-state-var="true"
     data-health="100"
     data-health-min="0"
     data-health-max="100"
     data-score="0">

    <div class="health-bar"></div>
</div>
Enter fullscreen mode Exit fullscreen mode

No re-renders.

No virtual DOM.

No diffing.

Just state → CSS → rendering.


2. Motion.jsScroll, Time & Viewport Signals

Motion.js exposes:

  • scroll progress
  • element visibility
  • time
  • easing
  • playback

…as CSS variables.

<div data-motion data-motion-var="true" data-motion-duration="2000">
  <!-- CSS animates based on scroll/time -->
</div>
Enter fullscreen mode Exit fullscreen mode

3. Gravity.jsPhysics Exposed to CSS

Gravity.js simulates:

  • gravity
  • velocity
  • friction
  • forces

…and exposes them as CSS variables.

<div data-gravity data-gravity-type="static">
  <!-- CSS animates based on physics -->
</div>
Enter fullscreen mode Exit fullscreen mode

This lets designers build physical-feeling interfaces without writing physics code.


4. Cursor.jsCursor State as CSS Variables

Cursor.js exposes:

  • x/y position
  • velocity
  • direction
  • hover state

…to CSS.

<div data-cursor>
  <!-- CSS reacts to cursor movement -->
</div>
Enter fullscreen mode Exit fullscreen mode

5. Keys.jsKeyboard State as CSS Variables

Keys.js exposes:

  • key pressed
  • key held
  • key released

…to CSS.

<div data-keys>
  <!-- CSS reacts to keyboard input -->
</div>
Enter fullscreen mode Exit fullscreen mode

Why This Works

Because CSS is already a reactive rendering engine.

When you expose state to CSS variables:

  • CSS handles the rendering
  • CSS handles the animation
  • CSS handles the interpolation
  • CSS handles the transitions
  • CSS handles the layout

JavaScript becomes:

  • the state updater
  • the signal provider
  • the glue

Not the renderer.

This flips the traditional model:

Traditional This Stack
JS renders CSS renders
JS animates CSS animates
JS manages state JS exposes state
HTML is passive HTML is declarative
CSS is styling CSS is reactive

Why Not Just Use React?

React is incredible — but it’s a UI framework.

This stack is a browser-native runtime.

React owns:

  • rendering
  • state
  • components
  • lifecycle

This stack owns:

  • signals
  • state exposure
  • declarative behaviour
  • CSS-driven rendering

They’re not competitors.

They’re different layers.

You could even use them together.


What This Enables

This stack makes it trivial to build:

  • physical-feeling UI
  • scroll-reactive UI
  • cursor-reactive UI
  • keyboard-reactive UI
  • time-based UI
  • stateful UI
  • animated UI

…without writing application logic.

It also opens the door to something bigger:

A new authoring medium where designers can build interactive systems without touching JS.

Think:

  • Figma-like tools that export real interactions
  • CSS-driven physics
  • declarative behaviour graphs
  • HTML as the app layer
  • CSS as the rendering engine
  • JS as the runtime

This is the direction the web has been moving toward for years.

This stack just pushes it further.


Where This Goes Next

This isn’t a framework.

It’s not a React competitor.

It’s not a “React killer.”

It’s a new layer.

A browser-native layer.

A declarative layer.

A layer that asks:

How much interactivity can we get from HTML + CSS before we need application code?

And the answer so far is:

More than people think.


If You Want to Explore the Stack

Here are the individual libraries:

Each one is tiny, focused, and works independently.

But together, they form something new.

A stack.

A philosophy.

A new way of thinking about the browser.

Top comments (0)