Modern frontend frameworks treat components as JavaScript functions, classes, or compiled files.
But what if components didn’t need JavaScript at all?
What if components were built from:
- HTML templates
- CSS for behavior and visuals
- State.js attributes for reactivity
…with no JS logic, no virtual DOM, and no build step?
That’s exactly what State.js enables.
State.js turns HTML attributes into reactive CSS variables, letting you build fully reactive, reusable UI components using nothing but HTML + CSS.
In this article, you’ll learn how State.js components actually work.
⭐ What Is a Component in State.js?
In React, a component is a function.
In Vue, it’s an object.
In Svelte, it’s a compiled file.
In State.js, a component is:
A reusable HTML template that becomes reactive when included with
data-state-include.
A component is:
- defined once
- cloned anywhere
- customized via attributes
- reactive automatically
- fully encapsulated
No JavaScript logic.
No rendering engine.
No framework runtime.
Just HTML + CSS + State.js.
⭐ Example: A Reusable <char-card> Component
1. Define the component once
<template id="char-card">
<div class="char-card" data-state data-hp="20" data-hp-max="20">
<div class="name" data-state-display="name"></div>
<div class="bar">
<div class="fill" style="width: var(--state-hp-percent)"></div>
</div>
<button data-state-trigger data-state-attr="hp" data-state-increment="-5">
Damage
</button>
</div>
</template>
This is the component definition.
State.js automatically exposes:
-
--state-hp -
--state-hp-max -
--state-hp-percent
These update reactively whenever attributes change.
⭐ 2. Use the component anywhere
<div data-state-include="#char-card"
data-name="Warrior"
data-hp="18"
data-hp-max="20"></div>
<div data-state-include="#char-card"
data-name="Mage"
data-hp="9"
data-hp-max="12"></div>
Each instance:
- clones the template
- merges your attributes
- becomes a fully reactive component
- auto-binds triggers to its own state
This is the State.js equivalent of props, but simpler.
⭐ CSS Drives the Component’s Behavior
.char-card .bar {
height: 12px;
background: #333;
border-radius: 4px;
overflow: hidden;
}
.char-card .fill {
height: 100%;
background: #4caf50;
width: var(--state-hp-percent);
transition: width 0.3s ease;
}
CSS handles:
- animation
- interpolation
- transitions
- layout
State.js handles:
- state changes
- variable updates
- triggers
- reactivity
This is CSS‑reactive architecture.
⭐ Updating the Component
Inside the component:
<button data-state-trigger data-state-attr="hp" data-state-increment="-5">
Damage
</button>
When clicked:
-
data-hpdecreases - State.js updates
--state-hp - CSS recalculates
--state-hp-percent - The bar animates automatically
This is reactivity without JavaScript logic.
⭐ Why This Works
State.js turns HTML attributes into live CSS variables.
CSS handles:
- layout
- animation
- transitions
- transforms
State.js handles:
- state
- triggers
- conditions
- intervals
- reactivity
Together, they form a browser‑native component system.
⭐ Why This Is a Big Deal
✔ Zero JavaScript logic
Your component logic lives in HTML + CSS.
✔ Zero framework runtime
No virtual DOM.
No hydration.
No re-renders.
✔ Zero build step
Works in plain HTML files.
✔ Fully portable components
A <char-card> works anywhere HTML works.
✔ Native browser performance
CSS is GPU‑accelerated and instant.
✔ Auto‑binding triggers
Buttons inside the template automatically bind to the nearest [data-state] parent.
⭐ Component API Design
You can define your own “props” using attributes:
<div data-state-include="#char-card"
data-name="Rogue"
data-hp="12"
data-hp-max="14"></div>
Or add more:
<div data-state-include="#char-card"
data-name="Cleric"
data-hp="14"
data-hp-max="16"
data-color="blue"
data-low="red"></div>
State.js exposes them all as CSS variables.
⭐ Conclusion
State.js lets you build components using:
- HTML for structure
- CSS for behavior
- State.js for reactivity
This creates a new category of UI architecture:
CSS‑Reactive Components
Components powered by HTML templates, CSS variables, and declarative reactivity — not JavaScript logic.
If you want lightweight, portable, framework‑free UI, this pattern is a game changer.












