Ansora value change
One 'from → to' pair: what a value was and what it is now, legible without relying on colour. The caller names both sides, which is also what a screen reader announces.
primitivedata
Install
pnpm dlx shadcn@4.23.0 add @ansora/value-changeExamples
Usage
All five visible strings are required props: title, from.label, from.value, to.label, to.value. Naming both sides is not ceremony — it is the whole component. The change has to be legible without colour, and the only thing that survives greyscale, a screen reader and a stylesheet that failed to load is the words. <ValueChange title="Dávka" from={{ value: "5 mg" }} to={{ value: "10 mg" }} /> does not compile.
The caption is title, not label, and the difference is load-bearing across this registry: DataTable, SectionCard and DetailFields all use label for the INVISIBLE accessible name, the string only a screen reader gets. Here it is rendered text, so it takes SectionCard's word for a visible heading instead. from.label / to.label keep the old name — those name a SIDE within the pair, not the component. (Renamed 2026-08-04, before anything consumed this component; label no longer compiles.)
The side labels are yours to choose because the products do not name the sides the same way. In the scribe the two sides are two authors of one field — the AI draft and the doctor's correction. In reservations they are two moments of one appointment — "Původně" and "Nově". A built-in pair would be wrong in one of the two.
A screen reader gets those five strings in reading order, and they are the sentence: "Dávka. Návrh AI 5 mg. Lékař 10 mg." The → is aria-hidden; so, effectively, is the strike-through on the old value, which is CSS and never reaches the accessibility tree. Do not rely on either to say which side is old.
There is no tint, and there will not be one. Green is the brand colour and never a state (§5.1), red/green is the worst pair for the common colour blindnesses, and — more basic than either — whether a value going down is better or worse is domain knowledge this component does not have. When a change genuinely needs flagging, put a StatusPill beside it: there the colour arrives with a word.
value is a ReactNode and is never formatted here. Numbers, doses, durations and dates come pre-formatted from @ansora/core (formatNumber, formatUnit, formatDose, formatDuration, formatDate) — formatUnit for a quantity with a unit that is not a medication dose, formatDose only for a dose. It does not assume a number either: a deleted field's new value is a word you supply ("odstraněno"), which is exactly the case a red/green diff would have signalled with hue alone.
note is an optional clarifier under the pair — why it changed, who changed it. className is merged onto the root, the element data-slot="ansora-value-change" names: a comparison view almost always has to constrain the width, and a wrapper <div> around the component would be invisible to that slot.
A stack of these inside a SectionCard is the whole comparison view; there is no list component for it, and none is needed.
import { formatDose } from "@ansora/core"
import { ValueChange } from "@/components/ansora/value-change"
<ValueChange
title="Dávka"
from={{ label: "Návrh AI", value: formatDose(5, "mg") }}
to={{ label: "Lékař", value: formatDose(10, "mg") }}
note="Upraveno podle poslední kontroly."
/>
<ValueChange
title="Alergie"
from={{ label: "Návrh AI", value: "Penicilin" }}
to={{ label: "Lékař", value: "odstraněno" }}
/>
Files
registry/ansora/components/value-change.tsx→@components/ansora/value-change.tsx
Dependencies
@ansora/base
Transcript
An ordered list of what was said, with speaker, offset and an optional confidence band — auto-following the tail by position rather than by guessing who scrolled.
Ansora app shell
Application skeleton on top of the sidebar — collapsible navigation with a brand and a user block, a top bar with the page title, breadcrumbs and an actions slot, a skip link, and a slot for the content. A server component; the only client parts are the sidebar itself, deriving the active nav item from the path, and the user menu.