Typed properties
By default, prop-for-that writes plain custom properties. They work everywhere, but to the engine they’re untyped (<*>): they can’t be interpolated (a transition or @keyframes on them snaps instead of tweening), and var(--live-x) is invalid until a value is written, which is why you reach for var(--live-x, fallback).
Opt in, and the library registers each --live-* it writes with @property (via CSS.registerProperty):
import { configure, register, propsFor } from 'prop-for-that'import { pointer } from 'prop-for-that/plugins'
configure({ typed: true }) // before attaching any sourcesregister(pointer) // pointer is an opt-in pluginpropsFor(['pointer'])Each property then gets:
- a
<number>type, so it interpolates: atransitionor animation tweens it instead of snapping; - a guaranteed initial value of
0, sovar(--live-x)resolves with no fallback.
With the auto entry: data-props-typed
Section titled “With the auto entry: data-props-typed”Using the zero-config auto entry — binding through data-props-for instead of calling propsFor() — you can opt in straight from markup by adding data-props-typed to the root <html>:
<html data-props-typed> <body> <input type="range" data-props-for="range"> <script type="module">import 'prop-for-that/auto'</script> </body></html>Read once on load, it’s the exact equivalent of configure({ typed: true }): every --live-* the page writes is registered with @property. It’s a boolean — @property is registered per name for the whole document, not per element, so typing is all-or-nothing and any attribute value is ignored (there’s no per-key subset, just as there isn’t in the JS API). For per-property initial values, reach for the JS defaults below.
Behavior-safe and opt-in
Section titled “Behavior-safe and opt-in”Nothing animates unless you add a transition or @keyframes, so turning typed on doesn’t change how an existing stylesheet renders, except that var(--live-x) now resolves to 0 before a value arrives instead of being invalid. Smoothing a jumpy value is then one line:
.bar { width: calc(var(--live-value-pct) * 100%); transition: --live-value-pct 120ms ease-out; /* tweens because it's typed */}Default values
Section titled “Default values”Set per-property defaults in the same configure call, keyed by a source’s local name. They become the @property initial value, so var(--live-…) resolves to them before any data arrives, overriding the source’s own default and the 0 fallback:
configure({ typed: true, defaults: { 'pointer-x-ratio': 0.5, // centered until the pointer moves 'pointer-y-ratio': 0.5, },})The value must be valid for the property’s syntax (a plain number for the default <number> typing). Defaults only apply when typed is on.
- Opt in before attaching. Call
configure({ typed: true })before your firstpropsFor()— or setdata-props-typedon<html>beforeautoruns — like the prefixes. - One-way door.
@propertyregistrations last the page’s lifetime (the spec has no unregister). Properties are still cleared from elements on teardown; only the type registration persists. - Inherited. Registrations use
inherits: true, which the container-bound sources (range,field,media) rely on. - Safe to double up. If you, or another library, already declared
@property --live-…, prop-for-that skips it rather than throwing. - Feature-detected. Where
CSS.registerPropertyis unavailable it is a no-op and properties stay untyped. Support: Chromium 85+, Safari 16.4+, Firefox 128+.
Custom syntax for your own sources
Section titled “Custom syntax for your own sources”A custom or plugin source can declare per-property typings via props, applied only when typed is on. Anything you don’t declare defaults to <number> / 0 / inherited:
register({ key: 'tilt', scope: 'element', props: { angle: { syntax: '<angle>', initial: '0deg' } }, start(ctx) { /* … ctx.write('angle', 12) … */ },})