# Troubleshooting (/docs/troubleshooting)

Most issues trace back to one of a handful of causes. Each fix is below, with a link to the guide page that explains it
in full.

## Charts render with no color, no strokes, just plain shapes

The stylesheet isn't loaded. Every visual property comes from `styles.css`: color, stroke weight, label size, the whole
theme. Import it once:

```tsx
import "@microcharts/react/styles.css";
```

Without it a chart is still valid, accessible SVG: the markup and the accessible name are both there, with no styling
applied. See [Quickstart](/docs/quickstart#compatibility) for where the import goes in your framework.

## A chart is invisible (or nearly) on a colored or dark panel

Charts never paint their own background; they render transparent SVG. On a plain light or dark page you never notice,
because the built-in light and dark tokens track `prefers-color-scheme`. It breaks when a chart sits on a surface whose
color doesn't match the _page's_ color scheme: a dark-branded card on an otherwise light page, or a colored KPI tile.
The tokens don't know that scope looks dark; they only know what the OS or browser reports.

Fix it by rebinding the token scope to match the surface, not the page:

```css
.dark-panel {
  --mc-stroke: #f5f5f5; /* text + data strokes; add --mc-neutral for muted labels */
}
```

or force the whole preset for that scope:

```tsx
<div data-mc-theme="dark">
  <Sparkline data={data} />
</div>
```

See [Theming](/docs/theming) for the full token set and how scope, preset, and prop precedence works.

## `Error: ... 'use client' ...` when importing a chart

You imported a chart's `/interactive` entry into a Server Component:

```tsx
// ❌ interactive entries are client components — this throws in an RSC
import { Sparkline } from "@microcharts/react/sparkline/interactive";
```

Two ways out. Render the static default entry if you don't need hover, keyboard, touch, or selection:

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

Or, if you do need interactivity, move the import into a component marked `"use client"` and render that component from
your server tree. See [Quickstart](/docs/quickstart#add-interactivity) for the static-vs-interactive split.

## `onPointFocus` / `onRunFocus` no longer fire

They were removed when every interactive chart moved onto one shared contract. The replacement is `onActive`, which
fires for the same reason on every chart in the catalog:

```tsx
// ❌ removed
<Sparkline data={data} onPointFocus={(i) => setHovered(i)} />

// ✅ one name, everywhere
<Sparkline data={data} onActive={(d) => setHovered(d?.index ?? null)} />
```

The payload is a `MicroDatum`, `{ index, value, label?, formatted? }`, where `value` is the raw number and `formatted`
is the chart's ready-to-display string; it's `null` when the chart clears. `onPointFocus` on Sparkline and SparkBar and
StreakSpark's `onRunFocus` map across directly. `index` identifies the chart's _navigable unit_, which isn't always a
series index: on Waveform it's a bucket, on Hypnogram a run, on SegmentedBar a segment. Each chart's page names its own
unit. For click-to-pin, add `onSelect`; see [Accessibility](/docs/accessibility#one-interaction-contract).

## `animate` does nothing

The entrance engine isn't loaded. `animate` is a flag; the code that runs the animation ships in a separate side-effect
import you add once, anywhere in your client bundle:

```tsx
import "@microcharts/react/motion";
```

In development the chart logs this to the console when the engine is missing. Three other reasons `animate` is inert,
none of them bugs: the reader has `prefers-reduced-motion: reduce`, the chart is a static entry (`animate` is an
`/interactive`-only prop), or the markup came from the server and is being hydrated rather than freshly mounted, where
an entrance would animate content the reader can already see. The entrance runs once per mount by design; remount with a
changed `key` to replay it. The full contract is on [Motion](/docs/motion).

## A selected unit stays lit after I click away

It shouldn't, from 0.16. A pin is dropped by a pointer press anywhere outside the chart, by **Escape** from anywhere on
the page, and by selecting the same unit again; each fires `onSelect(null)`. Blur is not one of them, so moving focus to
a panel that reads the pinned value keeps it.

If a pin is still stuck, the chart is controlled: `selectedIndex` makes your state the only thing that can move the
selection, and the chart just reports the dismissal. Clear it in your handler, or switch to `defaultSelectedIndex` to
seed the selection once and let the chart own it after that.

```tsx
const [picked, setPicked] = useState<number | null>(null);
<SparkBar data={data} selectedIndex={picked} onSelect={(d) => setPicked(d?.index ?? null)} />;
```

## The chart won't stretch to fill its container

Give it a width and make sure the _parent_ is block-level (a flex or grid item, or a `div`). A chart can't grow past an
inline parent no matter what you set on the chart itself.

```tsx
<Sparkline data={data} className="w-full" />
```

`.mc-root`, the chart's own `<svg>`, is `display: inline-block` by default, so it flows next to text; add
`display: block` when you want it on its own line.

A width alone is enough, and it is enough on a `className`. Before 0.15 it wasn't: the chart set both `width` and
`height` as SVG attributes, your CSS replaced only the width, and `preserveAspectRatio` then fitted the drawing to the
axis you hadn't touched. The element stretched, the drawing didn't, and you got a small mark centered in a wide box —
which reads as a chart that failed to load. If you carry a `h-auto` next to a `w-full` for that reason, you can drop it.
Interactive entries behave identically: `style`, `className`, and `width`/`height` size an interactive chart exactly the
way they size its static twin, and hit-testing stays exact at any size, so the crosshair tracks the cursor and a touch
drag scrubs the unit under the finger. Full recipes, including the inline-in-a-sentence case, are on
[Sizing & scaling](/docs/sizing).

## A chart stacks on its own line instead of sitting inline with text

This is usually a CSS reset, not a microcharts bug. `.mc-root`'s `display: inline-block` is declared inside a
zero-specificity `:where()` so your styles always win, including ones you didn't mean to apply. Tailwind's Preflight
sets a plain `svg { display: block; ... }` rule, which has _higher_ specificity than any `:where()` selector and
overrides it, dropping the chart onto its own block-level line. Re-assert the layout you want at the call site:

```tsx
<Sparkline data={data} className="inline-block" />
```

or wrap it in `className="mc-inline"` for text-flow placement — see
[Sizing & scaling](/docs/sizing#inline-in-a-sentence).

## Chart labels look wrong — a serif fallback, or mismatched with the page

Charts inherit `font-family` from the page; they don't set one of their own. If the surrounding page never declares a
font (a bare sandbox, a minimal starter), SVG text falls back to the browser's default serif. See
[Theming → The font](/docs/theming#the-font) for why it inherits and how to set it per-scope.
