E-commerce & Retail

Free Promo Code Reveal Widget

A click-to-reveal coupon that shows and copies your promo code.

A small inline card that hides your discount code behind a "Reveal code" button. One click reveals the code and copies it to the clipboard, with an optional link straight to your shop. Great for stores and landing pages — paste one script tag where you want it to appear and set your code in an attribute.

Live demo

Embed this widget

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.

Hosted (recommended)html
<script src="https://hayneslab.dev/widgets/promo-code-reveal.js" data-hl-code="SUMMER25" data-hl-discount="25% off your first order" data-hl-link="https://hayneslab.dev/tools" async></script>
<!-- Free Promo Code Reveal widget by haynes lab — https://hayneslab.dev/tools/widgets/promo-code-reveal -->
Inline (self-contained)html
<!-- Free Promo Code Reveal widget by haynes lab — https://hayneslab.dev/tools/widgets/promo-code-reveal -->
<script data-hl-code="SUMMER25" data-hl-discount="25% off your first order" data-hl-link="https://hayneslab.dev/tools">
/*!
 * haynes lab — Promo Code Reveal widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/promo-code-reveal
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-promo-code-reveal';
  var STYLE_ID = 'hlw-promo-code-reveal-styles';

  function findScript() {
    return document.currentScript ||
      document.querySelector('script[src*="widgets/promo-code-reveal.js"]');
  }

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { position: relative; max-width: 420px; margin: 0 auto; padding: 24px 20px 34px;' +
      ' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
      ' font-size: 16px; line-height: 1.5; color: #1f2937; background: #ffffff;' +
      ' border: 1px solid #e5e7eb; border-radius: 12px; text-align: center; }' +
      '.' + PREFIX + '__discount { display: block; font-size: 20px; font-weight: 700;' +
      ' color: var(--hlw-accent); margin-bottom: 4px; }' +
      '.' + PREFIX + '__hint { display: block; font-size: 13px; color: #6b7280; margin-bottom: 14px; }' +
      '.' + PREFIX + '__code { display: flex; align-items: center; justify-content: center; gap: 10px;' +
      ' padding: 12px 14px; border: 2px dashed var(--hlw-accent); border-radius: 8px;' +
      ' background: rgba(0, 0, 0, 0.03); margin-bottom: 14px; }' +
      '.' + PREFIX + '__value { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;' +
      ' font-size: 18px; font-weight: 600; letter-spacing: 2px; color: #111827; }' +
      '.' + PREFIX + '__btn { display: inline-block; border: 0; border-radius: 8px; padding: 11px 22px;' +
      ' font: inherit; font-size: 15px; font-weight: 600; cursor: pointer;' +
      ' background: var(--hlw-accent); color: #ffffff; }' +
      '.' + PREFIX + '__btn:hover { filter: brightness(0.92); }' +
      '.' + PREFIX + '__btn:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
      '.' + PREFIX + '__btn--copy { background: transparent; color: var(--hlw-accent);' +
      ' border: 1px solid var(--hlw-accent); padding: 9px 18px; font-size: 14px; }' +
      '.' + PREFIX + '__btn--copy:hover { filter: none; background: rgba(0, 0, 0, 0.04); }' +
      '.' + PREFIX + '__link { display: inline-block; margin-left: 12px; font-size: 14px; font-weight: 600;' +
      ' color: var(--hlw-accent); text-decoration: none; }' +
      '.' + PREFIX + '__link:hover { text-decoration: underline; }' +
      '.' + PREFIX + '__credit { position: absolute; right: 10px; bottom: 6px; font-size: 11px;' +
      ' display: inline-flex; align-items: center; gap: 4px; color: #9ca3af; text-decoration: none; }' +
      '.' + PREFIX + '__credit svg { display: block; }' +
      '.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }' +
      '@media (max-width: 480px) { .' + PREFIX + ' { padding: 20px 14px 32px; }' +
      ' .' + PREFIX + '__link { display: block; margin: 10px 0 0; } }';
    var style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = css;
    document.head.appendChild(style);
  }

  function el(tag, className, text) {
    var node = document.createElement(tag);
    if (className) node.className = className;
    if (text != null) node.textContent = text;
    return node;
  }

  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 copyText(text, done) {
    function fallback() {
      var ta = document.createElement('textarea');
      ta.value = text;
      ta.setAttribute('readonly', '');
      ta.style.position = 'absolute';
      ta.style.left = '-9999px';
      document.body.appendChild(ta);
      ta.select();
      var ok = false;
      try { ok = document.execCommand('copy'); } catch (e) { /* ignore */ }
      document.body.removeChild(ta);
      done(ok);
    }
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(text).then(function () { done(true); }, fallback);
    } else {
      fallback();
    }
  }

  function build(script) {
    var code = script.getAttribute('data-hl-code');
    if (!code) {
      console.warn('[haynes lab promo-code-reveal] Missing required data-hl-code attribute. Nothing rendered.');
      return;
    }
    var discount = script.getAttribute('data-hl-discount') || '';
    var link = script.getAttribute('data-hl-link') || '';
    var bg = script.getAttribute('data-hl-bg') || '#ffffff';
    var accent = script.getAttribute('data-hl-accent') || '#f97316';

    injectStyles();

    var root = el('div', PREFIX);
    root.style.setProperty('--hlw-accent', accent);
    root.style.background = bg;

    if (discount) root.appendChild(el('span', PREFIX + '__discount', discount));
    var hint = el('span', PREFIX + '__hint', 'Click below to reveal your promo code');
    root.appendChild(hint);

    var codeBox = el('div', PREFIX + '__code');
    codeBox.setAttribute('aria-live', 'polite');
    var value = el('span', PREFIX + '__value', code.replace(/./g, '•'));
    codeBox.appendChild(value);
    root.appendChild(codeBox);

    var revealed = false;
    var revealBtn = el('button', PREFIX + '__btn', 'Reveal code');
    revealBtn.type = 'button';
    root.appendChild(revealBtn);

    function showCopied(btn) {
      var original = 'Copy code';
      btn.textContent = 'Copied!';
      setTimeout(function () { btn.textContent = original; }, 2000);
    }

    revealBtn.addEventListener('click', function () {
      if (!revealed) {
        revealed = true;
        value.textContent = code;
        root.removeChild(revealBtn);
        hint.textContent = 'Code copied — paste it at checkout';

        var copyBtn = el('button', PREFIX + '__btn ' + PREFIX + '__btn--copy', 'Copy code');
        copyBtn.type = 'button';
        copyBtn.setAttribute('aria-label', 'Copy promo code ' + code);
        copyBtn.addEventListener('click', function () {
          copyText(code, function (ok) { if (ok) showCopied(copyBtn); });
        });
        root.appendChild(copyBtn);

        if (link) {
          var shop = el('a', PREFIX + '__link', 'Shop now →');
          shop.href = link;
          shop.target = '_blank';
          shop.rel = 'noopener';
          root.appendChild(shop);
        }

        // One click both reveals and copies, so the code is ready to paste at checkout.
        copyText(code, function (ok) { if (ok) showCopied(copyBtn); });
      }
    });

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/promo-code-reveal';
    credit.target = '_blank';
    credit.rel = 'noopener';
    root.appendChild(credit);

    if (script.parentNode && script.parentNode.nodeName !== 'HEAD') {
      script.insertAdjacentElement('afterend', root);
    } else {
      document.body.appendChild(root);
    }
  }

  function init() {
    var script = findScript();
    if (!script) {
      console.warn('[haynes lab promo-code-reveal] Could not locate its own script tag. Nothing rendered.');
      return;
    }
    build(script);
  }

  if (document.body) {
    init();
  } else {
    document.addEventListener('DOMContentLoaded', init);
  }
})();

