A lightweight, customizable pop-up widget for advertising messages and announcements. Built on vanilla JavaScript, with no dependencies and using Shadow DOM for style isolation.
Try the live demo and configure the widget directly in your browser: https://js-popup-sales.pages.dev/
You are also welcome to use the production-ready script hosted on Cloudflare: https://js-popup-sales.pages.dev/js-popup-sales.js
- π¨ Customizable themes β Light, Dark, or Auto (system preference)
- π 9 position options β Center, corners, and edges
- π 2 layouts β Vertical (image on top) or Horizontal (image on left)
- βοΈ Markdown support β Bold, italic, strikethrough, links
- π GTM integration β dataLayer events for analytics
- β¨οΈ Accessibility β ARIA attributes, focus trap, keyboard navigation
- π― Multiple triggers β Delay, scroll percentage, exit-intent, manual
- π Shadow DOM β Isolated styles, no CSS conflicts
- π± Responsive β Mobile-friendly with landscape support
- Add build script to
package.json:
{
"scripts": {
"build:js-popup-sales": "vite build --config vite.js-popup-sales.config.ts"
}
}- Run the build:
npm run build- Output:
dist-js-popup-sales/js-popup-sales.js
Upload js-popup-sales.js to your CDN or static hosting.
<script>
window.JSPopupSalesConfig = {
trigger: "delay",
delay: 3000,
dismissDays: 7,
title: "Don't lose customers! π",
subtitle: "**AIbizMate** helps you find _missed leads_ in your inbox",
features: ["β
Automatic scanning", "π€ AI-powered analysis", "π§ Instant notifications"],
ctaText: "Find for free",
ctaUrl: "https://aibizmate.com",
theme: "light",
position: "center",
layout: "vertical",
inheritFont: false,
buttonColor: "#f97316",
backgroundColor: "#ffffff",
textColor: "#1a1a1a",
buttonRadius: 10,
contentAlign: "left",
enableTracking: true,
popupId: "js_popup_sales",
closeOnCtaClick: true,
debug: false
};
</script>
<script src="https://yourdomain.com/js-popup-sales.js"></script><script
src="https://yourdomain.com/js-popup-sales.js"
data-js-popup-sales
data-trigger="delay"
data-delay="3000"
data-title="Special Offer! π"
data-subtitle="Get 50% off today"
data-cta-text="Shop Now"
data-cta-url="https://example.com"
data-theme="auto"
data-position="bottom-right"
></script>| Parameter | Type | Default | Description |
|---|---|---|---|
trigger |
string | "delay" |
"delay", "scroll", "exit-intent", "manual" |
delay |
number | 3000 |
Delay in ms (100-60000) |
scrollPercent |
number | 50 |
Scroll percentage trigger (1-100) |
dismissDays |
number | 7 |
Days to hide after dismiss (0 = always show) |
title |
string | "Don't lose customers! π" |
Popup title (supports markdown) |
subtitle |
string | β | Subtitle text (supports markdown) |
features |
string[] | β | List of features (supports markdown) |
ctaText |
string | "Find for free" |
CTA button text |
ctaUrl |
string | β | CTA button URL |
image |
string | β | Image URL |
theme |
string | "light" |
"light", "dark", "auto" |
position |
string | "center" |
See positions below |
layout |
string | "vertical" |
"vertical", "horizontal" |
inheritFont |
boolean | false |
Use website font instead of Inter |
buttonColor |
string | "#f97316" |
Button color (HEX, RGB, HSL) |
backgroundColor |
string | "#ffffff" |
Popup background color |
textColor |
string | "#1a1a1a" |
Text color |
buttonRadius |
number | 10 |
Button border radius in px (0-100) |
contentAlign |
string | "left" |
"left", "center", "right" |
enableTracking |
boolean | false |
Enable GTM dataLayer events |
popupId |
string | "js_popup_sales" |
Unique ID for tracking |
closeOnCtaClick |
boolean | true |
Close popup when CTA clicked |
debug |
boolean | false |
Enable console logging |
center(default)top-left,top-center,top-rightcenter-left,center-rightbottom-left,bottom-center,bottom-right
When theme: "auto", the widget detects system preference using prefers-color-scheme media query:
- Dark mode system β Dark theme
- Light mode system β Light theme (default)
The widget supports a subset of Markdown in title, subtitle, features, and CTA text:
| Syntax | Result |
|---|---|
**text** |
bold |
_text_ |
italic |
**_text_** |
bold italic |
~~text~~ |
|
[text](url) |
link |
| Emoji | πβ¨π (native support) |
Note: *text* is NOT supported for italic. Use _text_ instead.
Execute custom JavaScript when popup events occur:
window.JSPopupSalesConfig = {
// ...other options
onShow: () => {
console.log('Popup shown!');
},
onHide: () => {
console.log('Popup hidden!');
},
onCtaClick: () => {
gtag('event', 'cta_click', { popup_id: 'my_popup' });
}
};// Show popup
showJSPopupSales();
// Show with custom config override
showJSPopupSales({ title: 'New Title!' });
// Hide popup
hideJSPopupSales();
// Dismiss popup (respects dismissDays)
dismissJSPopupSales();// Access the auto-initialized instance
jsPopupSalesInstance.show();
jsPopupSalesInstance.hide();
jsPopupSalesInstance.dismiss();
// Create a new instance manually
const popup = new JSPopupSales({ trigger: 'manual', title: 'Hello!' });
popup.init();
popup.show();When enableTracking: true, these events are pushed to window.dataLayer:
| Event | Description | Extra Data |
|---|---|---|
js_popup_sales_shown |
Popup displayed | β |
js_popup_sales_primary_click |
CTA button clicked | β |
js_popup_sales_closed |
Popup closed | close_type: "cross", "outside", "escape" |
js_popup_sales_error |
Error occurred | error_type, error_message |
All events include popup_id for identification.
- Create a new Custom HTML tag
- Paste the code with
window.JSPopupSalesConfig - Set trigger (e.g., "All Pages" or specific page)
- Publish container
Create GTM variables (Constants or Data Layer Variables) and use them:
<script>
window.JSPopupSalesConfig = {
title: "{{Popup Title}}",
ctaUrl: "{{Popup CTA URL}}",
delay: {{Popup Delay}}
};
</script>
<script src="https://yourdomain.com/js-popup-sales.js"></script>Enable debug mode to see detailed logs in browser console:
window.JSPopupSalesConfig = {
debug: true,
// ...other options
};When disabled (default), no console logs are produced in production.
| Layout | Recommended Size | Aspect Ratio |
|---|---|---|
| Vertical | 480 Γ 200 px | 2.4:1 |
| Horizontal | 150 Γ 350 px | 1:2.3 |
- ARIA
role="dialog"andaria-modal="true" aria-labelledbyreferences popup title- Focus trap when popup is open
Escapekey closes popupprefers-reduced-motionsupport- Links have
rel="noopener noreferrer"
- Chrome 60+
- Firefox 55+
- Safari 11+
- Edge 79+
- Check browser console for errors
- Enable
debug: trueto see logs - Reset dismiss:
localStorage.removeItem('js_popup_sales_dismissed') - Verify script URL is accessible
- The widget uses Shadow DOM β external CSS cannot affect it
- Check if
inheritFont: trueis causing font issues - Verify custom colors are valid CSS colors
- Data attributes don't work in GTM β use
window.JSPopupSalesConfig - Ensure script loads after config is set
- Check GTM preview mode for tag firing
MIT License with restrictions on sale. See LICENSE for details.
You are free to use, modify, and distribute this software, but you may not sell it as a standalone product.
The project was vibecoded with Lovable)