Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
Generic 2x2 quadrant grid primitive backing SWOT, stakeholder maps, and any custom 2x2 visualization.
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.
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.
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>
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>
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>
The component layers cleanly:
<dl> of quadrant titles or children with data-quadrant) remains visible without JS or CSS.:not(:defined) selector keeps the host visible pre-upgrade; once defined, the grid layout applies.data-x / data-y children, wires drag-and-drop when requested, and fires quadrant-grid:move.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.
<impact-effort> — Specialized 2x2 with hardcoded Quick Wins / Big Bets / Fill-Ins / Money Pit labels and stable public API. Keeps its own surface; quadrant-grid covers other 2x2 use cases.<empathy-map> — UX research 2x2 with persona context and emotion tagging. Domain-specific.<drag-surface> — DnD primitive composed when draggable is set.