</script>

Configuration

Every option is an attribute on the script tag — the same attributes the customizer above exposes. All attributes are optional unless marked required — defaults apply when omitted.

AttributeDefaultDescription
data-hl-code(required)The coupon code visitors reveal and copy, e.g. "SUMMER25".
data-hl-discount(none)Optional short label shown above the code, like "25% off" or "Free shipping".
data-hl-link(none)Optional URL for a "Shop now" link shown after the code is revealed.
data-hl-bg"#ffffff"Any CSS color for the card background.
data-hl-accent"#f97316"Any CSS color used for the button, code border, and discount label.

Frequently asked questions

How do I install the promo code reveal?

Copy the embed snippet and paste it where you want the coupon card to appear — it renders inline at that spot. Set data-hl-code to your actual promo code and you are done.

How do I customize the code, colors, and link?

Everything is controlled through data-hl-* attributes on the script tag itself. Set data-hl-code for the coupon, data-hl-discount for a label like "25% off", data-hl-link for an optional shop link, and data-hl-bg / data-hl-accent for the colors. No CSS editing required.

Does it work on WordPress, Shopify, or Squarespace?

Yes. The widget is a single dependency-free vanilla JS file, so it runs anywhere you can paste a script tag — WordPress (Custom HTML block or code snippet plugin), Shopify (theme.liquid or a page), Squarespace (code injection), Webflow, or plain HTML.

More free widgets

Need a custom widget?

We build custom widgets, integrations, and whole products. Tell us what your site needs and we'll scope it.

Get in Touch