Examples

Live examples

Every demo here is real, grouped by what it shows. Reciprocity: the field reacts to elements and writes density back. Layout as physics: the same springs and repulsion, applied to where elements sit. Field control: driving the field from your own events. Each comes with its source.

Reciprocity

A glowing hero word

The canonical reciprocity effect. The word is an attract body with data-feedback; its --d drives weight and glow, so it grows where matter gathers. Sweep your cursor across it.

gravity
HTML + CSS
<span
  class="hero"
  data-body="attract"
  data-strength="1"
  data-range="340"
  data-feedback
>gravity</span>

<style>
  .hero {
    --d: 0;  /* the field eases this in ∈ [0,1] */
    font-weight: 700;
    font-variation-settings: 'wght' calc(380 + var(--d) * 420);
    color: color-mix(in srgb, #4da3ff calc(40% + var(--d) * 55%), #fff);
    text-shadow: 0 0 calc(var(--d) * 44px) rgba(77, 163, 255, var(--d));
  }
</style>

A reacting card grid

Each card is a gentle attractor. Its --d drives a subtle lift, edge glow, and shadow — the grid breathes as the field moves through it.

Reciprocal the field bends to elements; elements bend back
Conserved nothing is created from nothing — matter is moved
Physical one field, not a pool of decorative particles
HTML + CSS
<article class="card" data-body="attract" data-strength="0.6" data-range="220" data-feedback>
  …card…
</article>

<style>
  .card {
    --d: 0;
    transform: scale(calc(1 + var(--d) * 0.03));
    border-color: color-mix(in srgb, #4da3ff calc(var(--d) * 75%), rgba(255,255,255,0.1));
    box-shadow: 0 0 calc(var(--d) * 30px) rgba(77, 163, 255, calc(var(--d) * 0.5));
    transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
  }
</style>

Conserved emphasis

One finite attention budget across the page (setAttention). Focusing one item lifts it and makes the others recede by the same total — emphasis you can't fake with hover styles, because the page can never over-emphasize more than the budget allows. Hover or tab through the list, then flip the budget off to feel the difference.

  • Designing with forcewhy every element carries weight
  • The reciprocal fieldelements bend it; it bends them back
  • Conservation as a lawnothing created from nothing
  • Reading as attentiona budget you spend by looking
  • Layout as physicsposition is an equilibrium, not a value
HTML + CSS + JS
<ul class="reading-list">
  <li data-hot data-feedback data-body="attract" data-strength="1.1" data-range="240">
    Designing with force
  </li>
  <!-- …more items, each its own attractor… -->
</ul>

<script type="module">
  const field = document.querySelector('field-root');
  field.setAttention(true);   // ONE conserved budget across the page:
                              // focusing one body starves the others
</script>

<style>
  .reading-list li {
    --d: 0;                                  /* the field eases this in ∈ [0,1] */
    opacity: calc(0.4 + var(--d) * 0.6);     /* idle recedes, focus rises */
    font-weight: calc(400 + var(--d) * 320);
    transition: opacity .25s, font-weight .25s;
  }
</style>
Layout as physics

Self-spacing labels

Each chip is a data-move="layout" body: anchored to where it belongs, repelling its neighbours, drifting off dense field regions. Drop them in a loose pile and they spread to even spacing and re-settle when disturbed — collision-free placement with no layout code. Click the stage to shove them.

HTML
<div class="tag-cloud">
  <span class="tag" data-move="layout">design</span>
  <span class="tag" data-move="layout">physics</span>
  <span class="tag" data-move="layout">layout</span>
  <!-- …more chips, all stacked at the same point… -->
</div>

<style>
  .tag-cloud { position: relative; min-height: 260px; }
  .tag {
    position: absolute; left: 50%; top: 50%;   /* all start at one point */
    margin: -1.05em 0 0 -3em;                  /* rough centring */
    will-change: transform;                    /* the engine adds the offset */
  }
</style>

<!-- data-move="layout": each chip anchors to home, repels its neighbours,
     and drifts off dense field regions. Stacked at one point, the cluster
     spreads itself to even spacing and re-settles on disturbance or resize.
     No layout code. -->

Magnetic snapping

The same spring that powers the tether force, applied to an element's transform. Drag the tile anywhere — on release it springs to the nearest grid cell. A damped spring, not a stepped round(), so it carries momentum and settles.

// The tether force, applied to an element's transform instead of a particle:
//   v += (target − x) · k;   v *= damping;   x += v
// Critically damped (k≈0.18, damping≈0.72) → it settles without overshoot.
function springTo(el, tx, ty) {
  const s = (el._s ||= { x: 0, y: 0, vx: 0, vy: 0 });
  s.tx = tx; s.ty = ty;
  const tick = () => {
    s.vx = (s.vx + (s.tx - s.x) * 0.18) * 0.72;
    s.vy = (s.vy + (s.ty - s.y) * 0.18) * 0.72;
    s.x += s.vx; s.y += s.vy;
    el.style.transform = `translate(${s.x}px, ${s.y}px)`;
    if (Math.hypot(s.tx - s.x, s.ty - s.y) > 0.15) requestAnimationFrame(tick);
  };
  tick();
}

Spring reflow

Layout transitions as physics. When the order changes, each chip springs from its old position to its new one — the FLIP technique, but the Play step is the tether spring instead of a CSS curve, so the grid settles instead of snapping. Shuffle it.

attract repel swirl stream tether gravity charge thermal collide diffuse wind align
JavaScript
// FLIP, but the Play step is a spring instead of a CSS curve.
function reflow(items, mutate) {
  const first = items.map((el) => el.getBoundingClientRect());
  mutate();                                   // reorder / filter the DOM
  items.forEach((el, i) => {
    const last = el.getBoundingClientRect();
    el._s.x += first[i].left - last.left;     // invert: jump back to the old spot
    el._s.y += first[i].top - last.top;
    springTo(el, 0, 0);                        // play: spring to the new spot
  });
}
Field control

Click to burst

Drive the field from your own events. The FieldHandle's burst takes viewport coordinates — click anywhere in the stage to shove and heat the matter there.

JavaScript
const field = document.querySelector('field-root');

stage.addEventListener('click', (e) => {
  field.burst(e.clientX, e.clientY, '#4da3ff');  // shove + heat at the cursor
});
Want the recipes as a reference? The copy-paste-first versions live on the Recipes page; these examples are the same patterns, running.