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.
Pin-based annotation overlay for collaborative design review — wraps any content and adds interactive comment pins with pluggable persistence.
A web component that wraps any HTML content and adds a transparent overlay for placing review comment pins. Click anywhere in annotate mode to drop a numbered pin, then add a comment and replies. Supports pluggable persistence via memory, localStorage, or REST adapters. Designed for design reviews, sprint demos, and stakeholder presentations.
<review-surface editable adapter="local" storage-key="my-review" author="Sarah Chen"> <img src="/comps/landing-v3.png" alt="Landing page design comp" /></review-surface>
| Attribute | Type | Default | Description |
|---|---|---|---|
src |
string (URL) | — | Path to a JSON file containing pin data |
editable |
boolean | — | Enables the annotate toolbar, pin creation, and reply input |
adapter |
string | "memory" |
Persistence backend: memory, local, or rest |
endpoint |
string (URL) | — | REST endpoint URL (required when adapter="rest") |
storage-key |
string | "review-surface" |
localStorage key (when adapter="local") |
author |
string | "Anonymous" |
Default author name for new pins and replies |
compact |
boolean | — | Reduces pin size, popover width, and toolbar spacing |
show-resolved |
boolean | — | Display resolved pins (hidden by default) |
pin-count |
number | 0 |
Reflected count of visible pins (read-only) |
Add the editable attribute to show the review toolbar. Click the Annotate button to enter annotate mode — the cursor changes to a crosshair and clicking the content area places a new pin. The pin's popover opens immediately so you can type your comment.
<review-surface editable adapter="local" storage-key="sprint-4-empathy" author="Design Lead"> <empathy-map persona="Sarah Chen"> <h2 slot="title">Developer Onboarding</h2> <p slot="summary">Q1 research insights</p> <ul slot="says"><li>"I just want it to work"</li></ul> <ul slot="thinks"><li>There must be a simpler way</li></ul> <ul slot="does"><li>Searches Stack Overflow first</li></ul> <ul slot="feels"><li>frustrated</li></ul> </empathy-map></review-surface>
Press Escape to exit annotate mode or close an open popover. Pins are always clickable in both view and annotate modes.
Load pin data from a JSON file via the src attribute, or set the .pins property programmatically.
{ "pins": [ { "id": "pin-1", "x": 42.5, "y": 67.3, "text": "Fix alignment on the header", "author": "Sarah Chen", "createdAt": "2026-04-06T14:30:00Z", "resolved": false, "replies": [ { "id": "reply-1", "text": "Agreed, padding needs 8px", "author": "Tom Rivera", "createdAt": "2026-04-06T14:45:00Z" } ] } ]}
const surface = document.querySelector('review-surface'); // Add a pinconst pin = await surface.addPin({ x: 42, y: 67, text: 'Fix alignment here', author: 'Sarah'}); // Resolve itawait surface.resolvePin(pin.id); // Export all pins as JSONconst data = surface.exportPins();console.log(JSON.stringify(data, null, 2));
Adapters control how pins are persisted. Three built-in adapters are available, or provide a custom one via the .adapter property.
| Adapter | Persistence | Use Case |
|---|---|---|
memory |
Ephemeral (lost on page refresh) | Demos, throwaway reviews |
local |
Browser localStorage | Solo dev review, prototyping |
rest |
Server via REST API | Team collaboration, persistent reviews |
<review-surface editable adapter="rest" endpoint="/api/reviews/landing-page"> <img src="/comps/landing.png" alt="Landing page" /></review-surface>
The REST adapter expects standard REST endpoints: GET / to list, POST / to create, PATCH /:id to update, and DELETE /:id to remove.
const surface = document.querySelector('review-surface'); surface.adapter = { async load() { /* return Pin[] */ }, async save(pin) { /* persist and return pin with id */ }, async update(id, changes) { /* merge changes, return updated pin */ }, async remove(id) { /* delete pin */ },};
| Event | Detail | When |
|---|---|---|
review-surface:ready |
{ pinCount } |
After the component finishes rendering |
review-surface:add |
{ pin } |
A pin is created (via click or addPin()) |
review-surface:update |
{ pin, changes } |
A pin is modified (reply added, text changed, un-resolved) |
review-surface:remove |
{ pin } |
A pin is deleted |
review-surface:resolve |
{ pin } |
A pin is marked as resolved |
review-surface:select |
{ pin } |
A pin's popover is opened |
review-surface:mode |
{ mode } |
View/annotate mode is toggled ("view" or "annotate") |
| Variable | Default | Description |
|---|---|---|
--review-surface-bg |
#ffffff |
Popover and input background |
--review-surface-card |
#f8f9fa |
Toolbar and input area background |
--review-surface-text |
#1a1a1a |
Primary text color |
--review-surface-muted |
#666666 |
Secondary text (timestamps, pin count) |
--review-surface-border |
#e0e0e0 |
Popover, toolbar, and input borders |
--review-surface-accent |
#0066cc |
Focus rings, submit button, annotate mode toggle |
--review-surface-pin-bg |
#0066cc |
Pin marker background color |
--review-surface-pin-text |
#ffffff |
Pin marker text/number color |
--review-surface-pin-size |
28px |
Pin marker diameter |
--review-surface-resolved |
#16a34a |
Resolved pin and badge color |
<button> elements with aria-label, aria-expanded, and aria-haspopup="dialog"role="dialog" with aria-labelledbyrole="toolbar" with aria-labelaria-pressed to communicate statearia-live="polite" region announces pin creation, deletion, and resolutionprefers-reduced-motion: reduce<empathy-map> — Frequently wrapped for design review sessions<user-journey> — Journey maps wrapped for annotation<user-persona> — Persona cards that pair with review surfaces<impact-effort> — Prioritize feedback from review sessions<kanban-board> — Track review feedback as cards