feedback-popup
v5.0.0
Published
A simple to use popup for collecting feedback from users about issues with the site that they are using. Currently it captures a screenshot of the users browser, the users OS and browser name + version and also a message from the user. This is all then se
Maintainers
Readme
feedback-popup
Feedback Popup is a simple to use popup for collecting feedback from users about issues with the site that they are using. It captures a screenshot of the user's browser, the user's OS and browser name + version, and also a message from the user. This is all then sent to an API, where you can do whatever you like with the information.
More features to come!
Table of Contents
- Installation
- Breaking changes in v4
- Breaking changes in v5 (CSS theming)
- Usage
- Styling & Theming
- Configuration Options
- API
- Development
- New-Features
- Contributing
Installation
npm install feedback-popup
# or
yarn add feedback-popup
# or
pnpm add feedback-popupBreaking changes in v4
- DOM setup runs in
init(), not in the constructor. The constructor only stores configuration; the firstinit()call creates or finds the widget root, injects inner placeholder elements if needed, renders the floating button, and binds events. - Calling
init()more than once on the same instance is ignored (with a console warning). - Recommended integration is an explicit
mount(selector string orHTMLElement) or no HTML at all (the library appends a root todocument.body). Relying on a pre-placed.js-feedback-popupnode in your markup still works in v4 but is deprecated and will be removed in a future major version.
Breaking changes in v5 (CSS theming)
These changes apply when you adopt the v5 stylesheet (namespaced theming). Bump your major version when you ship this CSS to consumers.
- Theming is done via
--feedback-*CSS variables on.feedback-popup(or any ancestor of the widget). You do not need to override:rootfor the popup look. .btn,.control, and.spinnerare scoped under.feedback-popup. Styles from this package no longer apply to generic.btn/.control/.spinnerclasses elsewhere on the page. If you relied on those global rules, add your own styles for those classes outside the widget.- Checkbox styling uses
color-mix()for semi-transparent states. Use a browser that supportscolor-mixor override the--feedback-checkbox-*variables with solid colors.
Usage
Recommended: explicit mount
Place a single empty container where you want the widget to live (layout, sidebar, footer, etc.). You can use a CSS selector or pass the element directly (e.g. from a framework ref). Missing widget classes and data-html2canvas-ignore on that node are added for you when init() runs.
<body>
<div id="main-body"><!-- page content --></div>
<div id="feedback-root"></div>
<script type="module">
import FeedbackPopup from 'feedback-popup';
const feedbackPopup = new FeedbackPopup({
mount: '#feedback-root',
widgetTitle: 'Send Feedback',
title: 'Help Us Improve',
snapshotBodyId: '#main-body',
placeholderText: 'Tell us what you think...',
endpointUrl: 'https://your-api.com/feedback'
});
feedbackPopup.init();
</script>
</body>With a direct element reference:
import FeedbackPopup from 'feedback-popup';
const root = document.getElementById('feedback-root');
const feedbackPopup = new FeedbackPopup({
mount: root,
snapshotBodyId: '#main-body'
});
feedbackPopup.init();Zero markup: auto-inject on document.body
If you omit mount and there is no .js-feedback-popup in the document, init() creates a root element, adds the feedback-popup and js-feedback-popup classes, sets data-html2canvas-ignore, and appends it to document.body. This is ideal for quick demos; the widget usually appears at the end of the body.
import FeedbackPopup from 'feedback-popup';
const feedbackPopup = new FeedbackPopup({
snapshotBodyId: '#main-body',
endpointUrl: 'https://your-api.com/feedback'
});
feedbackPopup.init();Legacy: pre-built .js-feedback-popup in HTML (deprecated)
If you already ship the old wrapper and inner placeholder divs, you can still omit mount and init() will use the first matching .js-feedback-popup. Migrate to mount or auto-inject when you can; this pattern will be removed in a future major release.
<div class="feedback-popup js-feedback-popup" data-html2canvas-ignore="true">
<div class="js-feedback-popup-btn-show"></div>
<div class="js-feedback-popup-content"></div>
<div class="js-feedback-popup-confirmation"></div>
</div>Styling & Theming
The widget root is always an element with class feedback-popup (added automatically by init() if missing). All public theme tokens are CSS custom properties prefixed with --feedback-, with defaults set on .feedback-popup in src/styles/variables.css.
Loading the CSS
The published npm package ships the JS library from dist/; stylesheet files live in the repo under src/styles/. Include them in your app (copy, submodule, or bundle main.css and its imports) the same way you do today. The entry file is src/styles/main.css.
How to override
Add rules that target the widget root (or a wrapper you pass as mount) and set variables:
.feedback-popup {
--feedback-color-primary: #5c6bc0;
--feedback-color-brand: #283593;
--feedback-header-bg: var(--feedback-color-brand);
--feedback-widget-button-bg: var(--feedback-color-primary);
--feedback-widget-button-hover-bg: #3949ab;
}Because variables inherit, you can also set them on a parent container if the widget is nested:
#feedback-root.feedback-popup,
#feedback-root .feedback-popup {
--feedback-dialog-width: 36rem;
}Variables reference (defaults in variables.css)
| Variable | Role |
|----------|------|
| Typography | |
| --feedback-font-family | Font stack for widget UI |
| --feedback-font-size-header | Modal title |
| --feedback-font-size-body | Body / textarea / confirmation text |
| --feedback-font-size-widget | Floating button label |
| --feedback-font-size-screenshot-label | “Include a screenshot?” row |
| --feedback-font-weight-header | Title weight |
| --feedback-font-weight-body | Body weight |
| --feedback-font-weight-label | Checkbox label weight |
| --feedback-line-height-header | Title line height |
| Core colors | |
| --feedback-color-surface | White / surfaces |
| --feedback-color-text | Main text |
| --feedback-color-text-muted | Placeholder |
| --feedback-color-brand | Brand / header |
| --feedback-color-primary | Primary actions, widget button |
| --feedback-color-secondary | Screenshot row background |
| --feedback-color-on-primary | Text on primary-colored bars |
| --feedback-color-on-brand | Text on header |
| --feedback-color-disabled | Disabled / muted UI |
| --feedback-color-link | Links in confirmation text |
| --feedback-color-link-hover | Link hover |
| Overlay | |
| --feedback-overlay-bg | Backdrop behind modal |
| --feedback-overlay-z-index | Stacking order |
| --feedback-overlay-transition | Backdrop transition |
| Floating button | |
| --feedback-widget-offset-right | Horizontal inset from right |
| --feedback-widget-offset-bottom | Offset from bottom |
| --feedback-widget-width | Button strip width |
| --feedback-widget-min-height | Min height |
| --feedback-widget-padding-x / --feedback-widget-padding-y | Padding |
| --feedback-widget-button-bg | Button background |
| --feedback-widget-button-text | Button label color |
| --feedback-widget-button-hover-bg | Hover background |
| --feedback-widget-button-disabled-bg | Disabled background |
| Dialog | |
| --feedback-dialog-bg | Panel background |
| --feedback-dialog-width | Panel width |
| --feedback-dialog-max-height-offset | calc(100% - offset) on tall viewports |
| --feedback-dialog-max-height-offset-mobile | Same under 575px width |
| --feedback-dialog-border-radius | Panel and confirmation card corner radius |
| Header | |
| --feedback-header-bg | Header bar |
| --feedback-header-text | Title color |
| --feedback-header-height | Bar height |
| --feedback-header-padding-x | Horizontal padding |
| Textarea | |
| --feedback-textarea-bg | Textarea area background |
| --feedback-textarea-height | Textarea block height |
| --feedback-textarea-padding-x / --feedback-textarea-padding-y | Inner padding |
| Screenshot UI | |
| --feedback-add-screenshot-bg | Blue bar behind checkbox |
| --feedback-add-screenshot-text | Label color on that bar |
| --feedback-add-screenshot-height | Row height |
| --feedback-add-screenshot-padding | Row padding |
| --feedback-screenshot-area-bg | Canvas preview area |
| --feedback-screenshot-area-height | Preview height |
| Footer | |
| --feedback-footer-bg | Row behind Send/Cancel |
| --feedback-footer-padding | Footer padding |
| Confirmation card | |
| --feedback-confirmation-bg | Thank-you card background |
| --feedback-confirmation-width / --feedback-confirmation-height | Card size |
| --feedback-confirmation-padding-x / --feedback-confirmation-padding-y | Card padding |
| --feedback-confirmation-thank-you-margin-bottom | Space below thank-you line |
| Dialog buttons | |
| --feedback-button-radius | Border radius |
| --feedback-button-confirm-bg / --feedback-button-confirm-border / --feedback-button-confirm-text | Send button |
| --feedback-button-confirm-hover-bg / --feedback-button-confirm-hover-text | Send hover |
| --feedback-button-cancel-bg / --feedback-button-cancel-border / --feedback-button-cancel-text | Cancel |
| --feedback-button-cancel-hover-bg | Cancel hover |
| --feedback-button-dialog-text / --feedback-button-dialog-font-size | Alternate dialog-style button (e.g. .btn-diolog) |
| --feedback-button-padding-x / --feedback-button-padding-y | Action button padding |
| --feedback-button-gap | Space between Send and Cancel |
| --feedback-button-font-size | Button font size |
| --feedback-button-transition | Button transitions |
| --feedback-button-text-transform | e.g. uppercase |
| Checkbox | |
| --feedback-checkbox-label-font-size | Label size |
| --feedback-checkbox-indicator-size | Box size |
| --feedback-checkbox-indicator-border | Border color |
| --feedback-checkbox-indicator-radius | Radius |
| --feedback-checkbox-indicator-default / hover / checked / checked-hover / disabled | Indicator backgrounds (color-mix by default) |
| --feedback-checkbox-check-color | Checkmark color |
| --feedback-checkbox-check-disabled-border | Checkmark border when disabled |
| Spinner | |
| --feedback-spinner-size | Diameter |
| --feedback-spinner-border-width | Ring thickness |
| --feedback-spinner-track-color / --feedback-spinner-accent-color | Two-tone ring |
| --feedback-spinner-animation-duration | Rotation speed |
Examples
Dark header and primary accent
.feedback-popup {
--feedback-color-brand: #1a237e;
--feedback-color-primary: #ff6f00;
--feedback-header-bg: var(--feedback-color-brand);
--feedback-widget-button-bg: var(--feedback-color-primary);
--feedback-widget-button-hover-bg: #ff8f00;
--feedback-button-confirm-bg: var(--feedback-color-primary);
--feedback-button-confirm-border: var(--feedback-color-primary);
}Wider dialog, more padding
.feedback-popup {
--feedback-dialog-width: 40rem;
--feedback-textarea-height: 24rem;
--feedback-header-padding-x: 2rem;
--feedback-footer-padding: 1.2rem;
}Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| mount | string | HTMLElement | (none) | Root element for the widget (selector or element). If omitted, the first .js-feedback-popup is used, or a new root is appended to document.body. |
| widgetTitle | string | 'Feedback' | The title shown on the feedback button |
| title | string | 'Send Feedback' | The title of the feedback popup |
| snapshotBodyId | string | '#main-body' | CSS selector for the element to capture in the screenshot |
| placeholderText | string | 'Enter your feedback here...' | Placeholder text for the feedback textarea |
| endpointUrl | string | 'http://localhost:3005/api/feedback' | API endpoint to send feedback to |
API
Methods
init(): Initialize the feedback popup (DOM scaffold, button, event listeners). Safe to call once per instance.showFeedbackModal(): Show the feedback popuphideContentDiv(): Hide the feedback popupcreateScreenshot(): Create a screenshot of the current pagesendData(): Send feedback data to the configured endpoint
Development
# Install dependencies
pnpm install
# Start development server
pnpm start
# Run tests
pnpm test
# Build for production
pnpm buildLicense
MIT
New Features
There is a TODO.md with the current plan of new features, updates etc... that are being checked off as I get to them. Submit a PR if you want to add any suggestions.
Contributing
Clone this project to get involved
[email protected]:TommyScribble/feedback-popup.gitPrerequisites
Node.js =22.14.0 must be installed. If you are using Volta this is already pinned.
Installation
- Running
pnpm iin the app's root directory will install everything you need for development.
Development Server
pnpm run devwill run the app's development server at http://localhost:3000 and a devlopment API at http://localhost:3005, automatically reloading the page on every JS change.
Dev API
This api sends the body of the request to the feedback folder. This is excluded by the gitignore and will be generated if it doesnt exist. To clean the folder run
pnpm run clean-fedbackTesting
Vitest is used to test all functionality. To run all the tests run
pnpm run testBuilding
To test builds locally run
pnpm run buildThis will first delete and then build the output to the dist directory
pnpn run cleanwill delete built resources.
