Authoring across surfaces

Three surfaces, one contract. A body authored in plain HTML, as a web component, or in React is the same body in the field — each is just a different way to write the [data-body] contract the scanner reads. Pick the surface your stack already uses; field-ui does not ask you to adopt a framework.

A body, three ways

Each of these authors one attractor of strength 0.8. They compile to identical bodies.

<!-- the page is the medium; any element can be a body -->
<canvas id="field"></canvas>

<article data-body="attract" data-strength="0.8">
  Pulls matter inward
</article>

<script type="module">
  import { createField } from 'field-ui';
  // mounts the field and scans the page for [data-body] elements
  createField(document.getElementById('field'));
</script>
The same body — attract, strength 0.8 — across all three surfaces.

Live

These cards carry real data-body attributes, so the page's field reacts to them right now. Move your pointer near one and watch the field gather around it.

attractpulls matter inward
repelpushes matter away
swirlorbits matter around

The shared contract

Every surface writes the same data-* attributes. This is the contract:

AttributeMeaningExample
data-bodyforce tokens, space-separatedattract, repel swirl
data-strengthsource mass / pull (default 0.5)0.8
data-rangereach in px (default 280)220
data-preseta named bundle of virtual bodiesdipole
data-intentauthored intent, compiled to forcesgather
data-field-rolesemantic role → a default forcesource, sink, anchor

Beneath the surfaces: the platform

The surfaces above author bodies. When you need the page itself to participate — to measure elements, hold state, and write feedback on a disciplined loop — reach for @field-ui/platform directly. It is framework-agnostic; the same code runs under any surface.

TypeScript
import { createFieldPlatform } from '@field-ui/platform';

// the substrate beneath every surface: measure → state → write, on one scheduler
const platform = createFieldPlatform(document.querySelector('article'));
const card = document.querySelector('[data-body]');

platform.measure.register(card);                          // read phase
platform.feedback.bind(card, { attention: '--field-attention' }); // write phase
platform.on('compute', () => {                            // compute phase
  const m = platform.measure.for(card);
  if (m) platform.state.set(card, 'attention', m.visibilityRatio);
});

const tick = (t) => { platform.tick(t); requestAnimationFrame(tick); };
requestAnimationFrame(tick);
The measure → state → write loop, on the six-phase scheduler. See the Reading Field demo for a full page built on it.