Four files, zero dependencies
| File | Purpose |
|---|---|
feedback-widget.js | The widget itself. Three FABs (Pin, Highlight, My Feedback), two modes, html2canvas capture, slide-in submit panel, left-side My Feedback drawer, localStorage persistence, API POST/PATCH/DELETE. |
feedback-replay.js | Replay receiver. Reads URL params set by the review page and renders the pin/highlight overlay + detail card on the original dashboard. |
feedback-review.html | Admin review page. Stats, breakdown cards, rich feedback cards with thumbnails, lightbox, replay links, CSV export. |
index.html | This documentation page. |
Add three script tags to any dashboard
<!-- 1. Configure (set before both scripts) --> <script> window.FEEDBACK_CONFIG = { dashboardTitle : 'Asia-Eurasia GTM · CEO Dashboard · 2026-05-12', accentColor : '#c0563f', // matches your --accent CSS variable modes : ['pin', 'highlight'], // or just one }; </script> <!-- 2. Widget — floating FABs + panel + capture --> <script src="/feedback-widget.js"></script> <!-- 3. Replay receiver — activates only when ?fbw_replay= is in URL --> <script src="/feedback-replay.js"></script>
## Feedback Widget
Every dashboard MUST include before `</body>`:
```html
<script>
window.FEEDBACK_CONFIG = {
dashboardTitle : '{{DASHBOARD_TITLE}}',
accentColor : '{{ACCENT_COLOR}}',
modes : ['pin', 'highlight'],
};
</script>
<script src="https://YOUR-PROJECT-URL/feedback-widget.js"></script>
<script src="https://YOUR-PROJECT-URL/feedback-replay.js"></script>
```
Placeholders to fill in per dashboard:
- `{{DASHBOARD_TITLE}}` → the full <title> of the dashboard
- `{{ACCENT_COLOR}}` → hex value of the primary accent colour (--accent)
The script URLs are absolute and always point back to the central
feedback project. All responses are stored in one place and readable
at: https://YOUR-PROJECT-URL/feedback-review.html
<!-- Feedback widget — add before </body> --> <script> window.FEEDBACK_CONFIG = { dashboardTitle : '{{DASHBOARD_TITLE}}', accentColor : '{{ACCENT_COLOR}}', modes : ['pin', 'highlight'], }; </script> <script src="https://YOUR-PROJECT-URL/feedback-widget.js"></script> <script src="https://YOUR-PROJECT-URL/feedback-replay.js"></script>
FEEDBACK_CONFIG options
| Key | Type | Default | Description |
|---|---|---|---|
dashboardTitle | string | document.title | Stored with every record and shown in review page |
accentColor | string | #c0563f | FAB color, highlight outline, selected states |
modes | string[] | ['pin','highlight'] | Controls which FABs appear. Pass a single-element array for A/B testing. |
What gets captured per submission
| Field | Description |
|---|---|
mode | pin or highlight |
reaction | spot-on · needs-context · data-issue · suggestion |
comment | Free text, up to 2000 chars |
reviewer_name | Optional, defaults to "Anonymous" |
target_label | Human-readable heading of the targeted element |
target_selector | CSS selector path — used by replay mode to re-highlight the element |
target_text | Text content snippet of the element (first 200 chars) |
pin_x / pin_y | Coordinates as % of full page (0–100) — resolution-independent |
scroll_x / scroll_y | Scroll position at time of feedback |
viewport_w / viewport_h | Window size at time of feedback |
screenshot_crop | Base64 PNG of the targeted area (via html2canvas, with fallback) |
element_rect | JSON bounding rect {top, left, width, height} |
Submitters can manage their own feedback
After submitting, the viewer's feedback ID is saved to localStorage in their browser.
The 📋 My Feedback FAB (bottom of the stack) opens a left-side drawer listing all
submissions they made from that browser — across page refreshes.
Each card in the drawer supports three actions:
| Action | What it does | API call |
|---|---|---|
| Edit | Inline textarea replaces the comment text. Saves on button click. | PATCH /api/feedback/:id — only the comment field is updated; reaction, coordinates, screenshot are immutable. |
| Delete | Confirmation step inside the card, then soft-deletes the record. Card animates out. | DELETE /api/feedback/:id — sets deleted = true in the database. |
| View | Screenshot thumbnail, reaction emoji, element label, mode tag, and submission date shown per card. | GET /api/feedback/:id — fetched in parallel on drawer open. |
IDs that return 404 (already deleted, or from another device) are silently pruned from localStorage. The badge on the FAB reflects the live count of IDs stored on that browser.
To open the drawer programmatically:
window.FeedbackWidget.openMyFeedback();
How "Replay in dashboard" works
From the review page, every feedback card has a Replay in dashboard button. It opens the original dashboard URL with special query params appended:
/your-dashboard.html ?fbw_replay=<record_id> &fbw_x=<pin_x_pct> &fbw_y=<pin_y_pct> &fbw_sel=<css_selector> &fbw_mode=<pin|highlight>
feedback-replay.js detects these params, fetches the record from the API,
and renders a red banner at the top of the page, a pin dot or highlight ring
at the stored coordinates, and a floating detail card showing the reaction,
screenshot crop, and comment — anchored near the targeted element.
Press Esc or click "Exit replay" to dismiss and clean the URL.
Ready to read the responses?
Stats · Per-dashboard breakdown · Lightbox · Replay links · CSV export