---
title: "SourceCitation"
description: "Where a piece of health information came from, in plain words, with an optional link to the fuller citation and an optional date it was last checked."
url: "https://opsinjs.pensievelabs.org/components/source-citation"
source: "https://opsinjs.pensievelabs.org/components/source-citation.md"
section: "Components"
status: "shipped"
kind: "component"
category: "health-communication"
reviewed: "2026-09-20"
reviewer: "engineering"
aliases: ["citation", "evidence link", "reviewed by"]
governedBy: ["data-provenance-and-device-accuracy", "evidence-and-references"]
usedIn: ["diabetes-medicines-app", "results-screen"]
implemented: true
---

> 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.
> `<StubNotice>` IS THE EXCEPTION, AND IT IS THE ONE TO READ. It is a
> paired element rather than a self-closing one, and the text between
> its opening and closing tags is prose an author wrote, reproduced
> below word for word. That prose is where this page says whether the
> component has been reviewed. Read the children, not only the
> attributes.

<StubNotice
  name="source-citation"
  status="shipped"
  questions="[
  &#x22;Whether printing an admission is right for a value whose provenance went missing, or whether the product should refuse to render that value at all.&#x22;,
  &#x22;Provenance is visible text near the value, not part of its accessible name, so a screen-reader user reaching the value alone does not hear its origin.&#x22;,
  &#x22;No contrast pair for the source ink or the quieter date ink has been measured on the surfaces a citation sits on.&#x22;,
]"
>
  Audited against WCAG 2.2 AA. Clinical review pending.
</StubNotice>

## Preview [#preview]

<ComponentPreview name="source-citation" />

## Installation [#installation]

<ComponentInstall name="source-citation" unbuilt="false" importPath="@/components/ui/source-citation" registryDependencies="[&#x22;link&#x22;]" />

## Usage [#usage]

```tsx
import { SourceCitation } from "@/components/ui/source-citation"
```

```tsx
<SourceCitation
  source="Estimated by your watch from movement and heart rate."
  more={{ label: "Read how this is worked out", href: "/how-it-works" }}
  checkedOn="2026-03-14"
  locale="en-GB"
/>
```

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

