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 FAQ Accordion widget by haynes lab — https://hayneslab.dev/tools/widgets/faq-accordion -->
<script data-hl-config='[{"question":"Do you offer free estimates?","answer":"Yes — every project starts with a free, no-obligation estimate. Most quotes are delivered within one business day."},{"question":"What areas do you serve?","answer":"We serve the greater Charlotte metro area, including Concord, Gastonia, Huntersville, and Rock Hill."},{"question":"Are you licensed and insured?","answer":"Fully licensed and insured in North and South Carolina, with certificates available on request."}]'>
/*!
* haynes lab — FAQ Accordion widget (free)
* Docs & embed code: https://hayneslab.dev/tools/widgets/faq-accordion
*/
(function () {
'use strict';
var ROOT_CLASS = 'hlw-faq-accordion';
var STYLE_ID = 'hlw-faq-accordion-styles';
var instanceCount = 0;
function injectStyles() {
if (document.getElementById(STYLE_ID)) return;
var style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = [
'.' + ROOT_CLASS + ', .' + ROOT_CLASS + ' * { box-sizing: border-box; margin: 0; padding: 0; }',
'.' + ROOT_CLASS + ' { font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.5; color: #1f2937; max-width: 720px; width: 100%; }',
'.' + ROOT_CLASS + '__title { font-size: 22px; font-weight: 700; line-height: 1.3; color: #111827; margin-bottom: 16px; }',
'.' + ROOT_CLASS + '__item { border: 1px solid #e5e7eb; border-radius: 10px; background: #ffffff; margin-bottom: 10px; overflow: hidden; }',
'.' + ROOT_CLASS + '__question { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 14px 16px; border: 0; background: transparent; font: inherit; font-weight: 600; font-size: 15px; color: #111827; text-align: left; cursor: pointer; }',
'.' + ROOT_CLASS + '__question:hover { color: var(--hlw-accent, #f97316); }',
'.' + ROOT_CLASS + '__question:focus-visible { outline: 2px solid var(--hlw-accent, #f97316); outline-offset: -2px; }',
'.' + ROOT_CLASS + '__icon { flex: 0 0 auto; width: 18px; height: 18px; color: var(--hlw-accent, #f97316); transition: transform 0.2s ease; }',
'.' + ROOT_CLASS + '__question[aria-expanded="true"] .' + ROOT_CLASS + '__icon { transform: rotate(45deg); }',
'.' + ROOT_CLASS + '__answer { display: none; padding: 0 16px 16px; font-size: 15px; color: #4b5563; }',
'.' + ROOT_CLASS + '__answer--open { display: block; }',
'.' + ROOT_CLASS + '__credit { display: block; text-align: right; font-size: 11px; margin-top: 6px; }',
'.' + ROOT_CLASS + '__credit a { display: inline-flex; align-items: center; gap: 4px; color: #9ca3af; text-decoration: none; }',
'.' + ROOT_CLASS + '__credit a svg { display: block; }',
'.' + ROOT_CLASS + '__credit a:hover { text-decoration: underline; }'
].join('\n');
document.head.appendChild(style);
}
function findScript() {
if (document.currentScript) return document.currentScript;
return document.querySelector('script[src*="widgets/faq-accordion.js"]');
}
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 parseItems(raw) {
if (!raw) return null;
try {
var data = JSON.parse(raw);
if (!Array.isArray(data) || data.length === 0) return null;
var items = [];
for (var i = 0; i < data.length; i++) {
var entry = data[i] || {};
if (entry.question && entry.answer) {
items.push({ question: String(entry.question), answer: String(entry.answer) });
}
}
return items.length ? items : null;
} catch (err) {
console.warn('[haynes lab faq-accordion] data-hl-config is not valid JSON:', err);
return null;
}
}
function init() {
var script = findScript();
if (!script) return;
var attr = function (name) { return script.getAttribute(name); };
var items = parseItems(attr('data-hl-config'));
if (!items) {
console.warn('[haynes lab faq-accordion] Missing or invalid required attribute data-hl-config (JSON array of {"question","answer"}). Nothing rendered.');
return;
}
var title = attr('data-hl-title');
if (title === null) title = 'Frequently asked questions';
var openFirst = attr('data-hl-open-first') !== 'false';
var accent = attr('data-hl-accent') || '#f97316';
injectStyles();
var root = document.createElement('div');
root.className = ROOT_CLASS;
root.style.setProperty('--hlw-accent', accent);
instanceCount += 1;
var idBase = ROOT_CLASS + '-' + instanceCount;
if (title !== '') {
var h = document.createElement('h2');
h.className = ROOT_CLASS + '__title';
h.textContent = title;
root.appendChild(h);
}
items.forEach(function (item, i) {
var wrap = document.createElement('div');
wrap.className = ROOT_CLASS + '__item';
var open = openFirst && i === 0;
var answerId = idBase + '-answer-' + i;
var btn = document.createElement('button');
btn.type = 'button';
btn.className = ROOT_CLASS + '__question';
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
btn.setAttribute('aria-controls', answerId);
btn.setAttribute('aria-label', item.question);
var label = document.createElement('span');
label.textContent = item.question;
btn.appendChild(label);
var icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
icon.setAttribute('class', ROOT_CLASS + '__icon');
icon.setAttribute('viewBox', '0 0 24 24');
icon.setAttribute('fill', 'none');
icon.setAttribute('aria-hidden', 'true');
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', 'M12 5v14M5 12h14');
path.setAttribute('stroke', 'currentColor');
path.setAttribute('stroke-width', '2.5');
path.setAttribute('stroke-linecap', 'round');
icon.appendChild(path);
btn.appendChild(icon);
var answer = document.createElement('div');
answer.className = ROOT_CLASS + '__answer' + (open ? ' ' + ROOT_CLASS + '__answer--open' : '');
answer.id = answerId;
answer.setAttribute('role', 'region');
answer.textContent = item.answer;
btn.addEventListener('click', function () {
var expanded = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
answer.classList.toggle(ROOT_CLASS + '__answer--open', !expanded);
});
wrap.appendChild(btn);
wrap.appendChild(answer);
root.appendChild(wrap);
});
var credit = document.createElement('span');
credit.className = ROOT_CLASS + '__credit';
var link = document.createElement('a');
link.href = 'https://hayneslab.dev/tools/widgets/faq-accordion';
link.target = '_blank';
link.rel = 'noopener';
link.appendChild(logoSvg());
link.appendChild(document.createTextNode('Powered by haynes lab'));
credit.appendChild(link);
root.appendChild(credit);
if (script.parentNode && script.parentNode.nodeName.toLowerCase() !== 'head') {
script.insertAdjacentElement('afterend', root);
} else {
document.body.appendChild(root);
}
}
if (document.body) {
init();
} else {
document.addEventListener('DOMContentLoaded', init);
}
})();
</script>