Content & Media

Free Callout Box Widget

Highlight tips, warnings, and notes with a styled callout box.

The Callout Box widget renders a clean, styled note box for tips, warnings, info, and success messages anywhere in your content. It is perfect for bloggers, documentation sites, and course creators who want important asides to stand out. Drop one script tag where you want the box and set your type, title, and text with simple data attributes.

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/callout-box.js" data-hl-type="tip" data-hl-title="Pro tip" data-hl-text="Compress your images before uploading — pages that load in under 2 seconds keep far more visitors around." async></script>
<!-- Free Callout Box widget by haynes lab — https://hayneslab.dev/tools/widgets/callout-box -->
Inline (self-contained)html
<!-- Free Callout Box widget by haynes lab — https://hayneslab.dev/tools/widgets/callout-box -->
<script data-hl-type="tip" data-hl-title="Pro tip" data-hl-text="Compress your images before uploading — pages that load in under 2 seconds keep far more visitors around.">
/*!
 * haynes lab — Callout Box widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/callout-box
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-callout-box';
  var STYLE_ID = 'hlw-callout-box-styles';

  var TYPES = {
    info: { accent: '#2563eb', label: 'Info' },
    tip: { accent: '#f97316', label: 'Tip' },
    warning: { accent: '#d97706', label: 'Warning' },
    success: { accent: '#16a34a', label: 'Success' }
  };

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

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { position: relative; display: flex; gap: 12px; align-items: flex-start;' +
      ' max-width: 640px; margin: 16px 0; padding: 16px 18px 24px;' +
      ' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
      ' font-size: 15px; line-height: 1.55; color: #1f2937; background: #ffffff;' +
      ' border: 1px solid #e5e7eb; border-left: 4px solid var(--hlw-accent); border-radius: 10px; }' +
      '.' + PREFIX + '__icon { flex: 0 0 auto; width: 22px; height: 22px; margin-top: 1px;' +
      ' color: var(--hlw-accent); }' +
      '.' + PREFIX + '__icon svg { display: block; width: 22px; height: 22px; }' +
      '.' + PREFIX + '__body { flex: 1 1 auto; min-width: 0; }' +
      '.' + PREFIX + '__title { margin: 0 0 4px; font-size: 15px; font-weight: 700; color: #111827; }' +
      '.' + PREFIX + '__text { margin: 0; color: #374151; }' +
      '.' + PREFIX + '__credit { position: absolute; right: 10px; bottom: 5px; 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: 14px 14px 24px; gap: 10px; } }';
    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 iconSvg(type) {
    var svgNS = 'http://www.w3.org/2000/svg';
    var svg = document.createElementNS(svgNS, 'svg');
    svg.setAttribute('viewBox', '0 0 24 24');
    svg.setAttribute('fill', 'none');
    svg.setAttribute('stroke', 'currentColor');
    svg.setAttribute('stroke-width', '2');
    svg.setAttribute('stroke-linecap', 'round');
    svg.setAttribute('stroke-linejoin', 'round');
    svg.setAttribute('aria-hidden', 'true');
    svg.setAttribute('focusable', 'false');
    function add(tag, attrs) {
      var node = document.createElementNS(svgNS, tag);
      for (var key in attrs) node.setAttribute(key, attrs[key]);
      svg.appendChild(node);
    }
    if (type === 'tip') {
      add('path', { d: 'M9 18h6M10 21h4' });
      add('path', { d: 'M12 3a6 6 0 0 0-3.6 10.8c.7.6 1.1 1.4 1.1 2.2h5c0-.8.4-1.6 1.1-2.2A6 6 0 0 0 12 3z' });
    } else if (type === 'warning') {
      add('path', { d: 'M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z' });
      add('line', { x1: '12', y1: '9', x2: '12', y2: '13' });
      add('line', { x1: '12', y1: '17', x2: '12.01', y2: '17' });
    } else if (type === 'success') {
      add('circle', { cx: '12', cy: '12', r: '10' });
      add('path', { d: 'm8.5 12.5 2.5 2.5 4.5-5.5' });
    } else {
      add('circle', { cx: '12', cy: '12', r: '10' });
      add('line', { x1: '12', y1: '11', x2: '12', y2: '16' });
      add('line', { x1: '12', y1: '8', x2: '12.01', y2: '8' });
    }
    return svg;
  }

  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 build(script) {
    var type = (script.getAttribute('data-hl-type') || 'info').toLowerCase();
    if (!TYPES[type]) {
      console.warn('[haynes lab callout-box] Unknown data-hl-type "' + type + '" (expected info|tip|warning|success). Falling back to "info".');
      type = 'info';
    }
    var title = script.getAttribute('data-hl-title') || '';
    var text = script.getAttribute('data-hl-text') || '';
    if (!title && !text) {
      console.warn('[haynes lab callout-box] Missing data-hl-text (and data-hl-title). Nothing rendered.');
      return;
    }

    injectStyles();

    var root = el('div', PREFIX);
    root.setAttribute('role', 'note');
    root.setAttribute('aria-label', TYPES[type].label);
    root.style.setProperty('--hlw-accent', TYPES[type].accent);

    var icon = el('span', PREFIX + '__icon');
    icon.appendChild(iconSvg(type));
    root.appendChild(icon);

    var body = el('div', PREFIX + '__body');
    if (title) body.appendChild(el('p', PREFIX + '__title', title));
    if (text) body.appendChild(el('p', PREFIX + '__text', text));
    root.appendChild(body);

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/callout-box';
    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 callout-box] 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-type"info"Which style of callout to show: info, tip, warning, or success — each gets its own icon and accent color.
data-hl-title(none)Optional bold heading shown above the callout text.
data-hl-text(required)The body message of the callout box.

Frequently asked questions

How do I install the callout box?

Paste a single script tag with your data-hl-type, data-hl-title, and data-hl-text attributes wherever you want the box to appear in your page content. The widget renders inline right where the tag sits.

Can I change the colors or style?

Yes. The data-hl-type attribute (info, tip, warning, or success) controls the icon and accent color automatically, and you can override the accent with the --hlw-accent CSS custom property or your own stylesheet targeting the scoped classes.

Does it work on WordPress, Shopify, or Squarespace?

Yes — it is a single vanilla JavaScript file with no dependencies, so it works on WordPress, Shopify, Squarespace, Wix, or any plain HTML page that lets you paste a script tag.

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