Introduction
CSS already reacts to plenty: :hover, [open], container queries, prefers-* media features, scroll-driven animations. What it can’t do is read arbitrary runtime values, like a slider’s current number, the pointer’s coordinates, scroll velocity, an element’s visibility, network speed, or battery level.
prop-for-that bridges that gap. It reads runtime JavaScript state and writes it into CSS custom properties, batched and diffed, so your stylesheet can compute and react with no JS in your CSS files.
The core idea
Section titled “The core idea”JavaScript knows things CSS can’t see. prop-for-that exposes them as --live-* custom properties on :root or on individual elements:
/* reveal an element once it has fully scrolled into view, and keep it */.reveal { opacity: var(--const-has-entered); transition: opacity 0.4s;}
/* pointer-driven tilt card */.card { transform: rotateX(calc((0.5 - var(--live-pointer-y-ratio)) * 20deg)) rotateY(calc((var(--live-pointer-x-ratio) - 0.5) * 20deg));}
/* range slider with live color */input[type="range"] { accent-color: hsl(calc(var(--live-value-pct) * 120) 80% 50%);}Enable the sources declaratively: import the auto entry and tag elements with data-props-for. Globals go on the root <html>; elements opt in by attribute. Plugin sources (like pointer) load on demand the first time they’re named — no extra setup:
<script type="module">import 'prop-for-that/auto'</script>
<html data-props-for="pointer"> <!-- global → :root; loads the pointer plugin on demand -->
<input type="range" data-props-for="range"><div class="reveal" data-props-for="visibility">…</div>What makes it different
Section titled “What makes it different”- Batched. One
setPropertyflush per animation frame, never per event. - Diffed. It writes only when a value actually changed.
- Lean. Zero runtime dependencies, tree-shakeable; you ship only the sources you use.
- Hybrid. Works as a zero-config auto import, an imperative API, or a synchronous head snippet for FOUC-safe constants.