# Composition (/docs/composition)

microcharts are built to sit _inside_ an interface, not in a chart pane of their own. This page documents the placement
decisions the gallery demos make — the surface they need, the size and props each context wants, and how to compare
several at once — so nothing is left implied. If a demo on this site looks right, this is why.

## Charts sit on your surface

Every chart is transparent: it **never paints its own background**. That's deliberate — a chart drops onto whatever
surface it lands on and picks up the ink color from `--mc-stroke` (which tracks light/dark for you). The one decision
that's yours: give it a container with real contrast.

- Put the chart on a surface with enough contrast for a 1.5 px line — the data, valence, and accent inks all clear 4.5:1
  on the built-in light and dark themes. (`--mc-neutral`, the muted context ink for baselines and ghost marks, sits at
  3.5:1 — comfortably past the 3:1 bar for non-text, and deliberately quieter than the data.)
- Don't wrap it in a low-contrast tint and expect the chart to compensate; it won't, by design.
- Need the ink to differ from the page (charts on a colored panel, frosted glass)? Set `--mc-stroke` (and valence tokens
  if needed) on the container — see [Theming](/docs/theming#frosted-or-translucent-surfaces). Don't override `.mc-root`
  paint rules.

> The gallery renders each demo inside a panel with a normal card background and a hairline border. Nothing about that panel is part of the library — it's just a surface. Your app's cards, table rows, and headers are surfaces too.

## In a sentence

A word-sized chart in running prose scales with the **font** and aligns to the text baseline; drop its endpoint dots and
it reads as one glyph, not a widget. (There are no axes to drop — no chart in the catalog draws one.) Wrap it in
`mc-inline` (shipped in `styles.css`), pass `dots="none"`, and mark it decorative with `summary={false}` so a screen
reader hears the sentence, not a second description.

```tsx
<p>
    p95 latency this week{" "}
    <span className="mc-inline">
      <Sparkline data={latency} dots="none" summary={false} width={64} height={16} />
    </span>{" "}
    — trending down.
  </p>
```

See [Sizing](/docs/sizing#inline-in-a-sentence) for the `em`-based variant that scales with the surrounding type size.

## In a table cell

In a dense table, each row already carries a name and a number — the chart's job is the _shape_ between them. So it goes
small, with dots off, decorative (`summary={false}`), and the numeric column stays `tabular-nums` so figures line up.

```tsx
<tr>
    <td>checkout-api</td>
    <td><Sparkline data={latency} dots="none" summary={false} width={64} height={18} /></td>
    <td className="tabular-nums">31 ms</td>
  </tr>
```

## In a KPI card

Here the big number leads and the chart is context beneath it. Give it a little more height, turn `fill` on so it reads
as a filled area at a glance, and keep it decorative — the figure and label already say what it is.

```tsx
<div className="kpi">
    <span className="figure">1,600</span>
    <span className="unit">concurrent, now</span>
    <Sparkline data={connections} fill summary={false} width={120} height={32} />
  </div>
```

## In a tab or button

A spark beside a label previews what's behind the tab. It goes _tiny_ — the label is the primary read, the chart a hint
— so drop to a small box with no dots.

```tsx
<button className="tab">
    CPU <Sparkline data={cpu} dots="none" summary={false} width={40} height={14} />
  </button>
```

## Small multiples: one shared scale

The most important composition decision, and the easiest to get silently wrong: when you place several sparklines in a
row to _compare_ them, they must share one vertical scale. Auto-scaling each to its own range makes a flat line and a
spiking one look identical — a real correctness bug.

`SparkGroup` fixes it structurally. It reads every child's `data`, computes **one** domain across all of them, and
enforces **one** physical size — so a row of charts is honestly comparable. It's hook-free and RSC-safe, and any child's
own explicit `domain`/`width` still wins.

```tsx
import { SparkGroup } from "@microcharts/react";
import { Sparkline } from "@microcharts/react/sparkline";

<SparkGroup width={80} height={22}>
    <Sparkline data={us} summary={false} />
    <Sparkline data={eu} summary={false} />
    <Sparkline data={apac} summary={false} />
  </SparkGroup>
```

Pass `zero` when the group is bars or areas (so the shared domain anchors at zero), or an explicit `domain={[min, max]}`
when you want a fixed frame instead of the data's own range (`domain` defaults to `"shared"`).

Two details worth knowing. Only children that actually carry a `data` prop get the scale and size injected — plain
wrappers, labels, and captions pass through untouched, so you can interleave them without them collecting stray DOM
attributes. And the group renders one `<div className="mc-group">`, an `inline-flex` row that wraps: its gap is
`--mc-gap` scaled by `--mc-density`, and `className`/`style` on `SparkGroup` land on that div if you want a different
layout.

## Decorative vs. summarized — the `summary` decision

You'll notice `summary={false}` on nearly every demo above. That's a deliberate call, not a habit:

- **Standalone chart** (the one thing in its region): keep the auto-summary on. It's the accessible name a screen reader
  announces — real, generated from the data.
- **Repeated or captioned chart** (table rows, tabs, a chart next to a number that already states it): pass
  `summary={false}`. The surrounding text is the accessible name; a per-chart summary would just make a screen reader
  read the same thing many times.

This is the only accessibility lever you routinely touch. Everything else — the `role="img"`, the `<title>`, and the
whole interaction contract on interactive entries (roving arrow keys, select-to-pin with **Enter** or **Space**, touch
scrub, live announcements) — is automatic. See [Accessibility](/docs/accessibility).
