---
title: "Trend review"
description: "Reading change over time without over-interpreting noise. Minimum windows, honest axes, and separating direction from valence."
url: "https://opsinjs.pensievelabs.org/patterns/trend-review"
source: "https://opsinjs.pensievelabs.org/patterns/trend-review.md"
section: "Patterns"
kind: "pattern"
reviewed: "2026-09-20"
reviewer: "clinical"
aliases: ["trends over time", "change over time", "chart review", "is it going up", "noise vs signal"]
implements: ["trend-sparkline", "metric-tile", "range-bar", "value", "relative-time", "status-pill", "empty-state"]
---

> Elements written as `<PascalCase … />` below are opsinjs documentation
> components. Their attributes are the content: the values they render are
> generated from `tokens/*.json` and `registry/catalogue.ts` and are
> published separately at https://opsinjs.pensievelabs.org/r/index.json and under the Reference
> section.
> Nothing is missing from this page. The data simply does not live in
> the prose.

<PageTemplate kind="pattern" />

## When to use [#when-to-use]

Use this pattern when the question the reader has is not "what is my number" but
"is this getting better or worse".

Trend surfaces are where consumer health products do their most confident lying.
Three points and a line will produce a trend for any dataset; a y-axis truncated
to the data range will make a two per cent drift look like a cliff; a
seven-day average recomputed nightly will change direction on Tuesday and back
again on Thursday. None of this requires bad intent. It is what happens by
default.

Signals you are in this pattern:

* The reader is comparing a value to their own history rather than to a range.
* The underlying measure has real day-to-day variation.
* Someone might change a behaviour, or a dose, based on what the line does.

## When not to use [#when-not-to-use]

* **One value, one range.** If the question is "is this reading where it should
  be", that is [Result disclosure](./result-disclosure.mdx) and a `RangeBar`.
* **Fewer points than your minimum window.** Below the window, do not draw a
  trend at all. Draw the points and say so. This is an
  [Empty and first-use](./empty-and-first-use.mdx) surface, not a sparse chart.
* **The change itself is the alert.** If a sustained direction crosses a
  threshold that requires action, that is
  [Alert escalation](./alert-escalation.mdx); the chart is not the delivery
  mechanism.
* **You want chart mechanics.** Axis rules, colour and the table twin are
  [Data visualisation](../foundations/data-visualisation/index.mdx). Category
  colours are not chart colours, and that page says why.
* **You want the doctrine.** Minimum windows, direction versus valence and the
  prohibition on inferring cause are
  [Trends and change](../health/trends-and-change.mdx).

## How it works [#how-it-works]

<FlowDiagram>
  {`flowchart TD
    A["Reader opens the metric"] --> B{"Enough points for the minimum window?"}
    B -->|"no"| C["Show the points, state how many more are needed"]
    B -->|"yes"| D["Draw the series with a full, honest axis"]
    D --> E{"Is the change larger than the measure's own variation?"}
    E -->|"no"| F["Describe as steady; do not name a direction"]
    E -->|"yes"| G["Name the direction and the window in words"]
    G --> H{"Does the direction have a clinical valence here?"}
    H -->|"no"| I["Direction only, neutral colour"]
    H -->|"yes"| J["Direction plus status, from the status axis"]
    C --> K["Table twin and export available at every state"]
    F --> K
    I --> K
    J --> K`}
</FlowDiagram>

<NotBuiltYet />

The load-bearing rules:

* **A minimum window before the word "trend" is used.** The window is a property
  of the measure, not of the chart component, and it is owned by whoever owns
  the thresholds. Below it, the surface shows observations, not a direction.
* **The axis includes the range, not just the data.** Truncating the y-axis to
  the observed values is the single most effective way to manufacture alarm from
  noise, and it is the default in most charting libraries.
* **Direction and valence are separate.** "Down" is not "worse". Weight down,
  resting heart rate down and haemoglobin down are three different stories, and
  only the third has an obvious valence. The arrow describes the data; the
  status colour, where there is one, comes from the status axis and is owned
  by [Clinical status semantics](../health/clinical-status-semantics.mdx).
  Never let the sparkline's own colour carry the verdict.
