Docs · Embed

Embed a chart that stays alive

One hosted script, one custom element, no build step. The chart draws itself in, morphs when its spec changes, and carries its own accessibility — this page is the full contract. For the two-minute version, start at get started.


1 · Load the element

The bundle is self-contained — engine included, about 16 KB gzipped — and served from this domain with CORS open, so the same tag works on any origin. An npm package ships at launch.

<script type="module" src="https://meikucharts.com/v1/live-chart.js"></script>

2 · Drop a chart with its spec as a JSON child

The way most embedders start: the spec travels inside the element, so the snippet is one self-sufficient block of HTML.

<live-chart>
  <script type="application/json">
    {
      "type": "column",
      "theme": "editorial",
      "title": "Weekly Signups",
      "source": "Product analytics",
      "data": {
        "labels": [
          "Mon",
          "Tue",
          "Wed",
          "Thu",
          "Fri"
        ],
        "series": [
          {
            "name": "Signups",
            "values": [
              12,
              19,
              14,
              27,
              22
            ]
          }
        ]
      }
    }
  </script>
</live-chart>

3 · Or pass the spec another way

Three inputs, same result — pick whichever fits the page. A spec attribute carrying JSON suits server-rendered templates:

<live-chart spec='{"type":"line","theme":"mint","data":{"labels":["Q1","Q2","Q3"],"series":[{"values":[12,19,27]}]}}'></live-chart>

And the spec property suits scripts — assign a plain object, no serialization. Setting it on a mounted chart is also how you morph:

const chart = document.querySelector("live-chart");
chart.spec = {
  "type": "line",
  "theme": "mint",
  "title": "Weekly Signups",
  "source": "Product analytics",
  "data": {
    "labels": [
      "Mon",
      "Tue",
      "Wed",
      "Thu",
      "Fri"
    ],
    "series": [
      {
        "name": "Signups",
        "values": [
          18,
          24,
          20,
          33,
          29
        ]
      }
    ]
  }
};

The element re-parses on every change and the engine animates from the old shape to the new one — including across chart types. There is no separate update API to learn.


Sizing

The host renders as a block, full width, 300px tall by default. Size it like any element — the canvas follows its box, crisp on high-DPI screens:

live-chart {
  height: 420px;      /* default is 300px */
  max-width: 720px;
}

States you can style and script against

The host mirrors the engine's state as an attribute: "animating" while drawing or morphing, "idle" once settled. Tests in this repository wait for state="idle"; a host page can do the same, or hook CSS on it:

live-chart[state="animating"] { /* drawing or morphing */ }
live-chart[state="idle"]      { /* settled */ }

A spec that fails to parse never throws: the chart shows a short structured message, and the element's error property returns the same {path, message, hint} list the spec reference documents.


Accessibility, built in

Every chart ships a hidden data table with its raw values (screen readers get the truth, not a description of pixels), an aria-label naming the chart, and keyboard navigation — Tab to the canvas, arrows between marks, Escape out — with a visible focus ring. Hover tooltips have keyboard parity. None of this needs configuration.


Theming the embed

Charts follow their theme's light or dark variant. Pass options.variant as "auto" to follow the visitor's OS preference (omitting it shows the theme's own canonical side instead), or pin "light"/"dark" — which is what a page with its own theme toggle should do, since the engine can see the OS but never a host page's toggle. Details and the format/target options live in the spec reference.


What the embed never does

No network requests beyond loading the script itself, no cookies, no tracking, nothing uploaded — the spec renders entirely in the visitor's browser. The e2e suite proves the exported snippet renders on a blank page with only that one script fetch.