Callout
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.
Preview
callout · base base · style base-lyraOpen under the product themeInstallation
pnpm dlx shadcn@latest add @opsinjs/callout
The @opsinjs namespace is declared in your components.json. Everything it installs is code you then own. There is no runtime package to keep in step.
Usage
import { Callout } from "@/components/ui/callout"<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
Use it when
- Setting apart a note, a tip or a caveat that helps the reader understand the content around it.
- General health information that is true for everybody and is not about this reader's data.
- Explaining a limitation of what is on screen: how a figure was worked out, what it leaves out, where it came from.
Do not use it when
- The message is about this reader's data and carries a level of attention.Use
status-pillinstead. - Something has changed and the reader needs to know before carrying on.Use
alert-bannerinstead. - The reader is being asked to do something by a certain time.Use
care-cardinstead. - It is the standing statement about what this product is not.Use
disclaimer-noteinstead. - You are explaining a single word rather than a passage.Use
terminstead.
Anatomy
CalloutThe root, a role="note" named by its title. One neutral treatment for every variant, from neither colour axis.Controlled byclassNameCallout.IconOne distinct glyph per variant, never a status glyph. aria-hidden and sized in em.Controlled byvariantCallout.TitleOptional styled text, never an h1 to h6. Absent from the DOM when empty or whitespace.Controlled bytitleCallout.BodyOne short paragraph at the body type step. There is no actions slot.Controlled bychildren
Examples
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.
callout-next-to-what-it-explains · base base · style base-lyraOpen under the product themeOn 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.
callout-on-a-card · base base · style base-lyraOpen under the product themeContent 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.
Do
"This average does not include days when you did not wear the device." A specific, checkable limitation.
Don’t
"Important: your data may be incomplete." Warning register, no specifics, nothing the reader can do.
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.
| Key | Action | Notes |
|---|---|---|
| Tab | Skips the callout | No tab stop. A link inside the body is the stop, not the callout. |
| Shift+Tab | Skips the callout | Same in reverse. Never in the focus order. |
| Escape | Does nothing | There is no dismiss control, so nothing closes. |
| Pair | Theme | APCA Lc | WCAG 2.2 | Floor |
|---|---|---|---|---|
| body text on the page | light | 101.6 | 17.18:1 | Pass |
| body text on the page | dark | -100.5 | 18.00:1 | Pass |
| body text on a card | light | 104.7 | 17.96:1 | Pass |
| body text on a card | dark | -99.6 | 16.32:1 | Pass |
| body text on the muted ground | light | 98.1 | 16.32:1 | Pass |
| body text on the muted ground | dark | -97.5 | 13.77:1 | Pass |
| secondary text on the page | light | 83.9 | 7.42:1 | Pass |
| secondary text on the page | dark | -80.7 | 13.44:1 | Pass |
| secondary text on a card | light | 87.0 | 7.76:1 | Pass |
| secondary text on a card | dark | -79.8 | 12.19:1 | Pass |
| secondary text on the muted ground | light | 80.4 | 7.05:1 | Pass |
| secondary text on the muted ground | dark | -77.8 | 10.28:1 | Pass |
| a hairline boundary on the page | light | 19.3 | 1.41:1 | Below floor |
| a hairline boundary on the page | dark | -8.2 | 1.90:1 | Below floor |
| a hairline boundary on a card | light | 22.4 | 1.47:1 | Below floor |
| a hairline boundary on a card | dark | -7.3 | 1.72:1 | Below floor |
| a hairline boundary on the muted ground | light | 15.8 | 1.34:1 | Below floor |
| a hairline boundary on the muted ground | dark | 0.0 | 1.45:1 | Below floor |
| a placeholder boundary on the page | light | 69.8 | 4.52:1 | Pass |
| a placeholder boundary on the page | dark | -51.0 | 7.62:1 | Pass |
| a placeholder boundary on a card | light | 72.8 | 4.72:1 | Pass |
| a placeholder boundary on a card | dark | -50.1 | 6.91:1 | Pass |
| a placeholder boundary on the muted ground | light | 66.2 | 4.29:1 | Pass |
| a placeholder boundary on the muted ground | dark | -48.1 | 5.83:1 | Pass |
| a placeholder fill on the page | light | 8.3 | 1.18:1 | Below floor |
| a placeholder fill on the page | dark | -8.2 | 1.90:1 | Below floor |
| a placeholder fill on a card | light | 11.4 | 1.23:1 | Below floor |
| a placeholder fill on a card | dark | -7.3 | 1.72:1 | Below floor |
| a placeholder fill on the muted ground | light | 0.0 | 1.12:1 | Below floor |
| a placeholder fill on the muted ground | dark | 0.0 | 1.45:1 | Below floor |
| a placeholder boundary on its own fill | light | 58.9 | 3.84:1 | Pass |
| a placeholder boundary on its own fill | dark | -41.5 | 4.01:1 | Below floor |
| the card hairline on the page | light | 69.8 | 4.52:1 | Pass |
| the card hairline on the page | dark | -28.7 | 4.19:1 | Below floor |
| the card hairline on a card | light | 72.8 | 4.72:1 | Pass |
| the card hairline on a card | dark | -27.9 | 3.80:1 | Below floor |
These are the measured token pairs this component draws colour from, not a measurement of the component itself.
API reference
Prop
Type
Generated from CalloutProps in registry/bases/base/callout.tsx.
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
- AlertBanner is the clinical, interruptive counterpart and the component Callout is most often mistaken for.
- StatusPill states a clinical level directly, as a word, a glyph and a colour together.
- CareCard is for when there is something to do and somebody asking.
- DisclaimerNote is the standing statement about the product itself.
- Term explains a single word rather than a passage.
Dialog
A window that opens over the page and waits for an answer. It takes your keyboard and attention until you deal with it, and gives them back where it found them.
EmptyState
What a screen shows before there is anything on it. It says plainly why it is empty and gives you one clear thing to do about it.