Skip to content

prop-for-that

Expose runtime state JavaScript can read but CSS can't — slider values, pointer position, element size, visibility, battery, network — as batched, diffed CSS custom properties.
<script type="module">import 'prop-for-that/auto'</script>
<html data-props-for="pointer"> <!-- cursor → :root -->
<div id="card" data-props-for="size"></div> <!-- size → the card -->
.card {
/* lean toward the cursor — no JS in your styles */
transform: rotateY(calc((var(--live-pointer-x-ratio) - 0.5) * 16deg));
/* fluid type from the element's own measured width */
font-size: calc(var(--live-w) * 0.04px);
}

One module import and a data-props-for attribute — that’s it. Plugins like pointer load on demand. Prefer programmatic control and teardown? There’s an imperative API too.

Zero-config auto mode

Import prop-for-that/auto, add data-props-for attributes, done. A MutationObserver binds new elements as they appear — no JavaScript beyond the import.

Batched & diffed

One setProperty flush per animation frame, and only when a value actually changed. A hundred pointermove events in a frame collapse to one write.

Four core sources

viewport, size, visibility, and range ship in the main bundle. 20+ more — pointer included — are opt-in, tree-shakeable plugins, loaded on demand under auto.

One loop, one observer

A single shared rAF, one ResizeObserver, one IntersectionObserver, and ref-counted passive listeners drive the whole page — bind a thousand elements, pay for one of each.

Discrete state, real rules

Pair continuous numbers with var() / calc(), then flip whole rule blocks with @container style(--live-value: 100). Threshold styling that works today — no range queries needed.

Typed & interpolatable

Opt into @property registration and --live-* values animate with transition / @keyframes and resolve to a guaranteed initial value.

Calm DevTools

Global values are written into one adopted stylesheet rule, not <html> inline style — so the DevTools Styles panel stays usable while values change every frame.

Freeze & throttle

pause() / resume() the whole loop to inspect a frozen frame, or cap the rate with configure({ liveHz: 30 }) for fewer writes and less style recalc.

SSR-safe & guarded

Browser globals are feature-detected; sensor and permission sources (geo, orientation, battery) no-op cleanly when unavailable.

TypeScript, ESM + CJS

A fully typed API, both module formats, .d.ts shipped, MIT licensed, zero runtime dependencies.