---
title: "CareCard"
description: "A card that tells you what to do and when. It carries one instruction at one of three levels of urgency, with the urgency stated in the heading, not implied."
url: "https://opsinjs.pensievelabs.org/components/care-card"
source: "https://opsinjs.pensievelabs.org/components/care-card.md"
section: "Components"
status: "shipped"
kind: "component"
category: "health-communication"
reviewed: "2026-09-20"
reviewer: "engineering"
aliases: ["next steps", "what to do", "advice", "action card", "what to do next", "advice card", "guidance card"]
governedBy: ["two-colour-axes", "clinical-status-semantics", "alarm-fatigue", "clinical-interaction-guidelines", "emergency-and-escalation"]
usedIn: ["alert-escalation", "ask-users-for/contact-details", "ask-users-for/symptoms", "choose-a-component", "consent-and-permissions", "diabetes-medicines-app", "empty-and-first-use", "offline-and-stale-data", "onboarding-and-first-run", "onboarding-screen", "result-disclosure", "results-screen", "sharing-with-a-clinician", "staged-alert"]
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="care-card"
  status="shipped"
  questions="[
  &#x22;Whether an inconsistent urgency and status pair should warn, or whether warning would assert the mapping opsinjs refused to ship.&#x22;,
  &#x22;The card reads no clock, so one left open overnight says today about yesterday, and who re-renders it is undecided.&#x22;,
  &#x22;An urgent card may say ring for help, yet a tel: action is a plain link with no number and no emergency mode.&#x22;,
]"
>
  Audited against WCAG 2.2 AA. Clinical review pending.
</StubNotice>

## Preview [#preview]

<ComponentPreview name="care-card" />

## Installation [#installation]

<ComponentInstall name="care-card" unbuilt="false" importPath="@/components/ui/care-card" registryDependencies="[&#x22;card&#x22;, &#x22;button&#x22;, &#x22;status-pill&#x22;, &#x22;link&#x22;]" />

## Usage [#usage]

```tsx
import { CareCard } from "@/components/ui/care-card"
```

```tsx
<CareCard
  heading="Book a repeat blood test"
  urgency="this-week"
  attribution="Your clinic asks"
/>
```

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

