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
care-card · base base · style base-lyraOpen under the product themeInstallation
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
calloutinstead. - The message must interrupt because it has only just become true.Use
alert-bannerinstead. - You only need to label how much attention a result needs.Use
status-pillinstead. - The instruction is the standing not-medical-advice note that appears on every result.Use
disclaimer-noteinstead. - The reader must respond before they can do anything else.Use
dialoginstead.
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
CareCardA section named by its heading. No colour from either axis.Controlled byheadingCareCard.HeadingThe instruction, and the accessible name.Controlled byheadingLevelCareCard.TimingOne of three fixed phrases inside the heading. Unknown urgency renders none.Controlled byurgencyCareCard.AttributionWho is asking. With no author, the slot says so.Controlled byattributionStatusPillThe only status-coloured element, carrying word, glyph and data-status.Controlled bystatusCareCard.ReasonWhat prompted the instruction.Controlled byreasonCareCard.DeadlineThe due date in a time element, plus words for a passed date.Controlled bydueByCareCard.ActionsAt most two controls: the recommended bordered, the alternative underlined. Extras drop.Controlled byactions
- 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
- CareCard.Heading 0..1data-slot="care-card-heading"; h2 to h6
- Card 1data-slot="card"
Examples
A steady card that still asks for something
status="steady" with urgency="when-convenient" is the routine follow-up.
care-card-steady-and-still-asking · base base · style base-lyraOpen under the product themeA 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.
care-card-a-deadline-that-has-passed · base base · style base-lyraOpen under the product themeThe author is missing
The same instruction with and without attribution. Without one, the card says so.
care-card-no-author-supplied · base base · style base-lyraOpen under the product themeContent 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
sectionnamed byaria-labelfrom 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.
| Key | Action | Notes |
|---|---|---|
| Tab | Moves to the first action, then to the second | The card is not focusable. |
| Shift+Tab | Moves back through the actions, then out of the card | Actions keep DOM order. |
| Enter | Activates the focused action | No action is destructive: no dismiss, snooze or delete. |
| Space | Activates a focused action that is a button | An href action is a link, so Space scrolls. |
| Arrow keys | Nothing | Not a composite widget. |
| Escape | Nothing | Not an overlay. |
| 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 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.
Related
- 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.