Workshop Kit · widget reference
Shippedpositioning-plotter
A 2D control for placing a brand position on configurable axes. Mouse-drag the handle, or arrow-key it (Shift for big jumps, Home/End/PageUp/Down for the edges). A plain-language readout below the grid translates coordinates into the tier they map to. Used in L6a to plot cuisine breadth × price tier.
Live demo
Mouse-drag the handle, or tab into the grid and use the arrow keys. The readout under the grid names the tier the position maps to — that's the operator-facing string the widget commits, not raw coordinates.
Cuisine breadth × price tier — the L6a configuration
Contract
Drop a <section> with the six axis-config attributes and the widget renders the 2D control. No JSON config block, no inline items list. The widget always writes to positioning in MuntinContext.
| Attribute | Type | Purpose |
|---|---|---|
data-widgetRequired |
literal | Always "positioning-plotter". |
data-x-axisRequired |
string | Name of the X axis (rendered in plain language in the readout). |
data-x-low / data-x-highRequired |
string | Endpoint labels for the X axis. These render as tier names in the readout (low end = X near 0, high end = X near 1). |
data-y-axisRequired |
string | Name of the Y axis. |
data-y-low / data-y-highRequired |
string | Endpoint labels for the Y axis. Low end = bottom of the grid (Y near 1; the grid is in display coordinates), high end = top. |
| All six axis attributes | -es variants |
Each axis attribute takes an optional -es sibling for Spanish copy (e.g., data-x-axis-es="Amplitud de cocina"). The widget picks based on <html lang> or data-locale. |
What it writes: positioning in MuntinContext as { x: number, y: number, label: string }. x and y are floats 0..1 rounded to three decimals; label is the operator-facing tier name (e.g., "$$$$ upscale / Single specialty").
Markup
Accessibility
Keyboard-only operation is first-class — the widget started life as an HTML5 drag-and-drop, got rebuilt twice, and now ships as a plain <button> with a custom keyboard model. Reasons in the source comments; summary:
- The handle is a real
<button>— focusable, hover/focus states from the user agent's defaults. - Arrow keys move the handle by 1% of the grid per press.
- Shift + arrow moves by 10%.
- Home / End move to the X-axis edges (X = 0 or X = 1, Y unchanged).
- PageUp / PageDown move to the Y-axis edges (Y = 0 or Y = 1, X unchanged).
- aria-describedby points at the readout below the grid, so screen readers hear the tier name in plain language after every move ("$$$$ upscale / Single specialty"). The X/Y coordinates aren't announced — operators don't care about 0.347; they care about which tier their position lands in.
- The role is the default button — NOT
role="slider", which would imply a single-axis range. A 2D control with two simultaneous values doesn't fit the slider pattern's mental model.
Where it ships
Source
/tools/_shared/workshop/positioning-plotter.js — ~280 LOC. Exports { tag, contextKeys, mount, serialize } per the Workshop Kit widget contract. Reads the six axis-config attributes (+ optional -es variants), renders the grid + handle + readout, wires the mouse-drag and keyboard handlers.
Tier-name derivation: the X axis quartiles map to data-x-low (X < 0.5) and data-x-high (X ≥ 0.5); same for Y. The readout joins them as "{Y-tier} / {X-tier}". The lesson author owns the readout vocabulary by picking the four endpoint strings.