<WhenToUse
  use="[
  &#x22;Beside a health value whose provenance a reader needs to weigh it: a wearable estimate, a home device reading, a typed figure.&#x22;,
  &#x22;To attribute and link a manufacturer's accuracy document or a study you have read, rather than restating its claim as your own.&#x22;,
  &#x22;Wherever a number's origin bounds what may be said about it, so origin travels with the number where it is read.&#x22;,
]"
  avoid="[
  { case: &#x22;The message is a standing statement about the whole product, rather than the source of one value.&#x22;, instead: &#x22;disclaimer-note&#x22; },
  { case: &#x22;You are saying when a reading was taken or synced, rather than where it came from.&#x22;, instead: &#x22;relative-time&#x22; },
  { case: &#x22;You are explaining what a term means, rather than where a value came from.&#x22;, instead: &#x22;term&#x22; },
  { case: &#x22;The note is information about the content with no source to attribute.&#x22;, instead: &#x22;callout&#x22; },
]"
/>

## Clinical meaning [#clinical-meaning]

**Asserts.** Where a health value came from, in the product's own plain words, with an optional checked date and a link to the fuller source.

**Never read as.** A guarantee that the source is right, current, or checked by anybody. `checkedOn` is the date the product recorded. A value old enough to doubt is wrapped in [RelativeTime](./relative-time.mdx) beside it.

**Colour axis.** Neither: no `status` prop and no `category` prop, and a class from either axis raises a development warning. See [The two colour axes](../health/two-colour-axes.mdx).

**Thresholds.** None. No reference range, no threshold, no verdict, and no clinical number passes through.

**Who owns the words.** The product. [Data provenance and device accuracy](../health/data-provenance-and-device-accuracy.mdx) sets ten rules, and this component carries two. Rule 2 puts provenance in plain words at the value. Rule 6 attributes a manufacturer's claim rather than restating it. The other eight stay the caller's, and it renders a wrong string as readily as a right one. opsinjs ships no source, no date, no DOI and no study. See [Evidence and references](../health/evidence-and-references.mdx).

<SafetyCallout severity="attention" evidence="opinion">
  Never restate a manufacturer's accuracy figure as your own, and never invent one. Attribute the claim and link it through `more`, in the manufacturer's own terms.
</SafetyCallout>

## Anatomy [#anatomy]

<Anatomy
  name="source-citation"
  parts="[
  {
    name: &#x22;SourceCitation&#x22;,
    describes: &#x22;The root. Quiet and outside both colour axes: no fill, no radius, no role.&#x22;,
    prop: &#x22;className&#x22;,
  },
  {
    name: &#x22;SourceCitation.Source&#x22;,
    describes: &#x22;The product's plain words for where the value came from. Given nothing, it prints one line saying so.&#x22;,
    prop: &#x22;source&#x22;,
  },
  {
    name: &#x22;SourceCitation.Checked&#x22;,
    describes: &#x22;The checked date, one line down in quieter ink. The component prefixes it; a time element carries the machine date.&#x22;,
    prop: &#x22;checkedOn&#x22;,
  },
  {
    name: &#x22;SourceCitation.Link&#x22;,
    describes: &#x22;The fuller citation. Label and destination arrive together, on its own line, keeping the full target size.&#x22;,
    prop: &#x22;more&#x22;,
  },
]"
/>

## Examples [#examples]

### A device estimate naming its own method [#a-device-estimate-naming-its-own-method]

The provenance names the device and the class of the figure, so it reads as an estimate. `more` points at the fuller accuracy document.

<ComponentPreview name="source-citation-a-device-estimate" kind="example" align="start" />

### When nobody said where it came from [#when-nobody-said-where-it-came-from]

With no `source`, the citation prints an admission rather than rendering empty. Markup you wrap a source in is taken at its word.

<ComponentPreview name="source-citation-no-source-supplied" kind="example" align="start" />

## Content guidelines [#content-guidelines]

Name the concrete thing rather than the brand's marketing, and say the class of the figure: an estimate is called an estimate. Name the link for where it goes, since a screen reader may list it alone. A vague label raises a development warning in English only.

<DoDont>
  <DoDont.Do>
    **"Estimated by your watch from movement and heart rate."** Names the device, the inputs and the class of the figure.
  </DoDont.Do>

  <DoDont.Dont>
    **"Powered by clinically validated technology."** Names no device and no method.
  </DoDont.Dont>
</DoDont>

## Accessibility [#accessibility]

This component was audited against WCAG 2.2 AA in two passes, one over the source and one over the rendered output. The audit is author-run, not independent, and clinical review is still pending. Both passes were clean: no axe violations, target sizes met, focus visible, and no loss at 320px reflow.

* No `px` type size. Both lines are set at `text-opsin-footnote`, a rem from the generated ramp.
* No raw colour literal and no ramp step. The source ink is `--foreground` and the date ink is `--muted-foreground`.
* `more` carries the label and the href together, so a link cannot be added without a label. An external href adds an off-screen "opens in a new tab".
* The link is not inside a sentence, so it re-adds `--opsin-target-minimum` on both axes. See [Target size and motor](../accessibility/target-size-and-motor.mdx).
* It reflows at 200% text: no fixed height, `min-w-0` with `wrap-break-word`, so a long device name breaks.

**Residual, left for you to weigh.**

* The source line, the last-checked time and the fuller-citation link render as loose siblings with no programmatic group binding them into one citation. Reading order keeps the association, so this is not an AA failure.
* The "Last checked on" chrome is always English while the date follows the caller's `locale`; when they differ, the localized date carries no `lang` seam yet, per ADR 0005.

<KeyboardTable
  name="source-citation"
  rows="[
  {
    keys: &#x22;Tab&#x22;,
    action: &#x22;Moves to the link, when there is one&#x22;,
    notes: &#x22;The citation is text, not a tab stop. The link is its only focusable element.&#x22;,
  },
  {
    keys: &#x22;Shift + Tab&#x22;,
    action: &#x22;Moves back out of the link&#x22;,
    notes: &#x22;Reverse order matches visual order. The link is last.&#x22;,
  },
  {
    keys: &#x22;Enter&#x22;,
    action: &#x22;Follows the link&#x22;,
    notes: &#x22;Only when a link is present. An ordinary anchor, so Enter and not Space.&#x22;,
  },
  {
    keys: &#x22;Space&#x22;,
    action: &#x22;Scrolls the page, as it does anywhere else&#x22;,
    notes: &#x22;Nothing here captures Space or activates on it.&#x22;,
  },
]"
/>

<ContrastReport component="source-citation" />

## API reference [#api-reference]

<PropsTable name="SourceCitationProps" />

`source` is typed optional but required by the contract, so the missing state can render. `checkedOn` is a strict `YYYY-MM-DD` string or epoch milliseconds, both pinned to UTC. A value that is not a real calendar date is refused, not rendered as a plausible wrong one.

## Related [#related]

* [DisclaimerNote](./disclaimer-note.mdx): the standing statement about the whole product, not one value.
* [RelativeTime](./relative-time.mdx): when a reading was taken or synced, not where it came from.
* [Callout](./callout.mdx): information about the content with no source to attribute.
* [Term](./term.mdx): what a word means, rather than where a value came from.
