Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.
<!-- Free Email Signup Form widget by haynes lab — https://hayneslab.dev/tools/widgets/email-signup -->
<script data-hl-action="https://formspree.io/f/your-form-id" data-hl-title="Get one practical growth tip every week" data-hl-placeholder="[email protected]" data-hl-success-message="You're in — check your inbox to confirm.">
/*!
* haynes lab — Email Signup Form widget (free)
* Docs & embed code: https://hayneslab.dev/tools/widgets/email-signup
*/
(function () {
'use strict';
var script = document.currentScript ||
document.querySelector('script[src*="widgets/email-signup.js"]');
function getConfig() {
var d = script && script.dataset ? script.dataset : {};
var extra = {};
if (script && script.getAttribute('data-hl-config')) {
try {
var parsed = JSON.parse(script.getAttribute('data-hl-config'));
if (parsed && typeof parsed === 'object') extra = parsed;
} catch (e) {
console.warn('[hlw-email-signup] Ignoring malformed JSON in data-hl-config.');
}
}
function pick(key, camel, fallback) {
if (script && script.getAttribute(key) != null) return script.getAttribute(key);
if (extra[camel] != null) return String(extra[camel]);
return fallback;
}
return {
action: pick('data-hl-action', 'action', ''),
title: pick('data-hl-title', 'title', 'Join the list'),
placeholder: pick('data-hl-placeholder', 'placeholder', '[email protected]'),
buttonText: pick('data-hl-button-text', 'buttonText', 'Subscribe'),
successMessage: pick('data-hl-success-message', 'successMessage', "Thanks — you're on the list!"),
bg: pick('data-hl-bg', 'bg', '#0f0a0a'),
accent: pick('data-hl-accent', 'accent', '#f97316')
};
}
var cfg = getConfig();
if (!cfg.action) {
console.warn('[hlw-email-signup] data-hl-action is required (URL the form POSTs to). Widget not rendered.');
return;
}
function injectStyles() {
if (document.getElementById('hlw-email-signup-styles')) return;
var css =
'.hlw-email-signup, .hlw-email-signup * { box-sizing: border-box; margin: 0; padding: 0; }' +
'.hlw-email-signup { all: initial; display: block; max-width: 460px; width: 100%;' +
' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
' font-size: 16px; line-height: 1.5; border-radius: 12px; padding: 24px;' +
' background: #0f0a0a; color: #f5f5f5; box-shadow: 0 4px 24px rgba(0,0,0,.18); }' +
'.hlw-email-signup-title { display: block; font-size: 1.15em; font-weight: 700; line-height: 1.35; margin-bottom: 14px; }' +
'.hlw-email-signup-row { display: flex; gap: 8px; flex-wrap: wrap; }' +
'.hlw-email-signup-input { flex: 1 1 180px; min-width: 0; font: inherit; font-size: .95em; padding: 10px 12px;' +
' border: 1px solid rgba(128,128,128,.45); border-radius: 8px; background: rgba(255,255,255,.08); color: inherit; }' +
'.hlw-email-signup-input::placeholder { color: rgba(160,160,160,.8); }' +
'.hlw-email-signup-input:focus { outline: 2px solid rgba(128,128,128,.6); outline-offset: 1px; }' +
'.hlw-email-signup-btn { flex: 0 0 auto; font: inherit; font-size: .95em; font-weight: 600; padding: 10px 18px;' +
' border: 0; border-radius: 8px; background: #f97316; color: #fff; cursor: pointer; }' +
'.hlw-email-signup-btn:hover { filter: brightness(1.08); }' +
'.hlw-email-signup-btn:disabled { opacity: .6; cursor: default; }' +
'.hlw-email-signup-msg { display: none; font-size: .88em; margin-top: 12px; }' +
'.hlw-email-signup-msg[data-show] { display: block; }' +
'.hlw-email-signup-msg[data-kind="error"] { color: #fca5a5; }' +
'.hlw-email-signup-credit { display: block; text-align: right; margin-top: 12px; }' +
'.hlw-email-signup-credit a { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: rgba(160,160,160,.75); text-decoration: none; }' +
'.hlw-email-signup-credit a svg { display: block; }' +
'.hlw-email-signup-credit a:hover { text-decoration: underline; }';
var style = document.createElement('style');
style.id = 'hlw-email-signup-styles';
style.textContent = css;
(document.head || document.documentElement).appendChild(style);
}
function logoSvg() {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('viewBox', '0 0 32 32');
svg.setAttribute('width', '13');
svg.setAttribute('height', '13');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('focusable', 'false');
function circle(cx, cy, r, attrs) {
var c = document.createElementNS(svgNS, 'circle');
c.setAttribute('cx', cx);
c.setAttribute('cy', cy);
c.setAttribute('r', r);
for (var key in attrs) c.setAttribute(key, attrs[key]);
svg.appendChild(c);
}
circle(16, 16, 16, { fill: '#0f0a0a' });
circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
circle(16, 16, 3, { fill: '#06b6d4' });
circle(24.5, 7.5, 2, { fill: '#f97316' });
circle(7, 16, 1.8, { fill: '#f97316' });
circle(20, 20, 1.5, { fill: '#f97316' });
return svg;
}
function buildWidget() {
var root = document.createElement('div');
root.className = 'hlw-email-signup';
root.style.background = cfg.bg;
root.style.setProperty('--hlw-accent', cfg.accent);
var title = document.createElement('span');
title.className = 'hlw-email-signup-title';
title.textContent = cfg.title;
var form = document.createElement('form');
form.noValidate = true;
var row = document.createElement('div');
row.className = 'hlw-email-signup-row';
var input = document.createElement('input');
input.className = 'hlw-email-signup-input';
input.type = 'email';
input.name = 'email';
input.placeholder = cfg.placeholder;
input.required = true;
input.setAttribute('aria-label', 'Email address');
var btn = document.createElement('button');
btn.className = 'hlw-email-signup-btn';
btn.type = 'submit';
btn.textContent = cfg.buttonText;
btn.style.background = cfg.accent;
btn.setAttribute('aria-label', cfg.buttonText);
row.appendChild(input);
row.appendChild(btn);
form.appendChild(row);
var msg = document.createElement('p');
msg.className = 'hlw-email-signup-msg';
msg.setAttribute('role', 'status');
msg.setAttribute('aria-live', 'polite');
var credit = document.createElement('span');
credit.className = 'hlw-email-signup-credit';
var link = document.createElement('a');
link.href = 'https://hayneslab.dev/tools/widgets/email-signup';
link.target = '_blank';
link.rel = 'noopener';
link.appendChild(logoSvg());
link.appendChild(document.createTextNode('Powered by haynes lab'));
credit.appendChild(link);
function showMsg(text, kind) {
msg.textContent = text;
msg.setAttribute('data-kind', kind);
msg.setAttribute('data-show', '');
}
form.addEventListener('submit', function (e) {
e.preventDefault();
var email = input.value.trim();
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
showMsg('Please enter a valid email address.', 'error');
input.focus();
return;
}
btn.disabled = true;
var body = new FormData();
body.append('email', email);
fetch(cfg.action, { method: 'POST', body: body, headers: { Accept: 'application/json' } })
.then(function (res) {
if (res.ok) {
form.style.display = 'none';
showMsg(cfg.successMessage, 'success');
} else {
btn.disabled = false;
showMsg('Something went wrong — please try again.', 'error');
}
})
.catch(function () {
btn.disabled = false;
showMsg('Something went wrong — please try again.', 'error');
});
});
root.appendChild(title);
root.appendChild(form);
root.appendChild(msg);
root.appendChild(credit);
return root;
}
function mount() {
injectStyles();
var el = buildWidget();
if (script && script.parentNode) {
script.insertAdjacentElement('afterend', el);
} else {
document.body.appendChild(el);
}
}
if (document.body) {
mount();
} else {
document.addEventListener('DOMContentLoaded', mount);
}
})();
</script>