* **The words say what the picture says.** Every trend surface carries a
  sentence stating the direction, the size and the window in plain English. A
  reader who cannot see the chart, or cannot read a chart, gets the same answer.
* **No causal language, ever.** "Your sleep improved because you walked more" is
  a claim the product cannot support from observational self-tracked data. State
  the two series; let the reader draw the arrow.
* **Gaps are gaps.** Missing days are not zeroes and not interpolated. A line
  that bridges a three-week gap is asserting three weeks of data that do not
  exist.

### States [#states]

| State            | Behaviour                                                                                                  |
| ---------------- | ---------------------------------------------------------------------------------------------------------- |
| Below the window | Points only, with the count needed to draw a trend                                                         |
| Sparse           | Series drawn with gaps visibly broken, not bridged                                                         |
| Noisy            | Direction withheld; the sentence says the variation exceeds the change                                     |
| Clear            | Direction, magnitude and window stated in words and in the chart                                           |
| Stale            | Last-updated stamp promoted, direction demoted; see [Offline and stale data](./offline-and-stale-data.mdx) |
| Mixed units      | Series split, never rescaled onto one axis silently                                                        |

## Content [#content]

The caption is the deliverable. The chart is illustration.

<DoDont>
  <DoDont.Do>
    "Over the last 14 days your resting heart rate has been between 58 and 66,
    with no clear direction."
  </DoDont.Do>

  <DoDont.Dont>
    "Your resting heart rate is trending up ↑" It comes from four points
    spanning a range narrower than the sensor's own error.
  </DoDont.Dont>
</DoDont>

<DoDont>
  <DoDont.Do>
    "Your weekly average has fallen by about 3 kg since June." Magnitude,
    window, and a verb that describes the number.
  </DoDont.Do>

  <DoDont.Dont>
    "Great progress. Keep it up!" Praise attached to a body measurement, which
    becomes a rebuke the week it reverses.
  </DoDont.Dont>
</DoDont>

Number formatting, rounding and precision follow
[Numbers, units and precision](../health/numbers-units-precision.mdx). Relative
dates ("since June", "14 days ago") follow
[Numbers, dates and time](../content/numbers-dates-and-time.mdx).

## Accessibility [#accessibility]

* **Every chart ships a table twin.** It is an equivalent rather than a
  download, reachable in the same place, with the same numbers. See
  [Accessible charts](../foundations/data-visualisation/accessible-charts.mdx).
* **The summary sentence is the alt text.** The chart's accessible description
  is the same sentence a sighted reader gets, not "line chart showing resting
  heart rate".
* **Direction is never colour alone.** Grayscale must preserve the answer; the
  word carries it. Verify with
  [Colour independence](../accessibility/colour-independence.mdx).
* **Interactive points are keyboard reachable** with a defined order, and the
  focused point announces its value, its unit and its date rather than its
  pixel position.
* **Zoom and pinch are additive.** The default view must already be readable at
  200% text; see [Text resizing and
  zoom](../accessibility/text-resizing-and-zoom.mdx).
* **No motion on load carrying meaning.** A line that animates left-to-right is
  decoration; under `prefers-reduced-motion` it is absent, and nothing about the
  reader's understanding may depend on having seen it.

## Research [#research]

<ResearchNote evidence="opinion" date="2026-09-02">
  The prohibition on truncated y-axes in a health context, the minimum-window
  rule and the separation of direction from valence are design opinions held
  strongly enough to be enforced. They are grounded in the general graph-literacy
  literature and in how badly the defaults behave, not in a health-specific
  trial that we can point you at.

  We are deliberately not citing a number for how many points constitute a
  trend. It depends entirely on the measure's within-person variation, and any
  universal figure would be false precision of exactly the kind this page
  objects to. The window belongs to whoever owns the thresholds.

  What would change our mind: evidence that full-range axes reduce engagement
  enough to cost adherence in a population that benefits from tracking. That
  would be a genuine trade-off rather than a reason to truncate quietly.
</ResearchNote>

## Updates to this page [#updates-to-this-page]

<Reviewed />
