State.js


Documentation v1.1.0


What is State.js?


State.js is a super simple, efficient and lightweight CSS framework that exposes DOM element states as CSS variables. Track data attributes, form inputs, media playback, and element visibility - all automatically exposed for use in your CSS animations and transitions.
Using nothing but the power of CSS, HTML and State.js, you can create dynamic dashboards, reactive web applications, and interactive experiences! State.js is really lightweight and created with javascript without requiring any dependencies.
State.js includes state-animations.css with predefined UI animations. Pair with Cursor.js, Keys.js, and Motion.js for a complete CSS/HTML development toolkit - perfect for games, dashboards, and interactive interfaces.

State-Animations.css


Predefined CSS animations for UI feedback, notifications, progress indicators, status effects, and interactive elements

How To Install?

All you need to do is add the state.js file into your projects JS folder and add the following code with your state.js location as the src. Put this code in to your head HTML tags.

                            <script src="/js/state.js"></script>
                    

Or just add a CDN instead (coming soon)

                        <script src="https://cdn.jsdelivr.net/npm/@idevgames/state-js/src/state.js"></script>
                    

How To Use?

To activate state.js add the data attribute "data-state" or a class "enable-state" to your html element. State.js automatically tracks element visibility and adds the "state" and "state-visible" classes when elements appear on screen.

                            <div class="fadeIn" data-state> </div>
                    
                        .fadeIn{ opacity:0; }
                        .fadeIn.state{ animation: fadeIn 1s normal forwards ease-in-out; }
                        @keyframes fadeIn { 0% { opacity:0; } 100% { opacity:1; } }
                    

Data Attribute Tracking

State.js can watch data attributes and automatically expose them as CSS variables. Perfect for progress indicators, counters, stats, and dynamic data visualization!

                            <div id="player" data-state
                                 data-state-watch="health,score"
                                 data-health="100"
                                 data-health-min="0"
                                 data-health-max="100"
                                 data-score="0">
                            </div>
                    
                            #player .health-bar {
                                width: var(--state-health-percent);
                                background: linear-gradient(90deg, red, yellow, green);
                            }

                            [data-health="0"] {
                                animation: death 2s forwards;
                            }
                    

Interactive Demo

Move the slider to change the health value (no JavaScript needed!):

Health: 75%

The Code Behind This Demo:

<!-- Slider automatically updates healthDemo element -->
<input type="range"
       data-state
       data-state-bind="healthDemo"
       data-state-attr="health"
       min="0" max="100" value="75">

<!-- Health bar watches the health attribute -->
<div id="healthDemo"
     data-state
     data-state-watch="health"
     data-health="75"
     data-health-min="0"
     data-health-max="100">

    <div class="health-bar">
        <div class="health-fill"
             style="width: var(--state-health-percent);">
        </div>
    </div>

    <div>Health: <span data-state-display="health">75</span>%</div>
</div>

Button Triggers & Increment

Make buttons control state without writing JavaScript! Use increment for clickers and counters:

Clicks: 0

The Code:

<div id="clickerDemo"
     data-state
     data-state-watch="clicks"
     data-clicks="0">

    <div>Clicks: <span data-state-display="clicks">0</span></div>

    <!-- Button increments by 1 -->
    <button data-state
            data-state-trigger
            data-state-bind="clickerDemo"
            data-state-attr="clicks"
            data-state-increment="1">
        CLICK ME +1
    </button>

    <!-- Button increments by 5 -->
    <button data-state
            data-state-trigger
            data-state-bind="clickerDemo"
            data-state-attr="clicks"
            data-state-increment="5">
        CLICK ME +5
    </button>

    <!-- Progress bar using CSS variable -->
    <div class="progress-bar"
         style="width: var(--state-clicks-percent);"></div>
</div>

Auto-firing Triggers: The Missing Primitive

Automatically fire triggers when conditions become true - perfect for passive income, auto-unlocks, achievements, and automatic progression!

Gold: 0
Gems: 0
Status: Locked (need 20 gold)
What's happening:
  • Every 10 gold automatically converts to 1 gem (passive income!)
  • When you reach 20 gold, status automatically unlocks
  • No clicks required - pure automatic progression!

The Code:

<!-- Passive income: auto-convert 10 gold → 1 gem -->
<button id="autoConvert"
        data-state
        data-state-trigger
        data-state-bind="player"
        data-state-attr="gold"
        data-state-decrement="10"
        data-state-condition="gold >= 10"
        data-state-autofire="true"
        data-state-trigger-chain="addGem"
        style="display:none">
