The two function components a host mounts to show the Map beside its own
list: map_region/1, where the StatifierBlocksMap hook draws the
document, and description_region/1, which says in words what the
selected block is, or what the document is when nothing is selected.
They live in the StatifierBlocks.Editor.* namespace because they name
Phoenix, and that namespace is where ADR-0005 decision 1's compile guard
lives. The editor draws no map and never mounts map_region/1
(ADR-0018). It does mount description_region/1, under its canvas,
from its own document, view model, palette and selection (ADR-0005's
Amendment of 2026-09-29 on the shell arrangement); see "In the editor"
below.
What the host brings
The components keep no state. A host passes four things, and each stays the host's:
| The host's | What it is |
|---|---|
| view model | StatifierBlocks.ViewModel.build/3 of the document, which both regions read; the description region also takes the document and the palette it was built from |
| selection | the block id its list has selected, or nil; the map marks that block's box and the description region describes it |
| event names | the names its list already sends to select a block and to open an insert; a gesture on the map is sent under those names, with the payload the list sends for the same gesture |
| list | the list itself, which stays the keyboard and screen-reader path to every step |
The map region
map_region/1 renders the element the hook is attached to, inside a
region hidden from assistive technology (aria-hidden="true"): a reader
of the host's list would otherwise meet every step twice. The element
scrolls, and a browser makes a scroll box with nothing focusable in it a
Tab stop of its own, so it carries tabindex="-1"; nothing else in the
region takes focus.
The element carries what the hook reads, in the attributes it reads them
from: the graph StatifierBlocks.Map.graph/2 answers as JSON in
data-graph, with the selection marked in it; whether the page can edit
in data-editable; the list's event names in data-select-event and
data-insert-event; the element to scroll into view after an insert
armed from the map in data-insert-reveal, when the host names one; and
the description region's id, its hover layer's and its store's in
data-info-region, data-info-hover and data-info-store, when the
host names the region, which is what gives the map its hover. The
drawing goes into a child marked data-map-canvas, which LiveView
leaves alone (phx-update="ignore").
The description region
description_region/1 renders the region with aria-live="polite", so
a selection made in the list is read out, under the id the host's list
rows name with aria-describedby. Its content is rendered on the
server: the selected block's StatifierBlocks.Map.Info description, or
StatifierBlocks.Map.Info.idle/4 when nothing is selected. A block's
author-written note leads, above the built-in text (ADR-0001's Amendment
of 2026-09-28, clause 2g). It shows values and never controls. The
region's accessible name is its aria-label, "Description" unless the
host passes its own words in label.
Beside the region, and hidden, it renders the store the hook's hover
reads: one child per element the map draws, data-describes naming the
element's map id, holding the same markup the region shows for it. The
store's id is the region's with -store after it, which is the id
map_region/1 stamps when it is handed the region's.
Selection speaks, hover is silent
What the region announces changes on a selection and never on a hover.
The server writes the region, and only on a render: a row selected in
the list, or a block selected on the map, which arrives as the list's
own event. A hover is drawn in the hover layer instead, an element
beside the region and outside it, with the region's id and -hover
after it: aria-hidden="true", hidden until the hook fills it, and
left alone by LiveView (phx-update="ignore"). The two sit in one
frame, sb-map__description-frame, and while the layer is shown the
stylesheet stacks it over the region and makes the region transparent,
so the visible text is the hovered element's while the region's own
content, and its aria-live, stay as the server wrote them.
In the editor
StatifierBlocks.Editor renders description_region/1 under its
canvas with map={false}, which says no map is mounted beside the
region. The region then renders without its hover layer and without
its store, which only the Map's hook reads, and the document's idle
description leaves out its explanation, the paragraph on how to read
the map; the name, the description, what starts it and the counts
stay. A selected block's description is unchanged. The default,
map={true}, renders the region exactly as a host's page has it.
Summary
Functions
The Map's description region, its hover layer and its hidden store; see the moduledoc.
The region the StatifierBlocksMap hook draws the Map into; see the moduledoc.
Functions
@spec description_region(map()) :: Phoenix.LiveView.Rendered.t()
The Map's description region, its hover layer and its hidden store; see the moduledoc.
Attributes
id(:string) (required) - the region's id, which the host's list rows name witharia-describedby.document(StatifierBlocks.Document) (required) - the document the view model was built from.view_model(StatifierBlocks.ViewModel) (required)palette(StatifierBlocks.Palette) (required) - the palette the view model was built with.selected(:string) - the block id the host's list has selected. Defaults tonil.phrase(:any) - the host's words for event names, the functionmap_region/1takes:nilor a function of one argument; any other value raisesArgumentError. Defaults tonil.map(:boolean) - whether a map is mounted beside the region;falserenders no hover layer, no store and no how-to-read paragraph in the idle description. Defaults totrue.label(:string) - the live region's accessible name, rendered as itsaria-label; a host passes its own words for it, in its own language. Defaults to"Description".class(:string) - a class of the host's, added to the region's own. Defaults tonil.
@spec map_region(map()) :: Phoenix.LiveView.Rendered.t()
The region the StatifierBlocksMap hook draws the Map into; see the moduledoc.
Attributes
id(:string) (required) - the id of the element the hook is attached to.view_model(StatifierBlocks.ViewModel) (required)selected(:string) - the block id the host's list has selected. Defaults tonil.select_event(:string) - the event the host's list sends to select a block. Defaults tonil.insert_event(:string) - the event the host's list sends to open an insert. Defaults tonil.editable(:boolean) - whether the map's gaps and markers arm inserts. Defaults tofalse.phrase(:any) - the host's words for event names, asStatifierBlocks.Map.graph/2takes them:nilor a function of one argument; any other value raisesArgumentError. Defaults tonil.description(:string) - the id of the host'sdescription_region/1, which gives the map its hover. Defaults tonil.insert_reveal(:string) - a selector for what to scroll into view after an insert armed from the map. Defaults tonil.class(:string) - a class of the host's, added to the region's own. Defaults tonil.