E-commerce & Retail

Free Size Chart Table Widget

A clean apparel size chart with a built-in in/cm unit toggle.

Size Chart Table renders a mobile-friendly size chart from a simple JSON config of columns and rows, with a one-tap toggle that converts every measurement between inches and centimeters. It is built for apparel, footwear, and gear stores that want to cut down on returns and sizing questions. Paste one script tag where the chart should appear and you are done.

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/size-chart-table.js" data-hl-config='{"columns":["Size","Chest","Body Length","Sleeve Length"],"rows":[["S","35-37","27","31.5"],["M","38-40","28","32.5"],["L","41-43","29","33.5"],["XL","44-46","30","34.5"],["XXL","47-49","31","35.5"]]}' data-hl-title="Men’s T-Shirt Size Chart" async></script>
<!-- Free Size Chart Table widget by haynes lab — https://hayneslab.dev/tools/widgets/size-chart-table -->
Inline (self-contained)html
<!-- Free Size Chart Table widget by haynes lab — https://hayneslab.dev/tools/widgets/size-chart-table -->
<script data-hl-config='{"columns":["Size","Chest","Body Length","Sleeve Length"],"rows":[["S","35-37","27","31.5"],["M","38-40","28","32.5"],["L","41-43","29","33.5"],["XL","44-46","30","34.5"],["XXL","47-49","31","35.5"]]}' data-hl-title="Men’s T-Shirt Size Chart">
/*!
 * haynes lab — Size Chart Table widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/size-chart-table
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-size-chart-table';
  var STYLE_ID = 'hlw-size-chart-table-styles';
  var IN_PER_CM = 2.54;

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

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { position: relative; max-width: 640px; margin: 0 auto; padding: 24px 24px 32px;' +
      ' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
      ' font-size: 15px; line-height: 1.5; color: #1f2937; background: #ffffff;' +
      ' border: 1px solid #e5e7eb; border-radius: 12px; }' +
      '.' + PREFIX + '__header { display: flex; align-items: center; justify-content: space-between;' +
      ' flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }' +
      '.' + PREFIX + '__title { margin: 0; font-size: 18px; font-weight: 700; color: #111827; }' +
      '.' + PREFIX + '__units { display: inline-flex; border: 1px solid #e5e7eb; border-radius: 999px;' +
      ' overflow: hidden; background: #f9fafb; }' +
      '.' + PREFIX + '__unit { border: 0; padding: 6px 14px; font-size: 13px; font-weight: 600;' +
      ' font-family: inherit; color: #6b7280; background: transparent; cursor: pointer; }' +
      '.' + PREFIX + '__unit[aria-pressed="true"] { background: var(--hlw-accent); color: #ffffff; }' +
      '.' + PREFIX + '__unit:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: -2px; }' +
      '.' + PREFIX + '__scroll { overflow-x: auto; }' +
      '.' + PREFIX + '__table { width: 100%; border-collapse: collapse; }' +
      '.' + PREFIX + '__table caption { caption-side: bottom; text-align: left; font-size: 12px;' +
      ' color: #9ca3af; padding-top: 8px; }' +
      '.' + PREFIX + '__table th, .' + PREFIX + '__table td { padding: 10px 12px; text-align: center;' +
      ' border-bottom: 1px solid #e5e7eb; white-space: nowrap; }' +
      '.' + PREFIX + '__table thead th { font-size: 12px; font-weight: 700; text-transform: uppercase;' +
      ' letter-spacing: 0.04em; color: #ffffff; background: var(--hlw-accent); border-bottom: 0; }' +
      '.' + PREFIX + '__table thead th:first-child { border-radius: 8px 0 0 0; }' +
      '.' + PREFIX + '__table thead th:last-child { border-radius: 0 8px 0 0; }' +
      '.' + PREFIX + '__table tbody th { text-align: left; font-weight: 600; color: #111827; }' +
      '.' + PREFIX + '__table tbody tr:nth-child(even) { background: #f9fafb; }' +
      '.' + PREFIX + '__table tbody tr:last-child th, .' + PREFIX + '__table tbody tr:last-child td { border-bottom: 0; }' +
      '.' + 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: 560px) { .' + PREFIX + ' { padding: 18px 14px 30px; }' +
      ' .' + PREFIX + '__table th, .' + PREFIX + '__table td { padding: 8px 8px; } }';
    var style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = css;
    document.head.appendChild(style);
  }

  function parseChart(script) {
    var raw = script.getAttribute('data-hl-config');
    if (!raw) {
      console.warn('[haynes lab size-chart-table] Missing required data-hl-config attribute (JSON {"columns":[...],"rows":[[...]]}). Nothing rendered.');
      return null;
    }
    var data;
    try {
      data = JSON.parse(raw);
    } catch (e) {
      console.warn('[haynes lab size-chart-table] data-hl-config is not valid JSON. Nothing rendered.', e);
      return null;
    }
    if (!data || !Array.isArray(data.columns) || !data.columns.length ||
        !Array.isArray(data.rows) || !data.rows.length) {
      console.warn('[haynes lab size-chart-table] data-hl-config needs a non-empty "columns" array and "rows" array. Nothing rendered.');
      return null;
    }
    var columns = data.columns.map(String);
    var rows = data.rows
      .filter(function (r) { return Array.isArray(r) && r.length; })
      .map(function (r) {
        var cells = r.slice(0, columns.length).map(function (c) { return String(c); });
        while (cells.length < columns.length) cells.push('');
        return cells;
      });
    if (!rows.length) {
      console.warn('[haynes lab size-chart-table] data-hl-config has no valid rows. Nothing rendered.');
      return null;
    }
    return { columns: columns, rows: rows };
  }

  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;
  }

  // Convert every number found in a cell ("38", "38-40", "19.5") between units.
  function convertCell(text, baseUnit, toUnit) {
    if (baseUnit === toUnit) return text;
    var factor = toUnit === 'cm' ? IN_PER_CM : 1 / IN_PER_CM;
    return text.replace(/\d+(?:\.\d+)?/g, function (n) {
      var v = parseFloat(n) * factor;
      return String(Math.round(v * 10) / 10);
    });
  }

  function build(script) {
    var chart = parseChart(script);
    if (!chart) return;

    var title = script.getAttribute('data-hl-title') || '';
    var accent = script.getAttribute('data-hl-accent') || '#f97316';
    var baseUnit = script.getAttribute('data-hl-units') === 'cm' ? 'cm' : 'in';
    var currentUnit = baseUnit;

    injectStyles();

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

    var header = el('div', PREFIX + '__header');
    header.appendChild(el('h3', PREFIX + '__title', title || 'Size Chart'));
    var unitBar = el('div', PREFIX + '__units');
    var inBtn = el('button', PREFIX + '__unit', 'in');
    inBtn.type = 'button';
    inBtn.setAttribute('aria-label', 'Show measurements in inches');
    var cmBtn = el('button', PREFIX + '__unit', 'cm');
    cmBtn.type = 'button';
    cmBtn.setAttribute('aria-label', 'Show measurements in centimeters');
    unitBar.appendChild(inBtn);
    unitBar.appendChild(cmBtn);
    header.appendChild(unitBar);
    root.appendChild(header);

    var scroll = el('div', PREFIX + '__scroll');
    var table = el('table', PREFIX + '__table');
    var caption = document.createElement('caption');
    table.appendChild(caption);

    var thead = document.createElement('thead');
    var headRow = document.createElement('tr');
    chart.columns.forEach(function (col) {
      headRow.appendChild(el('th', null, col)).scope = 'col';
    });
    thead.appendChild(headRow);
    table.appendChild(thead);

    var tbody = document.createElement('tbody');
    var bodyCells = [];
    chart.rows.forEach(function (row) {
      var tr = document.createElement('tr');
      var rowCells = [];
      row.forEach(function (cellText, ci) {
        var cell = el(ci === 0 ? 'th' : 'td', null, cellText);
        if (ci === 0) cell.scope = 'row';
        rowCells.push({ node: cell, text: cellText });
        tr.appendChild(cell);
      });
      bodyCells.push(rowCells);
      tbody.appendChild(tr);
    });
    table.appendChild(tbody);

    function applyUnit(unit) {
      currentUnit = unit;
      inBtn.setAttribute('aria-pressed', unit === 'in' ? 'true' : 'false');
      cmBtn.setAttribute('aria-pressed', unit === 'cm' ? 'true' : 'false');
      bodyCells.forEach(function (rowCells) {
        rowCells.forEach(function (cell) {
          cell.node.textContent = convertCell(cell.text, baseUnit, unit);
        });
      });
      caption.textContent = 'All measurements in ' + (unit === 'in' ? 'inches' : 'centimeters') + '.';
    }
    inBtn.addEventListener('click', function () { applyUnit('in'); });
    cmBtn.addEventListener('click', function () { applyUnit('cm'); });
    applyUnit(currentUnit);

    scroll.appendChild(table);
    root.appendChild(scroll);

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/size-chart-table';
    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 size-chart-table] 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-config(required)JSON object with a "columns" array of header labels and a "rows" array of cell arrays, one per size.
data-hl-units"in"Unit your config values are written in: "in" for inches or "cm" for centimeters; the toggle converts from this base.
data-hl-title"Size Chart"Heading shown above the table.
data-hl-accent"#f97316"Hex color used for the header row and the active unit toggle.

Frequently asked questions

How do I install the Size Chart Table?

Paste the script tag into your page where the chart should appear. The table renders inline right after the tag, built from the columns and rows you put in data-hl-config.

Can I change the title, units, or colors?

Yes. Set data-hl-title for the heading, data-hl-units to declare whether your numbers are inches ("in") or centimeters ("cm"), and data-hl-accent for the header and toggle color. Shoppers can switch units themselves with the built-in in/cm toggle.

Does it work on WordPress, Shopify, or Squarespace?

Yes — it is a single vanilla JavaScript file with no dependencies. Paste the snippet into a custom HTML block or embed section on any platform and the chart works out of the box.

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