anfra docs
anfra SDK

Page structure

Mark the parts of your page so tools can find, show and render them

Mark the parts of your page with a few HTML attributes, and the Inspect panel in anfra serve shows them as a tree: hover a part to see it on the page, click it to scroll there, or pick a spot on the page to find its part in the tree. The same marks name the parts that Anfra.render() can save as a picture, and they give you a handle for a part that you can paste into a request to a coding agent.

The markup is optional, and it costs one attribute per part, so mark every part your page draws.

Marking the parts

<main data-anfra-container="page" data-anfra-label="Overview">
  <div data-anfra-container="controls" data-anfra-label="Controls">
    <label data-anfra-block="region-picker" data-anfra-control="region">…</label>
  </div>
  <div data-anfra-container="kpis">
    <div data-anfra-block="revenue" data-anfra-query="totals">…</div>
    <div data-anfra-block="orders" data-anfra-query="totals">…</div>
  </div>
  <section data-anfra-block="trend" data-anfra-label="Revenue over time">
    <div id="trend-chart" data-anfra-query="trend"></div>
    <select data-anfra-control="grain"></select>
  </section>
</main>

Here revenue and orders are two blocks drawn by the same query, and the trend block holds both its chart and the control that belongs to it.

AttributeOnMeans
data-anfra-container="<id>"Any elementA container, which can hold anything.
data-anfra-block="<id>"Any elementA block: the smallest part a reader sees as one thing. It holds no container or block.
data-anfra-label="<text>"A container or blockIts name in the tree and in a picture's filename. Optional.
data-anfra-query="<name>"Any elementThis element is drawn by that query. Separate several names with spaces. On a block's own element, it means the whole block is drawn by that query.
data-anfra-control="<name>"Any elementThe same, for a filter or date drill.
data-anfra-render="skip"Any elementLeft out of every picture, with everything it holds, such as a download button or a spinner.

Rules

  • Ids are required, and unique per kind in the page.
  • Query and control names are the ones you gave createQuery, createFilter and createDateDrill. In a page that creates several apps, write <index>/<name> with the app's index, such as 1/trend. A bare name means app 0.
  • Every query and control marker sits in a block, or on a block's own element. A control that stands alone, such as a filter in a toolbar, gets a block of its own. A control that belongs to one chart, such as a grain switch in the chart's header, goes in that chart's block.
  • Mistakes such as an unknown name, a duplicate id, or a block inside a block are shown on the part in the Inspect panel's tree, never thrown.
  • Elements your page adds later, such as a table drawn on the first result, appear once they exist.
  • Nothing reads the markup until a tool needs it: the Inspect panel while it is open, or Anfra.render() when it looks up a block or container. Marking every part costs your page nothing at load.

Finding a query on the page

query.locate() and control.locate() scroll to the elements marked with that query or control, and highlight them for a moment. Use them for a "find on page" button of your own. They return false when nothing is marked yet, and never throw.

Handles for a coding agent

When you pick a part in the Inspect panel, you can copy its handle, such as [Revenue over time](data-anfra-block="trend"), and paste it into a request to a coding agent. The part in parentheses is the attribute as written in the file, so the agent can search for it and change the element you mean.

On this page