<quadrant-grid>
Generic 2x2 quadrant grid primitive backing SWOT, stakeholder maps, and any custom 2x2 visualization.
Overview
The <quadrant-grid> component renders a configurable 2x2 grid with axis labels, low/high markers, and four titled quadrants. Children are placed by either data-quadrant (explicit bucket) or data-x / data-y (normalized coordinates that compute to the matching quadrant and pin the child as a labelled dot). Optional drag-and-drop via the draggable attribute composes <drag-surface>.
This is the primitive that backs SWOT, stakeholder-map, and other 2x2 patterns — it replaces three previously-proposed standalone components with one configurable element plus recipes.
Cartesian convention
Quadrant indices follow Cartesian numbering. q1 is top-right, q2 top-left, q3 bottom-left, q4 bottom-right:
+----------+----------+| Q2 | Q1 |+----------+----------+| Q3 | Q4 |+----------+----------+
Use data-quadrant="0".."3" on children to bucket them, or set data-x / data-y in [0..1] and the component computes the quadrant.
Attributes
| Attribute | Values | Default | Description |
|---|---|---|---|
x-label | string | — | X-axis label |
y-label | string | — | Y-axis label |
x-low | string | — | X-axis low-end marker (default: "Low") |
x-high | string | — | X-axis high-end marker (default: "High") |
y-low | string | — | Y-axis low-end marker (default: "Low") |
y-high | string | — | Y-axis high-end marker (default: "High") |
q1-label | string | — | Top-right quadrant title |
q2-label | string | — | Top-left quadrant title |
q3-label | string | — | Bottom-left quadrant title |
q4-label | string | — | Bottom-right quadrant title |
draggable | boolean | — | Enable drag-and-drop between quadrants (composes <drag-surface>) |
Child attributes
| Attribute | On | Values | Description |
|---|---|---|---|
data-quadrant | * | number | Explicit quadrant index 0..3 (Cartesian Q1..Q4) |
data-x | * | number | Normalized x coordinate 0..1; quadrant is computed from coords |
data-y | * | number | Normalized y coordinate 0..1; quadrant is computed from coords |
data-id | * | string | Stable identifier emitted in move events |
SWOT recipe
SWOT analysis is just a quadrant-grid with appropriate axis labels and quadrant titles. No new component required.
<quadrant-grid x-label="Origin" y-label="Helpfulness" x-low="Internal" x-high="External" y-low="Harmful" y-high="Helpful" q1-label="Opportunities" q2-label="Strengths" q3-label="Weaknesses" q4-label="Threats"> <p data-quadrant="1">Strong brand recognition</p> <p data-quadrant="0">Emerging AI integrations</p> <p data-quadrant="2">Limited mobile support</p> <p data-quadrant="3">Open-source competitors</p></quadrant-grid>
Stakeholder Map recipe
Power × Interest grid with labelled dots positioned at exact coordinates via data-x / data-y.
<quadrant-grid x-label="Interest" y-label="Power" q1-label="Manage Closely" q2-label="Keep Satisfied" q3-label="Monitor" q4-label="Keep Informed"> <span data-x="0.85" data-y="0.9" data-id="ceo">CEO</span> <span data-x="0.2" data-y="0.8" data-id="board">Board</span> <span data-x="0.6" data-y="0.35" data-id="users">End users</span></quadrant-grid>
Drag-and-drop
Add the draggable attribute to enable moving children between quadrants. Each quadrant becomes a <drag-surface>; the component listens for cross-surface transfers and emits quadrant-grid:move.
<quadrant-grid draggable x-label="Effort" y-label="Impact" q1-label="Big Bets" q2-label="Quick Wins" q3-label="Fill-Ins" q4-label="Money Pit"> <article class="card" data-quadrant="1" data-id="a">Refactor onboarding</article> <article class="card" data-quadrant="0" data-id="b">Mobile rewrite</article></quadrant-grid> <script> document.querySelector('quadrant-grid') .addEventListener('quadrant-grid:move', (e) => { console.log(e.detail); // { item, itemId, from, to } });</script>
Progressive Enhancement
The component layers cleanly:
- HTML. Static fallback content (a
<dl>of quadrant titles or children withdata-quadrant) remains visible without JS or CSS. - CSS. The
:not(:defined)selector keeps the host visible pre-upgrade; once defined, the grid layout applies. - JS. Adds axis labels, computes quadrants for
data-x/data-ychildren, wires drag-and-drop when requested, and firesquadrant-grid:move.
Accessibility
The grid host is a <section role="region"> with an axis-derived label. Each quadrant is its own <section> with an aria-label. Move events update a polite live region announcing the new quadrant.
Related
<impact-effort>— Specialized 2x2 with hardcoded Quick Wins / Big Bets / Fill-Ins / Money Pit labels and stable public API. Keeps its own surface;quadrant-gridcovers other 2x2 use cases.<empathy-map>— UX research 2x2 with persona context and emotion tagging. Domain-specific.<drag-surface>— DnD primitive composed whendraggableis set.