</button>

<!-- Auto-unlock at level 20 -->
<button data-state
        data-state-trigger
        data-state-bind="player"
        data-state-attr="unlocked"
        data-state-set="true"
        data-state-condition="gold >= 20"
        data-state-autofire="true"
        style="display:none">
</button>

The Magic:

When a condition transitions from falsetrue, the trigger fires automatically! No click required. No visibility required. This is the missing primitive for automatic game mechanics.


New in v1.1.0: Game Development Extensions

Seven new declarative primitives for building complete games with ZERO hand-written JavaScript logic!


Extension 1: Interval Triggers (Passive Income)

Triggers that fire automatically on a timer - perfect for passive income and cooldowns!

Gold: 0
Workers: 0
Watch the magic:

Each worker generates 1 gold per second automatically! Hire more workers to increase your income.

The Code:

<!-- Hidden trigger fires every 1000ms -->
<button data-state
        data-state-trigger
        data-state-bind="player"
        data-state-attr="gold"
        data-state-increment="calc(var(--state-workers))"
        data-state-interval="1000"
        data-state-condition="workers > 0"
        style="display:none">
</button>

Extension 2 & 3: data-state-set & data-state-text

data-state-set sets exact values, data-state-text uses template strings!

Notice:

Text updates automatically using {token} syntax! Full Heal uses data-state-set with calc() to restore to max.

The Code:

<!-- Template string with tokens -->
<h2 data-state
    data-state-bind="player"
    data-state-text="Level {level} {name}">
</h2>

<!-- Set to exact value using calc() -->
<button data-state-trigger
        data-state-bind="player"
        data-state-attr="health"
        data-state-set="calc(var(--state-healthmax))">
    Full Heal
</button>

Extension 4: Conditional CSS Classes

Automatically add/remove CSS classes based on conditions - perfect for visual feedback!

Health: 100/100
Watch the bar change:

≤50 HP: Yellow pulse | ≤20 HP: Red shake | 100 HP: Green glow

The Code:

<div data-state
     data-state-bind="player"
     data-state-class="health-low"
     data-state-class-condition="health <= 50"
     data-state-class-2="health-critical"
     data-state-class-condition-2="health <= 20"
     data-state-class-3="health-full"
     data-state-class-condition-3="health >= 100">
</div>

<style>
.health-low { animation: pulse 1s infinite; }
.health-critical { animation: shake 0.5s infinite; }
.health-full { box-shadow: 0 0 20px green; }
</style>

Extension 5: Procedural Sound Effects

Fire-and-forget Web Audio sounds - no audio files needed! Click the buttons to hear:

Score: 0
Built-in sounds:

click • coin • buy • levelup

Generated with Web Audio API - zero external dependencies!

The Code:

<button data-state-trigger
        data-state-bind="player"
        data-state-attr="score"
        data-state-increment="10"
        data-state-sound="coin">
    Collect Coin
</button>

Extension 6 & 7: Persist & Events

data-state-persist saves to localStorage, data-state-event dispatches CustomEvents!

Save Count: 0
Event log will appear here...
Try this:

1. Click "Increment & Save" a few times
2. Reload the page - your count persists!
3. Watch the event log below for CustomEvents

The Code:

<div id="game"
     data-state
     data-state-watch="score,level"
     data-state-persist="true"
     data-state-persist-key="my-game">
</div>

<button data-state-trigger
        data-state-attr="score"
        data-state-increment="10"
        data-state-event="score-up">
</button>

<script>
document.addEventListener('state:score-up', (e) => {
    console.log('Score increased!', e.detail);
});
</script>

CSS Variables Available

State.js creates CSS variables automatically based on your configuration:

--state-visible (0 or 1)
--state-intersection (0-100%)
--state-viewport-x (0-100%)
--state-viewport-y (0-100%)
--state-[name] (raw value)
--state-[name]-percent (0-100%)
--state-[name]-normalized (0-1)
--state-[name]-deg (0-360deg)
--state-[name]-reverse (100%-0%)

The data attribute will also update in increments for CSS attribute selectors like [data-state-health="50"]{ }


Data Attributes

You can use the below data attributes for additional features

<div id="yourelement"
     data-state
     data-state-var="true"
     data-state-watch="health,mana,xp"
     data-state-toggles="active,locked"
     data-state-dimensions="true"
     data-state-media="true"
     data-state-global="true"
     data-state-increment="10">
</div>
                        

BUILD REACTIVE UI
WITH CSS/HTML

Check out the code of the documentation as an example.