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.
<script src="https://hayneslab.dev/widgets/team-member-grid.js" data-hl-config='[{"name":"Ava Carter","role":"Founder & CEO","photo":"https://placehold.co/200x200/f97316/ffffff?text=AC","bio":"Leads strategy and keeps the coffee coming.","linkedin":"https://www.linkedin.com/"},{"name":"Marcus Lee","role":"Lead Developer","photo":"https://placehold.co/200x200/06b6d4/ffffff?text=ML","bio":"Turns coffee into clean, shippable code.","linkedin":"https://www.linkedin.com/"},{"name":"Sofia Reyes","role":"Design Director","photo":"https://placehold.co/200x200/0f172a/ffffff?text=SR","bio":"Obsessed with pixels, grids, and good type."}]' data-hl-title="Meet the Team" async></script>
<!-- Free Team Member Grid widget by haynes lab — https://hayneslab.dev/tools/widgets/team-member-grid -->
<!-- Free Team Member Grid widget by haynes lab — https://hayneslab.dev/tools/widgets/team-member-grid -->
<script data-hl-config='[{"name":"Ava Carter","role":"Founder & CEO","photo":"https://placehold.co/200x200/f97316/ffffff?text=AC","bio":"Leads strategy and keeps the coffee coming.","linkedin":"https://www.linkedin.com/"},{"name":"Marcus Lee","role":"Lead Developer","photo":"https://placehold.co/200x200/06b6d4/ffffff?text=ML","bio":"Turns coffee into clean, shippable code.","linkedin":"https://www.linkedin.com/"},{"name":"Sofia Reyes","role":"Design Director","photo":"https://placehold.co/200x200/0f172a/ffffff?text=SR","bio":"Obsessed with pixels, grids, and good type."}]' data-hl-title="Meet the Team">
/*!
* haynes lab — Team Member Grid widget (free)
* Docs & embed code: https://hayneslab.dev/tools/widgets/team-member-grid
*/
(function () {
'use strict';
var PREFIX = 'hlw-team-member-grid';
var STYLE_ID = 'hlw-team-member-grid-styles';
function findScript() {
return document.currentScript ||
document.querySelector('script[src*="widgets/team-member-grid.js"]');
}
function injectStyles() {
if (document.getElementById(STYLE_ID)) return;
var css =
'.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
'.' + PREFIX + ' { position: relative; max-width: 960px; margin: 0 auto; padding: 24px 24px 40px;' +
' 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; }' +
'.' + PREFIX + '__title { margin: 0 0 20px; font-size: 22px; font-weight: 700;' +
' color: #111827; text-align: center; }' +
'.' + PREFIX + '__grid { display: grid; gap: 20px; }' +
'.' + PREFIX + '__card { border: 1px solid #e5e7eb; border-radius: 10px; padding: 24px 16px 20px;' +
' background: #f9fafb; text-align: center; display: flex; flex-direction: column; align-items: center; }' +
'.' + PREFIX + '__photo { width: 96px; height: 96px; border-radius: 50%; object-fit: cover;' +
' border: 2px solid #e5e7eb; margin-bottom: 12px; display: block; }' +
'.' + PREFIX + '__photo--fallback { display: flex; align-items: center; justify-content: center;' +
' background: #e5e7eb; color: #6b7280; font-size: 32px; font-weight: 700; }' +
'.' + PREFIX + '__name { font-weight: 600; font-size: 16px; color: #111827; }' +
'.' + PREFIX + '__role { font-size: 13px; color: #6b7280; margin-top: 2px; }' +
'.' + PREFIX + '__bio { margin: 10px 0 0; font-size: 14px; color: #374151; }' +
'.' + PREFIX + '__linkedin { margin-top: 12px; width: 34px; height: 34px; border-radius: 50%;' +
' border: 1px solid #e5e7eb; background: #ffffff; color: #0a66c2; display: inline-flex;' +
' align-items: center; justify-content: center; text-decoration: none; }' +
'.' + PREFIX + '__linkedin:hover { border-color: #0a66c2; }' +
'.' + PREFIX + '__linkedin:focus-visible { outline: 2px solid #0a66c2; outline-offset: 2px; }' +
'.' + PREFIX + '__linkedin svg { display: block; }' +
'.' + 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: 900px) { .' + PREFIX + '__grid { grid-template-columns: repeat(2, 1fr) !important; } }' +
'@media (max-width: 560px) { .' + PREFIX + '__grid { grid-template-columns: 1fr !important; }' +
' .' + PREFIX + ' { padding: 20px 16px 38px; } }';
var style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = css;
document.head.appendChild(style);
}
function parseMembers(script) {
var raw = script.getAttribute('data-hl-config');
if (!raw) {
console.warn('[haynes lab team-member-grid] Missing required data-hl-config attribute (JSON array of {name, role, photo, bio, linkedin}). Nothing rendered.');
return null;
}
var items;
try {
items = JSON.parse(raw);
} catch (e) {
console.warn('[haynes lab team-member-grid] data-hl-config is not valid JSON. Nothing rendered.', e);
return null;
}
if (!Array.isArray(items)) {
console.warn('[haynes lab team-member-grid] data-hl-config must be a JSON array. Nothing rendered.');
return null;
}
var valid = items.filter(function (m) {
return m && typeof m.name === 'string' && m.name;
});
if (!valid.length) {
console.warn('[haynes lab team-member-grid] data-hl-config has no valid members (each needs at least "name"). Nothing rendered.');
return null;
}
return valid;
}
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 linkedinSvg() {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('width', '16');
svg.setAttribute('height', '16');
svg.setAttribute('fill', 'currentColor');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('focusable', 'false');
var path = document.createElementNS(svgNS, 'path');
path.setAttribute('d', 'M20.45 20.45h-3.55v-5.57c0-1.33-.03-3.04-1.85-3.04-1.86 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 1 1 0-4.12 2.06 2.06 0 0 1 0 4.12zM7.12 20.45H3.56V9h3.56v11.45z');
svg.appendChild(path);
return svg;
}
function safeUrl(url) {
if (typeof url !== 'string' || !url) return null;
if (url.indexOf('https://') === 0 || url.indexOf('http://') === 0) return url;
return null;
}
function build(script) {
var items = parseMembers(script);
if (!items) return;
var columns = parseInt(script.getAttribute('data-hl-columns'), 10);
if (isNaN(columns) || columns < 1) columns = 3;
if (columns > 6) columns = 6;
var title = script.getAttribute('data-hl-title');
injectStyles();
var root = el('div', PREFIX);
root.setAttribute('role', 'region');
root.setAttribute('aria-label', title || 'Team members');
if (title) root.appendChild(el('h2', PREFIX + '__title', title));
var grid = el('div', PREFIX + '__grid');
grid.style.gridTemplateColumns = 'repeat(' + columns + ', 1fr)';
root.appendChild(grid);
items.forEach(function (m) {
var card = el('div', PREFIX + '__card');
var photo = safeUrl(m.photo);
if (photo) {
var img = el('img', PREFIX + '__photo');
img.src = photo;
img.alt = m.name;
img.loading = 'lazy';
card.appendChild(img);
} else {
var fallback = el('div', PREFIX + '__photo ' + PREFIX + '__photo--fallback', m.name.charAt(0).toUpperCase());
fallback.setAttribute('aria-hidden', 'true');
card.appendChild(fallback);
}
card.appendChild(el('div', PREFIX + '__name', m.name));
if (m.role) card.appendChild(el('div', PREFIX + '__role', String(m.role)));
if (m.bio) card.appendChild(el('p', PREFIX + '__bio', String(m.bio)));
var linkedin = safeUrl(m.linkedin);
if (linkedin) {
var link = el('a', PREFIX + '__linkedin');
link.href = linkedin;
link.target = '_blank';
link.rel = 'noopener';
link.setAttribute('aria-label', m.name + ' on LinkedIn');
link.appendChild(linkedinSvg());
card.appendChild(link);
}
grid.appendChild(card);
});
var credit = el('a', PREFIX + '__credit');
credit.appendChild(logoSvg());
credit.appendChild(document.createTextNode('Powered by haynes lab'));
credit.href = 'https://hayneslab.dev/tools/widgets/team-member-grid';
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 team-member-grid] Could not locate its own script tag. Nothing rendered.');
return;
}
build(script);
}
if (document.body) {
init();
} else {
document.addEventListener('DOMContentLoaded', init);
}
})();
</script>