{
  "$schema": "https://mian.ro/schema/v2/registry.json",
  "schemaVersion": "2.0.0",
  "generatedAt": "2026-10-02T13:14:06.957Z",
  "id": "chart",
  "name": "Chart",
  "category": "Data display",
  "description": "Bar, horizontal bar, line/area and donut charts from plain markup. Each noodle draws them in its own style.",
  "tags": [
    "chart",
    "graph",
    "bar chart",
    "line chart",
    "area",
    "donut",
    "pie",
    "sparkline",
    "dashboard"
  ],
  "requiresJs": true,
  "license": "LicenseRef-MIAN-1.0",
  "composition": {
    "role": null,
    "intents": [
      "bar chart",
      "line chart",
      "donut chart",
      "graph",
      "analytics chart",
      "revenue over time"
    ],
    "prominence": null,
    "density": null,
    "worksAfter": [],
    "worksBefore": [],
    "alternatives": [],
    "avoid": null
  },
  "url": "https://mian.ro/components/chart",
  "markdownUrl": "https://mian.ro/ai/components/chart.md",
  "classes": [
    {
      "name": "mn-chart",
      "description": "A <figure> wrapper with an optional <figcaption>."
    },
    {
      "name": "mn-chart-bars · mn-bar",
      "description": "Column chart. Set --max on the container and --v on each bar; data-value shows on hover and with the arrow keys; mn-hl highlights."
    },
    {
      "name": "mn-chart-h",
      "description": "Horizontal bars."
    },
    {
      "name": "svg.mn-chart-line[data-values]",
      "description": "Line and area chart drawn by mian.js. Add data-values-2 for a second series. Hover, touch or arrow keys show a guide line and the values at that point."
    },
    {
      "name": "data-labels · data-names · data-unit",
      "description": "On the line chart: the x labels (comma-separated), the series names, and a unit for values (“€” gives €12, “k” gives 12k, “ km” gives 12 km)."
    },
    {
      "name": "mn-donut · --s1…--s4",
      "description": "Donut. Segment sizes in percent."
    },
    {
      "name": "mn-legend",
      "description": "Legend list; colours follow the series order."
    },
    {
      "name": "--mn-series-1…4",
      "description": "Series colours. Default: primary, accent, success, warning."
    }
  ],
  "accessibility": [
    "Charts are images: give the figure role=\"img\" and an aria-label that states the key numbers, or provide the data in a table next to it.",
    "Colour is never the only differentiator in the legend: order and labels match the series."
  ],
  "examples": [
    {
      "title": "Bars",
      "description": null,
      "html": "<figure class=\"mn-chart\" role=\"img\" aria-label=\"Revenue by month, peaking in December at 94\">\n  <div class=\"mn-chart-bars\" style=\"--max:100\">\n    <div class=\"mn-bar\" style=\"--v:38\" data-value=\"38\"><span>Jan</span></div>\n    <div class=\"mn-bar\" style=\"--v:52\" data-value=\"52\"><span>Feb</span></div>\n    <div class=\"mn-bar\" style=\"--v:44\" data-value=\"44\"><span>Mar</span></div>\n    <div class=\"mn-bar\" style=\"--v:66\" data-value=\"66\"><span>Apr</span></div>\n    <div class=\"mn-bar\" style=\"--v:58\" data-value=\"58\"><span>May</span></div>\n    <div class=\"mn-bar\" style=\"--v:74\" data-value=\"74\"><span>Jun</span></div>\n    <div class=\"mn-bar\" style=\"--v:62\" data-value=\"62\"><span>Jul</span></div>\n    <div class=\"mn-bar\" style=\"--v:81\" data-value=\"81\"><span>Aug</span></div>\n    <div class=\"mn-bar\" style=\"--v:70\" data-value=\"70\"><span>Sep</span></div>\n    <div class=\"mn-bar\" style=\"--v:88\" data-value=\"88\"><span>Oct</span></div>\n    <div class=\"mn-bar\" style=\"--v:76\" data-value=\"76\"><span>Nov</span></div>\n    <div class=\"mn-bar mn-hl\" style=\"--v:94\" data-value=\"94\"><span>Dec</span></div>\n  </div>\n</figure>"
    },
    {
      "title": "Line and area",
      "description": null,
      "html": "<figure class=\"mn-chart\" role=\"img\" aria-label=\"Visitors this year versus last year\">\n  <svg class=\"mn-chart-line\" data-values=\"12,18,15,24,21,30,28,36,33,41,38,47\" data-values-2=\"10,12,14,13,17,19,18,22,24,23,27,29\" data-labels=\"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec\" data-names=\"This year,Last year\" data-unit=\"k\"></svg>\n  <ul class=\"mn-legend\"><li>This year</li><li>Last year</li></ul>\n</figure>"
    },
    {
      "title": "Horizontal bars",
      "description": null,
      "html": "<figure class=\"mn-chart\" role=\"img\" aria-label=\"Traffic by source\">\n  <div class=\"mn-chart-bars mn-chart-h\" style=\"--max:60\">\n    <div class=\"mn-bar mn-hl\" style=\"--v:52\" data-value=\"52%\"><span>Search</span></div>\n    <div class=\"mn-bar\" style=\"--v:24\" data-value=\"24%\"><span>Direct</span></div>\n    <div class=\"mn-bar\" style=\"--v:14\" data-value=\"14%\"><span>Social</span></div>\n    <div class=\"mn-bar\" style=\"--v:10\" data-value=\"10%\"><span>Email</span></div>\n  </div>\n</figure>"
    },
    {
      "title": "Donut",
      "description": null,
      "html": "<figure class=\"mn-chart\" style=\"justify-items:center\">\n  <div class=\"mn-donut-wrap\" role=\"img\" aria-label=\"Plan mix: Pro 45%, Team 30%, Free 15%, Enterprise 10%\">\n    <div class=\"mn-donut\" style=\"--s1:45;--s2:30;--s3:15;--s4:10\"></div>\n    <div class=\"mn-donut-label\">2,350<small>customers</small></div>\n  </div>\n  <ul class=\"mn-legend\"><li>Pro</li><li>Team</li><li>Free</li><li>Enterprise</li></ul>\n</figure>"
    }
  ],
  "css": ".mn-chart { margin: 0; display: grid; gap: .75rem; min-width: 0; font: 400 .75rem/1.3 var(--mn-font); color: var(--mn-muted); }\n.mn-chart figcaption { font-size: .75rem; color: var(--mn-muted); }\n.mn-chart-bars {\n  --h: 10rem; --max: 100;\n  display: flex; align-items: stretch; gap: var(--mn-bar-gap, 6px);\n  height: var(--h); padding-bottom: 1.25rem; position: relative;\n  background: repeating-linear-gradient(to top, var(--mn-border) 0 1px, transparent 1px calc((var(--h) - 1.25rem) / 4));\n  background-size: 100% calc(100% - 1.25rem); background-repeat: no-repeat;\n}\n.mn-bar { position: relative; flex: 1; display: flex; flex-direction: column; justify-content: flex-end; min-width: 0; }\n.mn-bar::before {\n  content: \"\"; display: block;\n  height: calc(var(--v, 0) / var(--max) * 100%);\n  border-radius: var(--mn-bar-radius, min(6px, var(--mn-radius-field))) var(--mn-bar-radius, min(6px, var(--mn-radius-field))) 0 0;\n  background: var(--mn-bar-fill, color-mix(in oklab, var(--mn-series-1) 30%, transparent));\n  transition: height 500ms var(--mn-spring), background-color var(--mn-duration) var(--mn-ease);\n}\n.mn-bar.mn-hl::before, .mn-bar:hover::before { background: var(--mn-bar-fill-hl, var(--mn-series-1)); }\n.mn-bar > span { position: absolute; left: 50%; bottom: -1.25rem; translate: -50% 0; white-space: nowrap; font-size: .6875rem; }\n.mn-bar[data-value]::after {\n  content: attr(data-value); position: absolute; left: 50%; bottom: calc(var(--v, 0) / var(--max) * (100%) + 4px); translate: -50% 0;\n  padding: 2px 6px; border-radius: 4px; background: var(--mn-fg); color: var(--mn-bg); font-size: .6875rem; font-weight: 600;\n  opacity: 0; pointer-events: none; transition: opacity var(--mn-duration) var(--mn-ease);\n}\n.mn-bar:hover::after, .mn-bar.mn-active::after { opacity: 1; z-index: 2; }\n.mn-bar.mn-active::before { background: var(--mn-bar-fill-hl, var(--mn-series-1)); }\n/* The hovered column is marked like the line chart's guide. */\n.mn-chart-bars:not(.mn-chart-h) .mn-bar:is(:hover, .mn-active) { background: linear-gradient(color-mix(in oklab, var(--mn-fg) 6%, transparent), color-mix(in oklab, var(--mn-fg) 6%, transparent)) top / 100% calc(100% - 0px) no-repeat; border-radius: var(--mn-radius-field) var(--mn-radius-field) 0 0; }\n/* Horizontal bars */\n.mn-chart-bars.mn-chart-h { flex-direction: column; height: auto; padding: 0 2.5rem 0 var(--mn-chart-label, 4.5rem); gap: var(--mn-bar-gap, 8px); background: none; }\n.mn-chart-h .mn-bar { flex: none; flex-direction: row; justify-content: flex-start; align-items: center; height: 1.1rem; }\n.mn-chart-h .mn-bar::before { height: 100%; width: calc(var(--v, 0) / var(--max) * 100%); border-radius: 0 var(--mn-bar-radius, 4px) var(--mn-bar-radius, 4px) 0; transition: width 500ms var(--mn-spring); }\n.mn-chart-h .mn-bar > span { left: calc(var(--mn-chart-label, 4.5rem) * -1); bottom: auto; top: 50%; translate: 0 -50%; width: calc(var(--mn-chart-label, 4.5rem) - .5rem); text-align: end; overflow: hidden; text-overflow: ellipsis; }\n.mn-chart-h .mn-bar[data-value]::after { left: calc(var(--v, 0) / var(--max) * 100% + 6px); bottom: auto; top: 50%; translate: 0 -50%; opacity: 1; background: none; color: var(--mn-muted); padding: 0; }\n\n/* Line / area: mian.js draws the paths from data-values */\n.mn-chart-line { display: block; width: 100%; height: var(--h, 10rem); overflow: visible; }\n.mn-chart-line .mn-grid-line { stroke: var(--mn-border); stroke-width: 1; vector-effect: non-scaling-stroke; }\n.mn-chart-line .mn-line { fill: none; stroke: var(--mn-series-1); stroke-width: var(--mn-line-width, 2px); stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }\n.mn-chart-line .mn-line.mn-s2 { stroke: var(--mn-series-2); }\n.mn-chart-line .mn-area { fill: var(--mn-series-1); opacity: var(--mn-area-opacity, .12); }\n.mn-chart-line .mn-area.mn-s2 { fill: var(--mn-series-2); }\n.mn-chart-line .mn-point { fill: var(--mn-surface); stroke: var(--mn-series-1); stroke-width: 2px; vector-effect: non-scaling-stroke; opacity: var(--mn-point-opacity, 0); }\n.mn-chart-line .mn-point.mn-s2 { stroke: var(--mn-series-2); }\n.mn-chart-line { cursor: crosshair; touch-action: pan-y; }\n.mn-chart-line:focus-visible { outline: 2px solid var(--mn-ring); outline-offset: 4px; border-radius: 2px; }\n.mn-chart-line .mn-chart-guide { stroke: var(--mn-chart-guide, color-mix(in oklab, var(--mn-fg) 45%, transparent)); stroke-width: 1; stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; pointer-events: none; }\n.mn-chart-line .mn-point.mn-active { r: 4.5; }\n/* The hover tip: label, then one row per series with its colour. */\n.mn-chart-tip {\n  position: absolute; z-index: 5; pointer-events: none; display: grid; gap: .2rem; min-width: 6rem; max-width: 16rem;\n  padding: .45rem .6rem; border: var(--mn-border-w) solid var(--mn-border); border-radius: var(--mn-radius-field);\n  background: var(--mn-surface); color: var(--mn-fg); box-shadow: var(--mn-shadow-md);\n  font: 400 .75rem/1.35 var(--mn-font); font-variant-numeric: tabular-nums; white-space: nowrap;\n}\n.mn-chart-tip[hidden] { display: none; }\n.mn-chart-tip strong { font-weight: 600; color: var(--mn-fg); }\n.mn-chart-tip-row { display: flex; align-items: center; gap: .4rem; color: var(--mn-muted); }\n.mn-chart-tip-row b { margin-inline-start: auto; padding-inline-start: .75rem; color: var(--mn-fg); font-weight: 600; }\n.mn-chart-tip-row i { width: .55rem; height: .55rem; flex: none; border-radius: 2px; background: var(--mn-series-1); }\n.mn-chart-tip-row i.mn-s2 { background: var(--mn-series-2); }\n.mn-chart-bars[tabindex]:focus-visible { outline: 2px solid var(--mn-ring); outline-offset: 4px; border-radius: 2px; }\n.mn-legend { display: flex; flex-wrap: wrap; gap: .4rem 1rem; margin: 0; padding: 0; list-style: none; }\n.mn-legend li { display: inline-flex; align-items: center; gap: .4rem; }\n.mn-legend li::before { content: \"\"; width: .7rem; height: .7rem; border-radius: 3px; background: var(--_s, var(--mn-series-1)); }\n.mn-legend li:nth-child(2) { --_s: var(--mn-series-2); } .mn-legend li:nth-child(3) { --_s: var(--mn-series-3); } .mn-legend li:nth-child(4) { --_s: var(--mn-series-4); }\n\n/* Donut: segments are percentages in --s1…--s4 */\n.mn-donut {\n  --s1: 0; --s2: 0; --s3: 0; --s4: 0; --size: 8rem; --thick: 40%;\n  position: relative; display: inline-grid; place-items: center; width: var(--size); height: var(--size); border-radius: 50%;\n  background: conic-gradient(from var(--mn-donut-start, 0deg),\n    var(--mn-series-1) 0 calc(var(--s1) * 1%),\n    var(--mn-series-2) 0 calc((var(--s1) + var(--s2)) * 1%),\n    var(--mn-series-3) 0 calc((var(--s1) + var(--s2) + var(--s3)) * 1%),\n    var(--mn-series-4) 0 calc((var(--s1) + var(--s2) + var(--s3) + var(--s4)) * 1%),\n    var(--mn-surface-2) 0);\n  /* --thick is the ring width as a share of the radius (or a length). */\n  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - var(--thick)), #000 calc(100% - var(--thick) + .5px));\n          mask: radial-gradient(closest-side, transparent calc(100% - var(--thick)), #000 calc(100% - var(--thick) + .5px));\n}\n.mn-donut-wrap { position: relative; display: inline-grid; place-items: center; }\n.mn-donut-wrap > .mn-donut-label { position: absolute; text-align: center; font: 700 1.25rem/1.1 var(--mn-font); color: var(--mn-fg); }\n.mn-donut-wrap > .mn-donut-label small { display: block; font-size: .6875rem; font-weight: 500; color: var(--mn-muted); }",
  "js": "/* <svg class=\"mn-chart-line\" data-values=\"3,5,4,8\" data-values-2=\"2,3,6,5\">\n   Draws gridlines, a smoothed line, an area and points. Re-draws if the\n   data attributes change. */\nconst smooth = (pts) => {\n  if (pts.length < 2) return '';\n  let d = `M ${pts[0][0]} ${pts[0][1]}`;\n  for (let i = 0; i < pts.length - 1; i++) {\n    const p0 = pts[i - 1] || pts[i], p1 = pts[i], p2 = pts[i + 1], p3 = pts[i + 2] || p2;\n    // Clamp control points to the segment's own range so the curve never\n    // overshoots the data (a count must not appear to dip below zero).\n    const lo = Math.min(p1[1], p2[1]), hi = Math.max(p1[1], p2[1]);\n    const cl = (v) => Math.max(lo, Math.min(hi, v));\n    const c1 = [p1[0] + (p2[0] - p0[0]) / 6, cl(p1[1] + (p2[1] - p0[1]) / 6)];\n    const c2 = [p2[0] - (p3[0] - p1[0]) / 6, cl(p2[1] - (p3[1] - p1[1]) / 6)];\n    d += ` C ${c1[0].toFixed(1)} ${c1[1].toFixed(1)} ${c2[0].toFixed(1)} ${c2[1].toFixed(1)} ${p2[0]} ${p2[1]}`;\n  }\n  return d;\n};\nfunction drawLine(svg) {\n  // Draw in real pixels so points stay round and strokes stay crisp.\n  const box = svg.getBoundingClientRect();\n  const W = Math.max(60, Math.round(box.width) || 300), H = Math.max(40, Math.round(box.height) || 120), pad = 6;\n  // data-values-2 is not camel-cased by dataset (a digit follows the hyphen): read the attribute.\n  const series = [svg.getAttribute('data-values'), svg.getAttribute('data-values-2')].filter(Boolean).map((s) => s.split(',').map(Number));\n  if (!series.length) return;\n  // A sparkline shows the shape of a change, so it is scaled to its own range, not from zero.\n  const flat = series.flat(), lo = Math.min(...flat), hi = Math.max(...flat), spark = svg.classList.contains('mn-spark');\n  const max = spark ? hi + ((hi - lo) * .12 || 1) : hi * 1.1 || 1;\n  const min = spark ? lo - (hi - lo) * .2 : Math.min(0, lo);\n  svg.setAttribute('viewBox', `0 0 ${W} ${H}`);\n  let out = '';\n  for (let g = 1; g <= 3; g++) out += `<line class=\"mn-grid-line\" x1=\"0\" x2=\"${W}\" y1=\"${(H / 4) * g}\" y2=\"${(H / 4) * g}\"/>`;\n  const all = [];\n  series.forEach((vals, si) => {\n    const cls = si ? ' mn-s2' : '';\n    const pts = vals.map((v, i) => [+(pad + (i / (vals.length - 1 || 1)) * (W - pad * 2)).toFixed(1), +(H - pad - ((v - min) / (max - min)) * (H - pad * 2)).toFixed(1)]);\n    all.push(pts);\n    const line = smooth(pts);\n    out += `<path class=\"mn-area${cls}\" d=\"${line} L ${pts[pts.length - 1][0]} ${H} L ${pts[0][0]} ${H} Z\"/>`;\n    out += `<path class=\"mn-line${cls}\" d=\"${line}\"/>`;\n    out += pts.map(([x, y], i) => `<circle class=\"mn-point${cls}\" data-i=\"${i}\" cx=\"${x}\" cy=\"${y}\" r=\"3\"/>`).join('');\n  });\n  out += `<line class=\"mn-chart-guide\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"${H}\" visibility=\"hidden\"/>`;\n  svg.innerHTML = out;\n  svg._mn = { series, pts: all, W };\n  if (svg._mnIndex != null) showPoint(svg, svg._mnIndex);\n}\n\n/* Hover, touch or arrow keys: a guide line at the nearest point and a tip with\n   its label and values. Labels: data-labels=\"Jan,Feb,…\"; series names:\n   data-names=\"This year,Last year\"; units: data-unit, a currency sign or a\n   unit ending in a space is a prefix (“€12”), anything else a suffix\n   (“12k”, “60 k€” with data-unit=\" k€\"). */\nconst escTip = (t) => String(t).replace(/[&<>\"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' })[c]);\nconst fmt = (svg, v) => {\n  const u = svg.dataset.unit || '', n = Number(v).toLocaleString();\n  return !u ? n : /^[€$£¥]$/.test(u) || u.endsWith(' ') ? u + n : n + u;\n};\nfunction chartTip(svg) {\n  if (svg._mnTip) return svg._mnTip;\n  const host = svg.parentElement;\n  if (getComputedStyle(host).position === 'static') host.style.position = 'relative';\n  const tip = document.createElement('div');\n  tip.className = 'mn-chart-tip';\n  tip.setAttribute('role', 'status');\n  tip.hidden = true;\n  host.appendChild(tip);\n  return (svg._mnTip = tip);\n}\nfunction showPoint(svg, i) {\n  const d = svg._mn; if (!d) return;\n  const n = d.series[0].length;\n  i = Math.max(0, Math.min(n - 1, i));\n  svg._mnIndex = i;\n  const x = d.pts[0][i][0];\n  const guide = svg.querySelector('.mn-chart-guide');\n  guide.setAttribute('x1', x); guide.setAttribute('x2', x); guide.setAttribute('visibility', 'visible');\n  svg.querySelectorAll('.mn-point').forEach((c) => c.classList.toggle('mn-active', +c.dataset.i === i));\n  const labels = (svg.dataset.labels || '').split(',').map((s) => s.trim());\n  const names = (svg.dataset.names || '').split(',').map((s) => s.trim());\n  const tip = chartTip(svg);\n  tip.innerHTML = (labels[i] ? `<strong>${escTip(labels[i])}</strong>` : '') + d.series.map((vals, si) =>\n    `<span class=\"mn-chart-tip-row\"><i class=\"${si ? 'mn-s2' : ''}\"></i>${names[si] ? `${escTip(names[si])}: ` : ''}<b>${escTip(fmt(svg, vals[i]))}</b></span>`).join('');\n  tip.hidden = false;\n  // Place the tip beside the guide, flipped at the right edge.\n  const host = svg.parentElement, hb = host.getBoundingClientRect(), sb = svg.getBoundingClientRect();\n  const left = sb.left - hb.left + (x / d.W) * sb.width;\n  const flip = left + tip.offsetWidth + 12 > hb.width;\n  tip.style.left = `${flip ? left - tip.offsetWidth - 10 : left + 10}px`;\n  tip.style.top = `${sb.top - hb.top + 4}px`;\n}\nfunction hidePoint(svg) {\n  svg._mnIndex = null;\n  svg.querySelector('.mn-chart-guide')?.setAttribute('visibility', 'hidden');\n  svg.querySelectorAll('.mn-point.mn-active').forEach((c) => c.classList.remove('mn-active'));\n  if (svg._mnTip) svg._mnTip.hidden = true;\n}\nfunction nearest(svg, clientX) {\n  const d = svg._mn, b = svg.getBoundingClientRect();\n  const x = ((clientX - b.left) / b.width) * d.W;\n  let best = 0;\n  d.pts[0].forEach(([px], i) => { if (Math.abs(px - x) < Math.abs(d.pts[0][best][0] - x)) best = i; });\n  return best;\n}\nfunction interactive(svg) {\n  if (svg._mnWired) return; svg._mnWired = true;\n  if (!svg.hasAttribute('tabindex')) svg.setAttribute('tabindex', '0');\n  svg.addEventListener('pointermove', (e) => { if (svg._mn) showPoint(svg, nearest(svg, e.clientX)); });\n  svg.addEventListener('pointerdown', (e) => { if (svg._mn) showPoint(svg, nearest(svg, e.clientX)); });\n  svg.addEventListener('pointerleave', () => { if (document.activeElement !== svg) hidePoint(svg); });\n  svg.addEventListener('focus', () => showPoint(svg, svg._mnIndex ?? (svg._mn ? svg._mn.series[0].length - 1 : 0)));\n  svg.addEventListener('blur', () => hidePoint(svg));\n  svg.addEventListener('keydown', (e) => {\n    const n = svg._mn?.series[0].length || 0, i = svg._mnIndex ?? n - 1;\n    const to = { ArrowLeft: i - 1, ArrowRight: i + 1, Home: 0, End: n - 1 }[e.key];\n    if (to != null) { e.preventDefault(); showPoint(svg, to); }\n    if (e.key === 'Escape') hidePoint(svg);\n  });\n}\n\n/* Column and bar charts: the value shows on hover (CSS). Keyboard: the chart is\n   one tab stop; arrow keys move between bars and show each value. */\nfunction interactiveBars(chart) {\n  if (chart._mnWired) return; chart._mnWired = true;\n  const bars = () => [...chart.querySelectorAll(':scope > .mn-bar[data-value]')];\n  if (!bars().length) return;\n  if (!chart.hasAttribute('tabindex')) chart.setAttribute('tabindex', '0');\n  let i = -1;\n  const set = (k) => { const b = bars(); i = Math.max(0, Math.min(b.length - 1, k)); b.forEach((x, j) => x.classList.toggle('mn-active', j === i)); };\n  chart.addEventListener('keydown', (e) => {\n    const n = bars().length;\n    const horizontal = chart.classList.contains('mn-chart-h');\n    const map = horizontal ? { ArrowUp: i - 1, ArrowDown: i + 1 } : { ArrowLeft: i - 1, ArrowRight: i + 1 };\n    const to = { ...map, Home: 0, End: n - 1 }[e.key];\n    if (to != null) { e.preventDefault(); set(to); }\n  });\n  chart.addEventListener('focus', () => set(i < 0 ? 0 : i));\n  chart.addEventListener('blur', () => bars().forEach((x) => x.classList.remove('mn-active')));\n}\n\nregister('chart', (root) => {\n  each(root, '.mn-chart-bars', 'Bars', interactiveBars);\n  each(root, 'svg.mn-chart-line[data-values]', 'Line', (svg) => {\n    drawLine(svg);\n    interactive(svg);\n    new MutationObserver(() => drawLine(svg)).observe(svg, { attributes: true, attributeFilter: ['data-values', 'data-values-2'] });\n    let w = 0;\n    if ('ResizeObserver' in window) new ResizeObserver(([e]) => { const nw = Math.round(e.contentRect.width); if (nw && nw !== w) { w = nw; drawLine(svg); } }).observe(svg);\n  });\n});",
  "install": {
    "css": "https://mian.ro/dist/mian.css",
    "js": "https://mian.ro/dist/mian.js",
    "registryItem": "https://mian.ro/r/chart.json"
  }
}