<WhenToUse
  use="[
  &#x22;The reader must do something, and the product can say what, by when and who asks.&#x22;,
  &#x22;A result or a change produced a next step that would otherwise be buried in a paragraph.&#x22;,
  &#x22;Aftercare, preparation and follow-up instructions for one person at one moment.&#x22;,
]"
  avoid="[
  { case: &#x22;Nothing needs doing, or the content is general information not addressed to this reader.&#x22;, instead: &#x22;callout&#x22; },
  { case: &#x22;The message must interrupt because it has only just become true.&#x22;, instead: &#x22;alert-banner&#x22; },
  { case: &#x22;You only need to label how much attention a result needs.&#x22;, instead: &#x22;status-pill&#x22; },
  { case: &#x22;The instruction is the standing not-medical-advice note that appears on every result.&#x22;, instead: &#x22;disclaimer-note&#x22; },
  { case: &#x22;The reader must respond before they can do anything else.&#x22;, instead: &#x22;dialog&#x22; },
]"
/>

## Clinical meaning [#clinical-meaning]

**Asserts.** That the named author asks this reader to do this, optionally by a stated
date, for a stated reason.

**Never read as.** Advice from a clinician who has assessed this reader, unless the card
says so. `attribution` is required.

**Colour axis.** Status only, on the embedded [StatusPill](./status-pill.mdx). Surface,
boundary and heading take no category colour. See
[The two colour axes](../health/two-colour-axes.mdx).

**Thresholds.** None. `urgency` says when to act, `status` says how much attention the
reading needs, neither is derived from the other, and all fifteen pairs render. See
[Clinical status semantics](../health/clinical-status-semantics.mdx),
[Alarm fatigue](../health/alarm-fatigue.mdx) and
[Emergency and escalation](../health/emergency-and-escalation.mdx).

**No clock.** *Do this today* still says so after midnight. `overdue` is a product-supplied
boolean. See
[Clinical interaction guidelines](../health/clinical-interaction-guidelines.mdx).

<SafetyCallout severity="urgent" evidence="opinion">
  At most one `urgent` surface per screen, counted on the status axis. If the step is ringing
  for help, give the number: the component ships none.
</SafetyCallout>

## Anatomy [#anatomy]

<Anatomy
  name="care-card"
  parts="[
  {
    name: &#x22;CareCard&#x22;,
    describes: &#x22;A section named by its heading. No colour from either axis.&#x22;,
    prop: &#x22;heading&#x22;,
  },
  {
    name: &#x22;CareCard.Heading&#x22;,
    describes: &#x22;The instruction, and the accessible name.&#x22;,
    prop: &#x22;headingLevel&#x22;,
  },
  {
    name: &#x22;CareCard.Timing&#x22;,
    describes: &#x22;One of three fixed phrases inside the heading. Unknown urgency renders none.&#x22;,
    prop: &#x22;urgency&#x22;,
  },
  {
    name: &#x22;CareCard.Attribution&#x22;,
    describes: &#x22;Who is asking. With no author, the slot says so.&#x22;,
    prop: &#x22;attribution&#x22;,
  },
  {
    name: &#x22;StatusPill&#x22;,
    describes: &#x22;The only status-coloured element, carrying word, glyph and data-status.&#x22;,
    prop: &#x22;status&#x22;,
  },
  {
    name: &#x22;CareCard.Reason&#x22;,
    describes: &#x22;What prompted the instruction.&#x22;,
    prop: &#x22;reason&#x22;,
  },
  {
    name: &#x22;CareCard.Deadline&#x22;,
    describes: &#x22;The due date in a time element, plus words for a passed date.&#x22;,
    prop: &#x22;dueBy&#x22;,
  },
  {
    name: &#x22;CareCard.Actions&#x22;,
    describes: &#x22;At most two controls: the recommended bordered, the alternative underlined. Extras drop.&#x22;,
    prop: &#x22;actions&#x22;,
  },
]"
/>

<CompositionTree
  name="care-card"
  tree="[
  {
    part: &#x22;CareCard&#x22;,
    cardinality: &#x22;1&#x22;,
    note: &#x22;data-slot=\&#x22;care-card\&#x22;; a section with aria-label&#x22;,
    children: [
      {
        part: &#x22;Card&#x22;,
        cardinality: &#x22;1&#x22;,
        note: &#x22;data-slot=\&#x22;card\&#x22;&#x22;,
        children: [
          {
            part: &#x22;CareCard.Heading&#x22;,
            cardinality: &#x22;0..1&#x22;,
            note: &#x22;data-slot=\&#x22;care-card-heading\&#x22;; h2 to h6&#x22;,
            children: [{ part: &#x22;CareCard.Timing&#x22;, cardinality: &#x22;0..1&#x22;, note: &#x22;data-slot=\&#x22;care-card-timing\&#x22;&#x22; }],
          },
          { part: &#x22;CareCard.Attribution&#x22;, cardinality: &#x22;1&#x22;, note: &#x22;data-slot=\&#x22;care-card-attribution\&#x22;&#x22; },
          { part: &#x22;StatusPill&#x22;, cardinality: &#x22;0..1&#x22;, note: &#x22;data-slot=\&#x22;status-pill\&#x22;, data-status=\&#x22;<level>\&#x22;&#x22; },
          { part: &#x22;CareCard.Reason&#x22;, cardinality: &#x22;0..1&#x22;, note: &#x22;data-slot=\&#x22;care-card-reason\&#x22;&#x22; },
          { part: &#x22;CareCard.Deadline&#x22;, cardinality: &#x22;0..1&#x22;, note: &#x22;data-slot=\&#x22;care-card-deadline\&#x22;&#x22; },
          { part: &#x22;CareCard.Actions&#x22;, cardinality: &#x22;0..1&#x22;, note: &#x22;data-slot=\&#x22;care-card-actions\&#x22;; 0 to 2 children&#x22; },
        ],
      },
    ],
  },
]"
/>

## Examples [#examples]

### A steady card that still asks for something [#a-steady-card-that-still-asks-for-something]

`status="steady"` with `urgency="when-convenient"` is the routine follow-up.

<ComponentPreview name="care-card-steady-and-still-asking" kind="example" align="start" />

### A deadline the reader is already behind [#a-deadline-the-reader-is-already-behind]

`dueBy` renders as a written date and `overdue` is a product-supplied boolean. No
`urgency` is set; the date is the timing.

<ComponentPreview name="care-card-a-deadline-that-has-passed" kind="example" align="start" />

### The author is missing [#the-author-is-missing]

The same instruction with and without `attribution`. Without one, the card says so.

<ComponentPreview name="care-card-no-author-supplied" kind="example" align="start" />

## Content guidelines [#content-guidelines]

Start with a verb and let the card supply timing. Name the author in the reader's words:
*your GP asks*. Never write *you must*, *you need to*, *failure to* or *it is important
that you*. A telephone action carries the number in its label. Do not compare a reading to
a range the card does not show; see [reference ranges](../health/reference-ranges.mdx).

<DoDont>
  <DoDont.Do>
    **"Book a repeat blood test"**, with an author, a reason and a date.
  </DoDont.Do>

  <DoDont.Dont>
    **"Action required: abnormal result. Contact your provider."** No author, a banned
    word, no reason, no timing.
  </DoDont.Dont>
</DoDont>

## Accessibility [#accessibility]

**Audited against WCAG 2.2 AA.** A source pass covered the rubric and a rendered pass ran
axe, target size, focus visibility and reflow at 320px. The audit is author-run, not
independent, and clinical review is pending.

Confirmed:

* The card is a `section` named by `aria-label` from its heading, so entering it speaks the
  instruction.
* Urgency is text with no glyph or colour, and status is carried by
  [StatusPill](./status-pill.mdx) as word, glyph and tint, never colour alone.
* The card takes no tab stop. Of two actions, the recommended one is set apart by a
  bordered shape and a hidden qualifier, not by colour.

Not confirmed in a browser: the `sm` quiet button and secondary quiet link variants, which
inherit the 44px target floor but were not exercised. Contrast for the action tones and the
timing phrase stays unmeasured, as the report below records.

<KeyboardTable
  name="care-card"
  rows="[
  {
    keys: &#x22;Tab&#x22;,
    action: &#x22;Moves to the first action, then to the second&#x22;,
    notes: &#x22;The card is not focusable.&#x22;,
  },
  {
    keys: &#x22;Shift + Tab&#x22;,
    action: &#x22;Moves back through the actions, then out of the card&#x22;,
    notes: &#x22;Actions keep DOM order.&#x22;,
  },
  {
    keys: &#x22;Enter&#x22;,
    action: &#x22;Activates the focused action&#x22;,
    notes: &#x22;No action is destructive: no dismiss, snooze or delete.&#x22;,
  },
  {
    keys: &#x22;Space&#x22;,
    action: &#x22;Activates a focused action that is a button&#x22;,
    notes: &#x22;An href action is a link, so Space scrolls.&#x22;,
  },
  {
    keys: &#x22;Arrow keys&#x22;,
    action: &#x22;Nothing&#x22;,
    notes: &#x22;Not a composite widget.&#x22;,
  },
  {
    keys: &#x22;Escape&#x22;,
    action: &#x22;Nothing&#x22;,
    notes: &#x22;Not an overlay.&#x22;,
  },
]"
/>

<ContrastReport component="care-card" />

## API reference [#api-reference]

<PropsTable name="CareCardProps" />

`CareUrgency` and `CareAction` are exported from the same file, absent from the table.

```tsx
export type CareUrgency = "when-convenient" | "this-week" | "today"

export interface CareAction {
  label: string
  href?: string
  onSelect?: () => void
  recommended?: boolean
}
```

An `href` action renders a link and an `onSelect` action a button. Given both, the link
wins. `headingLevel` defaults to 3. A `dueBy` without `locale` warns in development.

## Related [#related]

* [AlertBanner](./alert-banner.mdx) is an announcement that something changed, not an instruction with a deadline.
* [Callout](./callout.mdx) covers information with no verb and no author.
* [ResultCard](./result-card.mdx) is the measurement that prompted the instruction.
* [DisclaimerNote](./disclaimer-note.mdx) is the standing note about what the product is not, never an instruction.
