opsinjs
ComponentsHealth communication

CareCard

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.

Preview

Theme
Density
Text
Status
care-card · base base · style base-lyraOpen under the product theme

Installation

pnpm dlx shadcn@latest add @opsinjs/care-card

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 { CareCard } from "@/components/ui/care-card"
<CareCard
  heading="Book a repeat blood test"
  urgency="this-week"
  attribution="Your clinic asks"
/>

When to use it

Use it when

  • The reader must do something, and the product can say what, by when and who asks.
  • A result or a change produced a next step that would otherwise be buried in a paragraph.
  • Aftercare, preparation and follow-up instructions for one person at one moment.

Do not use it when

  • Nothing needs doing, or the content is general information not addressed to this reader.Use callout instead.
  • The message must interrupt because it has only just become true.Use alert-banner instead.
  • You only need to label how much attention a result needs.Use status-pill instead.
  • The instruction is the standing not-medical-advice note that appears on every result.Use disclaimer-note instead.
  • The reader must respond before they can do anything else.Use dialog instead.

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. Surface, boundary and heading take no category colour. See The two colour axes.

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, Alarm fatigue and Emergency and escalation.

No clock. Do this today still says so after midnight. overdue is a product-supplied boolean. See Clinical interaction guidelines.

Anatomy

  1. CareCardA section named by its heading. No colour from either axis.Controlled by heading
  2. CareCard.HeadingThe instruction, and the accessible name.Controlled by headingLevel
  3. CareCard.TimingOne of three fixed phrases inside the heading. Unknown urgency renders none.Controlled by urgency
  4. CareCard.AttributionWho is asking. With no author, the slot says so.Controlled by attribution
  5. StatusPillThe only status-coloured element, carrying word, glyph and data-status.Controlled by status
  6. CareCard.ReasonWhat prompted the instruction.Controlled by reason
  7. CareCard.DeadlineThe due date in a time element, plus words for a passed date.Controlled by dueBy
  8. CareCard.ActionsAt most two controls: the recommended bordered, the alternative underlined. Extras drop.Controlled by actions
  • CareCard 1data-slot="care-card"; a section with aria-label
    • Card 1data-slot="card"
      • CareCard.Heading 0..1data-slot="care-card-heading"; h2 to h6
        • CareCard.Timing 0..1data-slot="care-card-timing"
      • CareCard.Attribution 1data-slot="care-card-attribution"
      • StatusPill 0..1data-slot="status-pill", data-status="<level>"
      • CareCard.Reason 0..1data-slot="care-card-reason"
      • CareCard.Deadline 0..1data-slot="care-card-deadline"
      • CareCard.Actions 0..1data-slot="care-card-actions"; 0 to 2 children

Examples

A steady card that still asks for something

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

Theme
Density
Text
Status
care-card-steady-and-still-asking · base base · style base-lyraOpen under the product theme

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.

Theme
Density
Text
Status
care-card-a-deadline-that-has-passed · base base · style base-lyraOpen under the product theme

The author is missing

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

Theme
Density
Text
Status
care-card-no-author-supplied · base base · style base-lyraOpen under the product theme

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.

Do

"Book a repeat blood test", with an author, a reason and a date.

Don’t

"Action required: abnormal result. Contact your provider." No author, a banned word, no reason, no timing.

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 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.

KeyActionNotes
TabMoves to the first action, then to the secondThe card is not focusable.
Shift+TabMoves back through the actions, then out of the cardActions keep DOM order.
EnterActivates the focused actionNo action is destructive: no dismiss, snooze or delete.
SpaceActivates a focused action that is a buttonAn href action is a link, so Space scrolls.
Arrow keysNothingNot a composite widget.
EscapeNothingNot an overlay.
PairThemeAPCA LcWCAG 2.2Floor
body text on the pagelight101.617.18:1Pass
body text on the pagedark-100.518.00:1Pass
body text on a cardlight104.717.96:1Pass
body text on a carddark-99.616.32:1Pass
body text on the muted groundlight98.116.32:1Pass
body text on the muted grounddark-97.513.77:1Pass
secondary text on the pagelight83.97.42:1Pass
secondary text on the pagedark-80.713.44:1Pass
secondary text on a cardlight87.07.76:1Pass
secondary text on a carddark-79.812.19:1Pass
secondary text on the muted groundlight80.47.05:1Pass
secondary text on the muted grounddark-77.810.28:1Pass
a hairline boundary on the pagelight19.31.41:1Below floor
a hairline boundary on the pagedark-8.21.90:1Below floor
a hairline boundary on a cardlight22.41.47:1Below floor
a hairline boundary on a carddark-7.31.72:1Below floor
a hairline boundary on the muted groundlight15.81.34:1Below floor
a hairline boundary on the muted grounddark0.01.45:1Below floor
a placeholder boundary on the pagelight69.84.52:1Pass
a placeholder boundary on the pagedark-51.07.62:1Pass
a placeholder boundary on a cardlight72.84.72:1Pass
a placeholder boundary on a carddark-50.16.91:1Pass
a placeholder boundary on the muted groundlight66.24.29:1Pass
a placeholder boundary on the muted grounddark-48.15.83:1Pass
a placeholder fill on the pagelight8.31.18:1Below floor
a placeholder fill on the pagedark-8.21.90:1Below floor
a placeholder fill on a cardlight11.41.23:1Below floor
a placeholder fill on a carddark-7.31.72:1Below floor
a placeholder fill on the muted groundlight0.01.12:1Below floor
a placeholder fill on the muted grounddark0.01.45:1Below floor
a placeholder boundary on its own filllight58.93.84:1Pass
a placeholder boundary on its own filldark-41.54.01:1Below floor
the card hairline on the pagelight69.84.52:1Pass
the card hairline on the pagedark-28.74.19:1Below floor
the card hairline on a cardlight72.84.72:1Pass
the card hairline on a carddark-27.93.80:1Below 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 CareCardProps in registry/bases/base/care-card.tsx.

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

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.

  • AlertBanner is an announcement that something changed, not an instruction with a deadline.
  • Callout covers information with no verb and no author.
  • ResultCard is the measurement that prompted the instruction.
  • DisclaimerNote is the standing note about what the product is not, never an instruction.

On this page