@ax-design/typed-om
v0.3.1
Published
Typed reads and change detection for CSS custom properties, for browsers with or without CSS Typed OM and @property.
Downloads
2,157
Readme
Installation
// with npm
npm install @ax-design/typed-om
// with bun
bun add @ax-design/typed-omand import it:
import { StyleSchema, StyleBinding } from '@ax-design/typed-om';Usage
CSS Typed OM and @property are not available in every browser your users have, and reading getComputedStyle by hand means parsing calc(), em and colors yourself.
To read your custom properties as numbers, describe them once with a schema and bind it to your elements.
Here's an example:
import { StyleSchema, StyleBinding } from '@ax-design/typed-om';
const schema = new StyleSchema([
{ name: '--my-radius', syntax: { type: 'length-percentage' }, initial: '0px', inherits: false, register: true },
{ name: '--my-color', syntax: { type: 'color' }, initial: 'black', inherits: true },
{ name: '--my-mode', syntax: { type: 'keyword', values: ['soft', 'hard'] }, initial: 'soft', inherits: false },
]);
// Inside your custom element, with a shadow root:
const binding = new StyleBinding(schema, this, this.shadowRoot);
binding.read();
binding.get('--my-radius'); // { px: 4, percent: 10 } for calc(10% + 4px)
binding.get('--my-color'); // [255, 0, 0, 1]
binding.get('--my-mode'); // 'soft'Values in common forms (12px, 50%, calc(50% + 4px), rgb(), #hex, 200ms, 0.2s, keywords) are parsed directly. Anything else (calc(1em + 2px), red) goes through a hidden probe element, so the browser does the math.
register: true registers the property with CSS.registerProperty where it exists, so your transitions on it interpolate. Where it does not exist, everything still works and transitions jump to the end value.
Times
A time property holds a CSS <time> and reads as a number of milliseconds, whichever unit the author wrote:
const schema = new StyleSchema([
{ name: '--my-delay', syntax: { type: 'time' }, initial: '250ms', inherits: true, register: true },
]);
// --my-delay: 0.4s
binding.get('--my-delay'); // 400A bare number is not a time, as in CSS: --my-delay: 400 keeps the initial value. calc() and other forms go through the probe like the other types.
Watching changes
binding.observe(() => {
// Something changed: read again on the next frame.
});
binding.unobserve();The detector lives in the shadow root and costs a little on every style change of the page, so attach it only while your element is visible.
Skipping properties
A property can be skipped when another property makes it irrelevant, which saves reads:
{ name: '--my-depth', syntax: { type: 'length' }, initial: '0px', inherits: false, skipWhen: { property: '--my-mode', values: ['soft'] } }Browser support
Typed OM works in browsers released since 2016 that support custom properties and Shadow DOM. Keyword values cannot be CSS-wide keywords (initial, inherit and so on), and changes of a keyword to none are not seen by the detector.
The detector sees changes of a time property only where CSS.registerProperty exists (a time can only transition as a registered property). Elsewhere read() still gives the right value: read again when you know it may have changed.
