---
title: "Callout"
description: "A short piece of set-apart information that helps you understand what you are reading without claiming anything about your health. A note, a tip or a caveat."
url: "https://opsinjs.pensievelabs.org/components/callout"
source: "https://opsinjs.pensievelabs.org/components/callout.md"
section: "Components"
status: "shipped"
kind: "component"
category: "feedback"
reviewed: "2026-09-20"
reviewer: "engineering"
aliases: ["admonition", "note", "info box", "aside", "note box", "tip"]
usedIn: ["alert-escalation", "choose-a-component", "consent-and-permissions", "consent-flow", "diabetes-medicines-app", "empty-and-first-use", "forms/error-summaries", "offline-and-stale-data", "onboarding-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="callout"
  status="shipped"
  questions="[
  &#x22;An axis colour passed through className is stripped in every build, but a consumer's own stylesheet or style attribute is not, and nothing checks consumer code.&#x22;,
  &#x22;The root is a role=\&#x22;note\&#x22; named by its title, and nobody has listened to it in VoiceOver or TalkBack, or to a screen carrying several.&#x22;,
  &#x22;Nothing here is dismissible, and a dismiss control would need a name, a 44pt target and a memory that outlives the render, none of them designed.&#x22;,
]"
>
  Audited against WCAG 2.2 AA. Clinical review pending.
</StubNotice>

## Preview [#preview]

<ComponentPreview name="callout" />

## Installation [#installation]

<ComponentInstall name="callout" unbuilt="false" importPath="@/components/ui/callout" dependencies="[&#x22;lucide-react&#x22;]" />

## Usage [#usage]

```tsx
import { Callout } from "@/components/ui/callout"
```

```tsx
<Callout variant="caveat" title="What this average leaves out">
  This average does not include days when you did not wear the device.
</Callout>
```

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

<WhenToUse
  use="[
  &#x22;Setting apart a note, a tip or a caveat that helps the reader understand the content around it.&#x22;,
  &#x22;General health information that is true for everybody and is not about this reader's data.&#x22;,
  &#x22;Explaining a limitation of what is on screen: how a figure was worked out, what it leaves out, where it came from.&#x22;,
]"
  avoid="[
  { case: &#x22;The message is about this reader's data and carries a level of attention.&#x22;, instead: &#x22;status-pill&#x22; },
  { case: &#x22;Something has changed and the reader needs to know before carrying on.&#x22;, instead: &#x22;alert-banner&#x22; },
  { case: &#x22;The reader is being asked to do something by a certain time.&#x22;, instead: &#x22;care-card&#x22; },
  { case: &#x22;It is the standing statement about what this product is not.&#x22;, instead: &#x22;disclaimer-note&#x22; },
  { case: &#x22;You are explaining a single word rather than a passage.&#x22;, instead: &#x22;term&#x22; },
]"
/>

## Anatomy [#anatomy]

<Anatomy
  name="callout"
  parts="[
  {
    name: &#x22;Callout&#x22;,
    describes: &#x22;The root, a role=\&#x22;note\&#x22; named by its title. One neutral treatment for every variant, from neither colour axis.&#x22;,
    prop: &#x22;className&#x22;,
  },
  {
    name: &#x22;Callout.Icon&#x22;,
    describes: &#x22;One distinct glyph per variant, never a status glyph. aria-hidden and sized in em.&#x22;,
    prop: &#x22;variant&#x22;,
  },
  {
    name: &#x22;Callout.Title&#x22;,
    describes: &#x22;Optional styled text, never an h1 to h6. Absent from the DOM when empty or whitespace.&#x22;,
    prop: &#x22;title&#x22;,
  },
  {
    name: &#x22;Callout.Body&#x22;,
    describes: &#x22;One short paragraph at the body type step. There is no actions slot.&#x22;,
    prop: &#x22;children&#x22;,
  },
]"
/>

## Examples [#examples]

### Next to what it explains [#next-to-what-it-explains]

One callout per surface, directly under the thing it annotates, saying something specific and checkable. `title` names what the reader is about to be told.

<ComponentPreview name="callout-next-to-what-it-explains" kind="example" align="start" />

### On a card [#on-a-card]

The fill sets the callout apart, so `bg-card` behind it changes how much work the boundary does. The caller passes `rounded-opsin-xs` through `className` for the concentric inner radius.

<ComponentPreview name="callout-on-a-card" kind="example" align="start" />

## Content guidelines [#content-guidelines]

Keep it to a short paragraph in the same voice as the surrounding text. The box is the emphasis, so the words need not be. The title names what the reader is about to be told, not the component. The glyph is `aria-hidden`, so the words must carry whatever the variant carries. Do not use a callout to hedge.

<DoDont>
  <DoDont.Do>
    **"This average does not include days when you did not wear the device."** A specific, checkable limitation.
  </DoDont.Do>

  <DoDont.Dont>
    **"Important: your data may be incomplete."** Warning register, no specifics, nothing the reader can do.
  </DoDont.Dont>
</DoDont>

## Accessibility [#accessibility]

This component was audited against WCAG 2.2 AA in a source pass and a rendered pass on `/view/base/base-lyra/component/callout`. The audit is author-run and is not an independent review. Clinical review is still pending.

**What the audit found.** Nothing in the source needed to change. It is a server component with no interactive elements, so the keyboard, focus and control criteria do not apply; `role="note"` gives it a start and end boundary, and a title supplies the accessible name through `aria-label`. It paints no colour from either axis and carries no clinical status, so greyscale and `forced-colors` take away nothing. Type sizes and colours are role tokens, the icon is sized in em, and the body reflows at 200% through `min-w-0` and `wrap-break-word` rather than scrolling.

**What a reader should still know.** The variant glyph is `aria-hidden`, so the note, tip and caveat distinction reaches sighted readers alone. This is deliberate: announcing the glyph name would not convey the sense either, so the copy is required to carry it, and both examples do. The title renders as styled text rather than an `h1` to `h6`, because the component cannot know its nesting level, but it stays exposed to assistive technology as the accessible name. The four contrast pairs this component paints are not yet measured, and the report below says so rather than printing numbers nobody produced.

<KeyboardTable
  name="callout"
  rows="[
  {
    keys: &#x22;Tab&#x22;,
    action: &#x22;Skips the callout&#x22;,
    notes: &#x22;No tab stop. A link inside the body is the stop, not the callout.&#x22;,
  },
  {
    keys: &#x22;Shift + Tab&#x22;,
    action: &#x22;Skips the callout&#x22;,
    notes: &#x22;Same in reverse. Never in the focus order.&#x22;,
  },
  {
    keys: &#x22;Escape&#x22;,
    action: &#x22;Does nothing&#x22;,
    notes: &#x22;There is no dismiss control, so nothing closes.&#x22;,
  },
]"
/>

<ContrastReport component="callout" />

## API reference [#api-reference]

<PropsTable name="CalloutProps" />

There is no `status`, `severity`, `color` or `warning` variant, and no `action` or `dismissible`. The variant union is not exported, so a wrapper writes `CalloutProps["variant"]`. An axis colour in `className` is stripped before it reaches the root, and development warns once per offending class list.

## Related [#related]

* [AlertBanner](./alert-banner.mdx) is the clinical, interruptive counterpart and the component Callout is most often mistaken for.
* [StatusPill](./status-pill.mdx) states a clinical level directly, as a word, a glyph and a colour together.
* [CareCard](./care-card.mdx) is for when there is something to do and somebody asking.
* [DisclaimerNote](./disclaimer-note.mdx) is the standing statement about the product itself.
* [Term](./term.mdx) explains a single word rather than a passage.
