# Thermometer (/docs/charts/thermometer)

Thermometer fills a ticked tube from the bottom of its range, and the ticks calibrate the read, which is what buys the
precision. The fill always anchors at the bottom of the domain: never re-zeroed, never log. An optional target line
crosses the tube to mark the goal. `domain` defaults to `[0, 100]` because a calibrated instrument needs a stated range,
and auto-fitting would move the scale under the reader. The bulb is instrument chrome and always full; it is not data,
so its area means nothing.

```tsx
<Thermometer value={72} target={80} title="Fundraiser" height={56} />
```

## Install

```tsx
import { Thermometer } from "@microcharts/react/thermometer";

<Thermometer value={72} target={80} title="Fundraiser" />
```

Setup (package + stylesheet): [Quickstart](/docs/quickstart#set-up-with-an-ai-agent) or paste [`/agent-setup.md`](/agent-setup.md) into your agent.

## Try it

```tsx
import { Thermometer } from "@microcharts/react/thermometer/interactive";

<Thermometer
  value={72}
  target={80}
/>
```

## When to use it

Use it for a fundraising or goal-progress read, a capacity or utilization gauge in a cell, or any value against a stated
range. Trends belong in Sparkline and proportions of a whole in SegmentedBar; it reads a single value, so many series
need a different chart.

## Sizing

**horizontal, in a table cell**

```tsx
<Thermometer value={62} orientation="horizontal" bulb={false} />
```

**explicit calibration ticks**

```tsx
<Thermometer value={72} domain={[32, 100]} ticks={[32, 50, 68, 86, 100]} />
```

## Variants

Vertical with the bulb is the default, since a filling tube reads as progress toward the goal.
`orientation="horizontal"` fits a table cell where a vertical tube cannot, and `bulb={false}` drops the reservoir where
the instrument metaphor is too much for the context.

```tsx
<Thermometer value={62} orientation="horizontal" bulb={false} />
<Thermometer value={72} domain={[32, 100]} ticks={[32, 50, 68, 86, 100]} />
```

## Edge cases

```tsx
<Thermometer value={130} domain={[0, 100]} title="Overrun" />
```

```tsx
<Thermometer value={0} title="Empty" />
```

```tsx
<Thermometer
  value={7234}
  domain={[0, 10000]}
  target={8000}
  format={{ maximumFractionDigits: 0 }}
  locale="de-DE"
/>
```

A value beyond the domain clamps the fill, and the accessible name still reports the true number, so the reading is
never silently clipped. The accessible summary's value, domain, and target all go through the same locale-aware
formatter, so "7,234" becomes "7.234" in German grouping.

## Four homes

**In a sentence**

```tsx
<p>
  Fundraiser progress{" "}
  <span className="mc-inline">
    <Thermometer value={72} target={80} orientation="horizontal" bulb={false} summary={false} />
  </span>{" "}
  — $72K raised, $8K from goal.
</p>
```

**In a table cell**

```tsx
<td>
  <Thermometer value={72} target={80} orientation="horizontal" bulb={false} />
</td>
```

**In a KPI card**

```tsx
<div className="kpi">
  <span className="figure">$72K</span>
  <span className="unit">of $80K goal</span>
  <Thermometer value={72} target={80} orientation="horizontal" bulb={false} />
</div>
```

**In a tab header**

```tsx
<button className="tab">
  Annual <Thermometer value={72} target={80} orientation="horizontal" bulb={false} />
</button>
```

## Accessibility

The accessible name states the value on its scale, **"62 on a 0–100 scale."**, and appends the goal when a target is
set. The interactive entry reveals the exact value on hover or focus, glides the fill to its new level with a
reduced-motion-gated transition, and announces each change through a polite live region.

This chart is a single unit, so there is nothing to rove between: a click, tap, `Enter` or `Space` selects it
and fires `onSelect`, and no selection stays pinned. That is the scalar half of the shared
[interaction contract](/docs/accessibility#one-interaction-contract).
Hover or focus also reveals the reading itself in a floating chip, for the sizes and label modes where the mark
does not print it; `readout={false}` drops the chip and keeps everything else.

## Props

| Prop | Type | Description |
| --- | --- | --- |
| `value` (required) | `number` | The reading. |
| `target` | `number` | A goal tick across the tube. |
| `domain` | `[number, number]` | The calibrated range (default [0, 100]). |
| `ticks` | `number \| number[]` | Tick count or explicit values. |
| `orientation` | `"vertical" \| "horizontal"` | Horizontal fits table cells. |
| `bulb` | `boolean` | Draw the reservoir bulb (default true). |
| `fontSize` | `number` | Type size of the tick and value numerals, in viewBox units (default 8). |
| `animate` | `boolean` | (interactive) Opt-in entrance motion when the chart mounts client-side — add `import "@microcharts/react/motion"` once. Inert on the server, on hydrated server HTML, and under `prefers-reduced-motion`. |

Plus the shared grammar — `data`, `domain`, `color`, `title`, `summary`, `format` — and the layout props (`width`, `height`, `className`, `style`) that every chart accepts. Interactive entries also share `animate` and `live`, and — wherever a chart has more than one navigable unit — `onActive`, `onSelect`, `selectedIndex` and `defaultSelectedIndex`; and — wherever the chart shows a hover value — `readout`. See [the shared grammar](/docs/quickstart#the-shared-grammar).
