Cookbook

Chrome & accessibility

Navigation is the hardest surface to put a field on, because it is the one that absolutely must work when the field does not. The same discipline that makes chrome safe — signals only, meaning in the DOM, a plain fallback by construction — is what makes any field accessible.

Shipped · demo runs live on this page

bindFieldNav is the idiom this site spread across a dozen surfaces — top nav, sidebar, breadcrumbs, pagers, footer — lifted into the platform. It runs a Pattern signals-only over the links inside a root: each becomes a body, the current one can be pinned as the well, and previously-visited links can be marked from a predicate you supply. Nothing is drawn, and the visit log stays yours.

import { bindFieldNav } from '@fundamental-engine/dom';

// Signals-only over real links: every <a> becomes a body, the current link is pinned
// as the well, and "visited" is a predicate YOU own. Nothing is drawn.
const handle = bindFieldNav(nav, 'navigation-current', {
  pin: nav.querySelector('[aria-current="page"]'),
  visited: (href) => visitLog.has(href),
});

// Returns NULL under prefers-reduced-motion, with no links, or on an unknown pattern —
// so the failure mode is "plain nav", never "broken nav".
handle?.destroy();
A nav bound as a field live on this page

starting…

These are ordinary links: click them and they navigate. The weight and ink come from the attention lane the binding writes; the trailing dot marks a 'visited' route from this page's own predicate.

Progressive enhancement is the contract, not a courtesy. Under prefers-reduced-motion, with no links, or with a Pattern it cannot resolve, bindFieldNav returns null and writes nothing. There is no degraded state to design, because the undegraded state is the HTML.

The three rules that keep a field accessible

  1. The canvas carries nothing. It is aria-hidden, not focusable and click-through by construction. If a reader would lose information with the canvas removed, the information is in the wrong place.
  2. Meaning lives in the DOM. The field measures semantic HTML and writes numbers back onto it. Assistive technology reads the same heading it always did; the field only changes how emphatically it is drawn.
  3. Reduced motion keeps the meaning. The engine stops integrating under prefers-reduced-motion, and engagement still works through focus as well as hover. What you build on top must degrade to a static equivalent — weight, ink, a border — not to nothing.
<!-- The canvas is decorative by construction: aria-hidden, not focusable,
     click-through. Meaning never lives in it. -->
<field-root></field-root>

<!-- The meaning lives in the DOM, where assistive tech already reads it.
     The field AMPLIFIES the heading; it does not carry it. -->
<h2 data-body="attract" data-feedback>Q3 sales</h2>

The platform's own guarantees, and a side-by-side of the animated field against its static fallback, are on the accessibility page and its preview.

Checking your own surface

  • Turn the field off. Every docs page here has a toggle for exactly this reason: the page must still be complete.
  • Turn motion off. Then re-read the page and ask what information you lost.
  • Tab through it. Focus must engage a body the way hover does.
  • Run the lint. lintPlatform() catches the feedback-lane mistakes — a body that writes a variable nothing reads, or CSS reading one nothing writes.