Internal & investor material. Sign in with your @qiri.ai Google account to continue.
One token set across every surface, the foundations documented, and the real components published as live libraries. The pharmacist console, patient app, demo, outreach tool and marketing site all draw from a single source of truth for colour, type, spacing, radius, elevation and motion — so every surface is unmistakably Qiri.
Everything below serves a handful of convictions. Qiri is vertical software in a regulated clinical setting, read by a burnt-out pharmacist at 500 scripts a day and by a patient collecting a sensitive medication. The design has a job to do for both, and these are the rules it holds to.
| Principle | What it means |
|---|---|
| Clinical safety is a visual language | Anything touching a safety decision reads as calm and deliberate, never as alarm. Danger is a quiet chip, not a filled red block; going against a flag is a considered, traced act. We never traffic-light a screen into the 90% override reflex the console exists to end. |
| Warm for humans, cool for the clinic | A deliberate split: warm sand tones for patient-facing, human moments; cool charcoal for clinical chrome. Semantic colour always carries meaning, never decoration. |
| Say what happened | A control names its action ("Sign off"), the confirmation reports it in past tense ("Signed off"), and an error explains what went wrong and how to fix it. No apologies, no vagueness, no raw stack. |
| Real components, one source of truth | The libraries are the app's own compiled components on one shared token set, thin clients per surface. A design and the code it maps to never drift, because they are the same thing. |
| Kiosk-ready by default | Every choice still has to make sense on a single touchscreen with intermittent connectivity: legible at standing distance, no keyboard assumptions, motion that degrades gracefully to a static state. If it only works on a 27-inch monitor with stable wifi, it is wrong. |
| Present, not loud | Motion is unhurried and warm. The brand breathes rather than bounces; the signature moment is delight, not confetti. Nothing is "app-store loud". |
The console exists to end the 90% override reflex; every visual decision on a safety screen serves that. This is the severity grammar, rendered live from the same tokens the console ships: each verdict is a locked triplet of colour, glyph and copy, with its accessibility decision and clinical sign-off attached.
One verdict banner per screen. The verdict and its one-line because-clause are one unit: the headline states the decision, the line under it states why. The longer clinical explanation can follow below, but a verdict never sits alone with its reason floating elsewhere in muted text. And the brand owns the call: it is BLOCKED BY QIRI, never QIRI BLOCKS; keeping Qiri's name on the decision is what earns trust in the block instead of a reflex override.
| Tone | Label | Colour | Glyph | Accessibility decision |
|---|---|---|---|---|
| refuse | BLOCKED BY QIRI | --color-danger #9A2A2A: deep, not bright; 7.67:1 on white (AAA). Text on the danger wash uses --color-danger-text #A23D30 (5.28:1). | Circled X | Status text is held at AA-normal via per-status derived text tokens (Jon White, COO, 2026-07-14), superseding for text the earlier AA-large/UI position (David Hanin, CCO & Head of Pharmacy, 2026-07-12); fills, borders and large chrome keep the base. Status is never colour alone: the glyph carries the meaning; colour reinforces it. Pharmacy staff skew older and colour-vision-deficient. |
| hold | HELD BY QIRI | --color-warning #C9861F: amber caution, deliberately quieter than the block. | Alert triangle | |
| clear | CLEARED BY QIRI | --color-success #3A8B6A: clinical green; a clear queue is a good state and should feel like one. | Circled check |
"Interaction" is a reserved word: it means a drug interaction and nothing else (any other event is an Activity). Interaction rows rank four tiers; severity colour follows the clinical-reference convention pharmacists already know from Micromedex and Lexicomp, and every tier is a quiet chip, never a filled red block. Danger is deliberately calm: alarm is what trained the 90% override reflex.
Tier ranks live in code (InteractionCard's SEV_RANK): contraindicated/critical 4 · severe/major 3 · moderate 2 · minor 1. Chip colour is per row: tiers 3 and 4 read danger text, matching what Micromedex and Lexicomp trained pharmacists to expect of MAJOR (Jon White, COO, 2026-07-15); a moderate row keeps its amber even beside a critical neighbour and reads as "counsel and monitor", not as an alarm. Only tier 4 escalates the whole card to the danger wash, so the top tier still outranks a red-chip MAJOR. A check that ran and found nothing renders the green result card rather than silence: "checked, clean" and "not checked" are different facts, and the pharmacist deserves to know which one they are looking at.
| Beat | What the screen does | The rule behind it |
|---|---|---|
| 1 · Read the drug | The medicine, strength and directions open the screen; machine identifiers (AMT code, script ID) sit in mono beside them. | The pharmacist verifies the thing itself before any judgement about it. |
| 2 · See the verdict | One verdict banner: tone colour, glyph, BLOCKED/HELD/CLEARED BY QIRI, and the one-line reason, together. | Verdict + reason are one unit; brand owns the decision. |
| 3 · Understand why | The full reasoning rows follow: each pass/warn/fail row pairs a glyph with its colour. Whatever resolves the block (prior FBC results for a haematological hold) surfaces here, one click closer, never buried in an edge panel. | Status never by colour alone; put the unblocking context one click closer. |
| 4 · Act | The recommended action is the single filled primary. Overriding against a flag is a danger outline, never a filled red block, and it is a considered, traced act. | Emphasis carries hierarchy, not traffic-light colour; every override is audited. |
On a waiting or blocked script, the "N min waiting" and on-counter location chips carry real stakes: a patient is physically present while the clock runs. They get presence on blocked states rather than being styled as quiet metadata. The design treats the queue clock as clinical information.

InteractionCard: a MODERATE interaction reads as a calm chip plus management guidance ("counsel and monitor"), not an alarm. This is the severity grammar in production code.The canonical tokens live in design-system/qiri-tokens.css. Each product stylesheet carries a synced @qiri-tokens block; changing a token there and running node design-system/sync-tokens.mjs propagates it to every surface. The block also carries the designed dark counterpart (:root[data-theme="dark"]), inert until a surface opts in. Product-specific tokens (clinical-domain colours, marketing layout) live outside the shared block and are free to differ. One named exception: the light-mode accent-wash knobs (--wash-cover / --wash-band) are hand-declared per app, not synced.
| Surface | Where the tokens land |
|---|---|
| Pharmacist console | console-v8/app/globals.css — Next.js app |
| Patient app | patient-app/src/theme/qiri-tokens.ts — generated for React Native (no CSS vars) |
| HTML demo | demo-site/public/index.html — inlined @qiri-tokens block |
| Outreach tool | outreach-next/app/globals.css — admin app |
| Marketing site | website/public/css/styles.css — brand / landing |
Three families, each with a clear job. Playfair Display carries the warm, human voice in headlines; Plus Jakarta Sans does the reading and every control; JetBrains Mono is reserved for machine data. The mono diet is a rule, not a preference — mono means "a machine produced this exact value" (an ID, a code, a quantity), never prose.
| Role | Family | Used for |
|---|---|---|
| Display | Playfair Display | Headlines, hero moments, and hero figures: when a number is the moment (the Counter statement total, the payment-modal amount, the Pulse donut centre), it takes the display face. Everyday numerals stay in Jakarta or, when machine-exact, mono. |
| Body & UI | Plus Jakarta Sans | Everything the user reads and every control |
| Machine data | JetBrains Mono | IDs, codes, tokens, quantities — never prose |
| Token | Size | Use |
|---|---|---|
--fs-hero | clamp(2.5–3.5rem) | Page heroes |
--fs-display | clamp(2–2.75rem) | Section display headings |
--fs-h1 … --fs-h4 | 2.25 · 1.5 · 1.375 · 0.9375rem | Heading ladder |
--fs-body-lg / --fs-body | 1rem / 0.8125rem | Lead paragraph / default body (13px) |
--fs-cta / --fs-eyebrow | 0.875 / 0.72rem | Buttons / small-caps labels |
--fs-small / --fs-xs | 0.75 / 0.6875rem | Secondary / fine print |
--fs-mono-id / --fs-mono-tag | 0.625 / 0.5625rem | Machine IDs / tag chrome |
Line-heights: --lh-display 1.05 · --lh-heading 1.15 · --lh-body 1.55 · --lh-tight 1.1.
The same ladder rendered at size, in its real family and weight, so a step is chosen by eye rather than by arithmetic.
Qiri runs a deliberate warm / clinical split: warm sand tones for patient-facing, human moments; cool charcoal for clinical chrome. Semantic colour (success / warning / danger / info) is separate from the brand accent and always carries meaning, never decoration.
The brand colours are anchored to specific Pantone TCX chips. These sRGB values are the exact source of truth — do not nudge them; where a hue must shift for legibility, add a derived text token rather than moving the base.
| Token | Swatch | Pantone | Hex |
|---|---|---|---|
--color-bg | Snow White · 11-0602 TCX | #F2F0EB | |
--color-stone | Pumice Stone · 14-0002 TCX | #CAC2BA | |
--color-taupe | Doeskin · 15-1308 TCX | #BDAB9B | |
--color-castle | Castle Wall · 14-0108 TCX | #C8C1AB | |
--color-ink / --color-cta | Carbon · 19-4012 TCX | #272F38 | |
--color-accent | Regatta · 18-4039 TCX | #497AB7 | |
--color-accent-soft | Alaskan Blue · 15-4225 TCX | #6DA9D2 |
Every status hue whose base is only large/UI-safe now carries a derived text token at the same hue, AA for normal-size text; fills, borders and large text keep the base. On light: --color-accent-text #3A68A0 (info and accent; 5.73:1 on white), --color-danger-text #A23D30 (5.28:1 on the danger wash), --color-success-text #32775B (4.64:1), --color-warning-text #965A0C (4.85:1; a deep amber), plus the console severity pair --color-aero-text #B94B3C (4.60:1) and --color-amber-text #965A0C (5.06:1 on its wash; it converged with the warning text token when both moved to true amber, 2026-07-15). Each has a dark counterpart that lifts or converges as the dark grounds require. This extends the derived-text pattern to every status: status text is AA-normal (Jon, 2026-07-14), superseding for text the earlier chips-are-UI AA-large position (recorded in the verdict table, David 2026-07-12); fills and large chrome keep the base. Pumice Stone (--color-stone) and Castle Wall (--color-castle) are reserved warm neutrals: tokenised but intentionally unused, earmarked for Counter terminal surfaces and dividers; decorative and UI use only, never small text on light backgrounds.
These chips render from this page's own synced @qiri-tokens block, so they cannot drift from what ships. Click any chip to copy its value.
Five hues (console-only, outside the shared block) tag clinical categories on the review surface, so a pharmacist reads the domain at a glance. Their per-domain mapping is owned by the console.
| Token | Swatch | Hex |
|---|---|---|
--color-aero | #C4584A | |
--color-rad | #3A8B6A | |
--color-fin | #5B8EC9 | |
--color-law | #7B6AA0 | |
--color-hum | #C4884A |
The console has exactly one blue-filled control: the Ask Qiri entry point (a Regatta gradient pill with the sparkle glyph). Every ordinary primary action is Carbon-filled; override-against-a-flag is a danger outline. So a Regatta fill is a semantic, not an emphasis: it marks the places where Qiri's intelligence is invoked. Never use a blue fill to make a button feel more important, and never give a Qiri-intelligence entry point any other treatment.
Qiri is read at kiosk standing distance, so contrast is a requirement. Key pairs, measured:
| Pair | Ratio | Rating |
|---|---|---|
Body ink #272F38 on page #F2F0EB | 11.9 : 1 | AAA |
| Body ink on white surface | 13.55 : 1 | AAA |
Muted ink (65% ink over page = #6E7377) | 4.21 : 1 | AA (large) — under 4.5, so muted ink is for secondary/large text, never for small text that must be read |
| CTA text on CTA ink | 13.55 : 1 | AAA |
Danger #9A2A2A on surface | 7.67 : 1 | AAA |
Danger text --color-danger-text #A23D30 on danger wash (over white card) | 5.28 : 1 | AA |
Regatta --color-accent #497AB7 on surface | 4.42 : 1 | AA (large / UI only) |
Accent text --color-accent-text #3A68A0 on white / cream | 5.73 / 5.03 : 1 | AA |
Success #3A8B6A on surface | 4.13 : 1 | AA (large) |
Success text --color-success-text #32775B on success wash | 4.64 : 1 | AA |
Warning text --color-warning-text #965A0C on warning wash | 4.85 : 1 | AA |
Severity text (critical) --color-aero-text #B94B3C on wash | 4.60 : 1 | AA |
Severity text (high) --color-amber-text #965A0C on amber wash; the base --color-amber #C9861F holds 3.04 : 1 on white for fills and large chrome | 5.06 : 1 | AA |
#9A2A2A, not a brighter redThe plain danger red failed AA as small button text, so the override-against-a-safety-flag label is darkened to #A23D30 (5.46:1) and rendered as a calm chip, not a filled red block — going against a flag reads as deliberate, never as alarm-fatigue traffic-lighting.
Light-mode page grounds across the console, outreach, and the marketing site carry a soft Alaskan Blue cast: a low-alpha radial of --color-accent-soft over the existing ground, so the three surfaces read as one product. The wash lives on the page ground, never on title blocks or cards. Its strength sits in two knobs, --wash-cover and --wash-band, and dark mode zeroes both in one declaration: Carbon already carries its own accent treatment, and a blue wash over it reads as a smudge. One deliberate exception to the one-token-set rule: the two wash variables are declared by hand in each app's stylesheet, not synced from qiri-tokens.css, so check each app when tuning them.
The dark theme is designed, not inverted: the same Pantone anchors, recast. Carbon (19-4012 TCX) becomes the card ground, Snow White (11-0602 TCX) becomes the ink, and Alaskan Blue (15-4225 TCX) takes the accent seat, because Regatta reads only 3.07:1 on Carbon (UI and large text only). The status hues are lifted for the dark grounds, where the deep light bases sink (the light danger reads 1.77:1 on Carbon, illegible); their washes keep the deep light hues at raised alpha, so chips stay tinted rather than chalky. Where a lifted base already clears AA as normal text, the derived text token converges with it: --color-accent-text and --color-danger-text equal their bases on dark, and stay defined so product CSS resolves unchanged.
The dark block ships inside every surface's synced @qiri-tokens block but is strictly opt-in: it activates only when a surface sets data-theme="dark" on the root element. This docs site adopts it (following prefers-color-scheme, with the toggle in the nav as the override; use it to read this table on the grounds it describes). The patient app ships its warm dark counterpart through the same generated token module, and the pharmacist console now ships light and dark too, driven by the same tokens through a System / Light / Dark control in Settings › Appearance. On dark, elevation inverts: a shadow barely reads on near-black, so height is carried by the lightening surface ladder plus subtle cream-based borders, and the verdict grammar (blocked, held, cleared) plus status chips stay calm and AA-legible, recomputed on the dark grounds below. The Counter is deliberately light-locked, keeping its warm rail even when the console is dark.
| Pair (dark grounds) | Ratio | Rating |
|---|---|---|
Body ink #F2F0EB on dark surface #272F38 | 11.9 : 1 | AAA, the exact mirror of light's ink-on-page |
Body ink on dark page #171C23 | 15.03 : 1 | AAA |
| Muted ink (65% ink over dark surface) | 5.95 : 1 | AA: the same 65% alpha that is only AA-large on light clears AA outright on dark |
| Dark CTA text (Carbon on Snow White fill) | 11.9 : 1 | AAA |
Accent and accent text #6DA9D2 on dark surface / page | 5.33 / 6.73 : 1 | AA: the base itself is text-safe on dark |
Danger #F28B82 on dark surface | 5.67 : 1 | AA |
| Danger text on danger wash (over Carbon card) | 5.19 : 1 | AA |
Success #4EA37C on dark surface | 4.42 : 1 | AA (large): the clinical-status stance, now with headroom over the light base's 3.28 there |
Warning #DA9A36 on dark surface | 5.58 : 1 | AA |
Amber severity text #DDA63F on the dark amber wash (over Carbon); the base #DA9A36 reads 4.25 : 1 there | 4.71 : 1 | AA |
| Warm ink (Doeskin) on dark warm surface | 6.78 : 1 | AA |
Keyboard focus draws a 2px solid accent ring with a 2px offset (the console's global :focus-visible rule; mouse clicks stay quiet, and an alpha wash is banned as a focus indicator because it is invisible). This page ships the same rule site-wide: Tab through these to see it.
A three-step ladder of soft shadows — floating surfaces on cream, not hard outlines. Pick by how far the surface sits above the page; don't stack levels.
| Token | Role | Recipe |
|---|---|---|
--elevation-1 | Resting cards, panels | 0 1px 2px + 0 4px 12px rgba(0,0,0,.04) |
--elevation-2 | Popovers, menus, sheets | 0 12px 32px -8px rgba(0,0,0,.12) |
--elevation-3 | Modals | 0 25px 50px -12px rgba(0,0,0,.25) |
One 4px rhythm everywhere: --space-N is N × 4px. Compose gaps and padding from the scale; never invent an off-scale pixel value. (Outreach still re-declares --space-5..8 locally — a tracked follow-up, not a second scale.)
| Token | Value | Typical use |
|---|---|---|
--space-1 / --space-2 | 4 / 8px | Icon-to-label gaps, chip padding, tight in-row spacing |
--space-3 / --space-4 | 12 / 16px | Control padding, gaps between fields, row padding |
--space-5 / --space-6 | 20 / 24px | Card internal padding, space between grouped cards |
--space-7 / --space-8 | 28 / 32px | Section padding inside big surfaces, modal gutters |
--space-10 / --space-12 | 40 / 48px | Space between page sections |
--space-16 / --space-20 | 64 / 80px | Hero breathing room, page-level top/bottom padding |
Round increases with prominence — and fully interactive pills are the house shape for buttons: every shipped button (console and patient) wears --radius-pill, which is why a squared-off button reads instantly as off-brand.
| Token | Value | Role |
|---|---|---|
--radius-sm | 8px | Inputs, small controls, keycaps, inline code chips |
--radius-md | 12px | Cards, chips, table cards, notices |
--radius-lg | 16px | Modals, sheets, hero surfaces |
--radius-pill | 999px | Buttons (all of them), status pills, tags, avatars |
Motion is calm and purposeful. Entrances decelerate; state changes are quick and even. Everything honours prefers-reduced-motion — the signature aura falls back to a static state, never a jarring stop.
| Token | Value | Use |
|---|---|---|
--dur-fast / base / slow | 120 / 200 / 320 ms | Hover · default transition · entrance |
--ease-out | cubic-bezier(.16,1,.3,1) | Entrances (decelerate) |
--ease-standard | cubic-bezier(.4,0,.2,1) | State changes |
| Signature tier (patient) | bloom 780 · halo 1100 · breath 4200 ms | The living-aura moment (motion.ts) |
The one place console motion carries clinical weight is when a verdict lands and when a script leaves the queue. Both halves shipped 2026-07-13: the landing entrances in fc81d54, the queue-exit choreography in 3c6cc1a (the signed row leaves, the counts move only after it has gone, the toast enters last; the sequencing is kept under Reduce Motion because order is information). It uses only the shared tier above, and its first rule is a prohibition.
| Moment | Motion | Why |
|---|---|---|
| Never animate alarm | A BLOCKED banner must never shake, pulse, flash or bounce. It lands with exactly the same calm as CLEARED: same duration, same easing. | Urgent motion is the animation-layer version of the filled red block; it retrains the override reflex the colour system was built to end. |
| The verdict lands | One-time entrance: fade + 6px rise, --dur-slow 320 ms, --ease-out. The glyph and label arrive together as one unit; nothing loops. | Slow and deliberate reads as considered judgement; a snap reads as an alert. |
| The reasoning follows | The pass/warn/fail rows appear as one group after the banner settles: fade + 4px rise, --dur-base 200 ms. No per-row stagger. | Row-by-row theatre makes the pharmacist wait for information they came to read; one settle beat is enough hierarchy. |
| A script clears the queue | On sign-off the row exits by height collapse + fade (--dur-base 200 ms); the queue count decrements only after the row has gone; the past-tense toast enters last (--dur-fast 120 ms). | The count must never contradict the list mid-animation, and the order (row, count, confirmation) narrates what just happened. |
| Reduce Motion | Every entrance and exit becomes an instant swap. The row-then-count sequencing is kept, because order is information, not decoration. | The same contract as the patient aura: progress and sequence stay, movement goes. |
The patient app's rarest asset: a brand mark that is quietly alive. Where the console is calm chrome, the patient surface breathes. It is tuned for someone collecting sensitive medication, so every motion is slow, low-contrast and warm: presence, not a spinner; delight, not confetti. All of it is driven by one motion source of truth (lib/motion.ts) and honours the OS "Reduce Motion" setting everywhere.
A soft radial wash carries the screen's emotional state. Content sits centred and unboxed inside it, so the colour dissolves into the cream canvas rather than living in a card. This is the app's ambient background (StateBloom / AmbientBloom).
| State | Wash | Reads as |
|---|---|---|
| Ready | Success green | Your medication is ready. The good, resolved moment. |
| With your pharmacist | Warm taupe | A human has it in hand. Reassuring, in-progress. |
| Action for you | Qiri blue | Something needs you: pay, confirm, choose. |
The aura mark (LivingAuraOrb) always breathes gently in place. On a fresh "medication ready" event it performs a one-time arrival: the glow blooms up on a soft breath of light, three concentric rings ripple outward in sequence, and a single success haptic lands. It plays once per event, then settles back into the calm breathing state on later visits. The timings below are the whole signature tier.
| Token | Value | Role |
|---|---|---|
breath | 4200 ms half-cycle | The ambient in-out swell (a full breath is ~8.4s, ~7/min): perceptibly alive, never busy |
bloom | 780 ms | The glow swelling to life on arrival |
halo | 1100 ms | Each ripple ring expanding out, staggered 60 / 300 / 540 ms so they read one after another, not as one thick ring |
spring.gentle | damping 22 · stiffness 120 | The mark settling in: a whisper of spring, no bounce |
easing.standard | cubic-bezier(.22,1,.36,1) | Entrances (quick to settle, no overshoot) |
easing.inOut | Easing.inOut(quad) | The symmetric loop for the breath |
This is not a video: the orb below is rendered live by this page from the same numbers as the table above (breath 4200 ms half-cycle at a ~7%/3.5% swell, bloom 780 ms, halo 1100 ms staggered 60/300/540, ring growth 1.7×/2.0×/2.3×). The arrival plays once when it first scrolls into view; replay it as often as you like. With Reduce Motion on, it renders fully static, exactly as the app does.
The glow (RadialGlow) is not react-native-svg. Its RadialGradient fill renders unreliably on Android, where the aura once silently vanished, so the wash is drawn from 36 stacked concentric Views: background colour, border radius and opacity, the one primitive that renders identically on every platform. The stack is then flattened to a single GPU texture (renderToHardwareTextureAndroid + needsOffscreenAlphaCompositing) so the breath doesn't flicker on Android. What renders on iOS renders the same on Android.
The signature moment is the first thing to yield when the patient has asked for less motion. Never a jarring stop: a graceful, static fallback.
| Rule | Detail |
|---|---|
| The aura falls fully static | Under Reduce Motion the orb renders with no loop, no bloom and no haptic: the mark, glow and rings simply rest. |
| Ambient washes stop easing in | AmbientBloom skips its fade-in and paints in place, so nothing swells or snaps. |
| Progress stays, decoration goes | The one thing that keeps moving is HoldToConfirm's fill, because it is progress the user needs to see, not decoration. |
The exception, live: HoldToConfirm asks for a deliberate 1200 ms press before an irreversible act fires; a danger tint fills behind the label as visual feedback (a JS timer decides completion, the fill is purely feedback). Releasing early rewinds in 160 ms. Try it, and note the fill still animates under Reduce Motion, because it is information.
| Component | What it is |
|---|---|
RadialGlow | The platform-safe soft glow primitive (stacked Views, no SVG). The base of every aura and wash. |
LivingAuraOrb | The brand mark, alive: ambient breath, plus the one-time arrival for the signature moment. |
StateBloom / AmbientBloom | The state-colour wash, and its full-screen ambient-background variant that sits behind the whole surface. |
QiriLoading | The living aura breathing in place of a bare spinner, wherever a wait is long enough to show anything. |
HoldToConfirm | A press-and-hold destructive action (1200 ms): nothing happens on a tap, so cancelling a consult or discarding a script can't happen by accident. A danger outline, never a filled red block. |
One line-icon language: Lucide-style, drawn on a 24×24 frame with stroke-width: 2, round caps, and colour inherited from the text (currentColor). No icon fonts, no pasted raw SVG.
| Rule | Detail |
|---|---|
| Source of truth | The named constants in console-v8/components/icons.ts |
| Use in code | <Icon name="alert" size={16} /> — the typed component; never paste a raw <svg> |
| Adding one | Add it to icons.ts with a name, so it's reusable and lint-clean |
| Web ↔ mobile | Console uses Lucide; the patient app uses Material Community icons of the same concept |
The Qiri mark is a single glyph: a blue aura orb with a white "reasoning drop" inside it, the shape that carries the idea of a system that thinks. It has three sanctioned forms and a short list of rules, all of them living in real code (patient-app/src/components/brand.tsx) so the brand is applied identically everywhere.
| Form | Component | Use |
|---|---|---|
| The mark | QiriMark | The orb-and-glyph on its own. App icons, tight spaces, anywhere the wordmark won't fit. |
| The wordmark | QiriWordmark | The mark plus the "qiri" lockup. The default header signature. Pass mark={false} for the text-only wordmark where the living aura already carries the mark, so the brand isn't stamped twice on one screen. |
| The aura logo | QiriAuraLogo / LivingAuraOrb | The mark inside a soft radial glow: the hero moment. LivingAuraOrb is the same mark alive, breathing, for the signature arrival (see Signature aura & motion). |
| Rule | Detail |
|---|---|
| Use the component, never raw SVG | Reach for the typed QiriMark / QiriWordmark, exactly as with icons. Never paste the glyph path into a screen. |
| Two tones, theme-aware | tone="ink" follows the theme (charcoal on light, off-white on dark) so the wordmark stays legible in dark mode; tone="white" is the fixed override for dark heroes. The orb keeps its brand blue in both. |
| Don't stamp the brand twice | One brand mark per screen. If the living aura is present, use the text-only wordmark beside it, not a second orb. |
| Don't recolour or distort the glyph | The white reasoning-drop stays white and the orb stays brand blue. No stretching, no re-tinting, no drop shadows. |
Qiri's rarest design asset: opinionated, clinically-motivated content rules. They're safety-driven and specific — the same kind of guideline Stripe and Shopify are known for, in a regulated clinical setting.
| Rule | What it means |
|---|---|
| "Interaction" = drugs | In the UI, interaction refers to a drug interaction only. Everything else a user does is an "Activity". A terminology rule with real clinical weight. |
| The mono diet | JetBrains Mono is for machine data only — IDs, codes, tokens, quantities, Medicare/eRx numbers. Never prose or labels. |
| Patient-level counselling | Warm, no jargon; extra care and dignity for sensitive items (mental & sexual health, addiction). The voice the whole patient app is written in. |
| Say what happened | A control names its action ("Sign off"); the confirmation is past tense ("Signed off"). Errors explain what went wrong and how to fix it — no apologies, no vagueness. |
| House mechanics | No em-dashes in NEW prose (use a colon, semicolon or comma); existing copy is grandfathered behind per-file ratchet ceilings that only go down. No "Kova" (retired brand). Cite v8, not the old v7 demo. The drift gate lints all of this on every push. |
The words the product actually uses, and the near-misses that creep in. Australian pharmacy first: these match PBS, AHPRA and patient expectations, not US software habits.
| Say | Avoid | Why |
|---|---|---|
| Script | Prescription, Rx | The word Australian pharmacists and patients use; "Rx" is US shorthand. |
| Repeat 3 of 5 | Refills | PBS language; "refills" is American and ambiguous about which repeat this is. |
| Collect / ready to collect | Pick up, pickup | The patient app's register: "Ready to collect" is warmer and unambiguous. |
| Medicine, medication | Drug (patient-facing) | "Drug" is clinical vocabulary; it survives only in clinical compounds ("drug interaction") on the console. |
| Blocked · Held · Cleared | Rejected, denied, failed, error | The only three verdict words, and they carry Qiri's name. "Rejected" reads as judgement of the patient; "failed" reads as a system fault. |
| Sign off / Approve & dispense | Submit, Confirm, OK | The control names the clinical act. "Submit" describes a form, not a decision. |
| Your pharmacist | The system, Qiri (in patient reassurance) | When a patient needs trust ("your pharmacist is checking"), the human carries it; Qiri owns verdicts on the console, the pharmacist owns care in the app. |
| Medicine information | CMI (patient-facing) | The leaflet is CMI to the pharmacist and "your medicine information" to the patient; acronyms are clinic-side only. |
The most differentiated voice rule, shown rather than stated: the same three moments written in clinic-speak and in Qiri's patient register. Sensitive items get an extra layer: notifications and lock screens never name the medicine; the name appears only inside the app.
| Moment | Not this | This |
|---|---|---|
| Routine collection antibiotic, in-app | "Administer 1 capsule TDS PO for 7 days. Complete the full course." | "Take one capsule three times a day, spaced through the day. Keep going until they're all finished, even if you feel better sooner. If they upset your stomach, take them with food." |
| Sensitive item mental health, notification + in-app | "Your SERTRALINE 50MG is ready for pickup at the counter." | Notification: "Your order is ready to collect whenever suits you." In-app: "Your medication is packed and labelled, ready when you are. Collect it from the counter or the private collection point; no need to explain anything at the desk." |
| A held script patient-facing, while the console shows HELD BY QIRI | "Script REJECTED: prescriber verification required (DDI-404)." | "Your pharmacist is double-checking one detail with your doctor before this is ready. There's nothing you need to do; we'll message you the moment it's sorted." |
Every rewrite does the same four things: plain words for clinical shorthand; the action first, the reason second; the medicine name kept off surfaces other people can see; and the next step made explicit so the patient is never left holding an error they can't act on. That is the whole register; apply it to any new patient copy.
Foundations and atoms answer "what does a control look like"; patterns answer "how do we build the screen". These are the house recipes for the three things every console surface does: take input, list records, and handle the moments when there's nothing, something's loading, or something went wrong. Each recipe is realised in real components, so the rule and the code never drift.
Every form is built from TextField and its siblings. A field is always labelled, help sits on one line under it, and validation shows in place rather than in a popup. Confirmation is reserved for the actions that deserve it.
| Rule | How |
|---|---|
| Always label the field | TextField requires a label. A placeholder is a hint, never the label: it disappears the moment the user types, and screen readers skip it. |
| One helper line, hint or error | A field carries a single line beneath it: a muted hint by default, replaced by the error message (in danger colour, with aria-invalid) when the value is wrong. The line never stacks both. |
| Validate in place, not in a popup | Errors render under the field they belong to, so the fix is next to the problem. No modal, no toast, for a field-level mistake. |
| Inline edit vs form field | A value the user types once in a form is a TextField. A value that autosaves inside a record panel is an InlineEditField. Don't wrap a single autosaving value in a full form. |
| Confirm only what's consequential | A routine save just happens, then a toast says so. A destructive or irreversible action (delete, discard, an irreversible send) is gated behind a ConfirmDialog, with danger set for a destructive confirm. |
| Name the action, past-tense the result | The submit button names its action ("Sign off"); the confirmation toast reports it in past tense ("Signed off"). Never "OK" / "Success". |
A field error explains what went wrong and how to fix it, never just "invalid". Danger is the calm #9A2A2A rendered as a quiet line, not a filled red block: a correctable mistake should read as guidance, not an alarm.
The scripts queue, records, patients and transactions are all the same surface: a DataTable, optionally paired with a Pager, a row Menu, and an EmptyState for when the list is empty. Build a list from these, not a bespoke table.
| Rule | How |
|---|---|
| One table everywhere | DataTable takes columns + rows and owns the header, alignment and horizontal scroll. Right-align numeric columns; put actions only in the last column. |
| A single value isn't a table | For one record's key/value read-out use a definition list, not a one-row table. Tables are for lists of records. |
| Paginate long lists | Past ~2 pages, pair the table with a Pager (previous / next + a "Page x of y" read-out). Disable the ends. Don't paginate a list that fits one comfortable page; an infinite feed uses lazy loading instead. |
| Row actions live in a Menu | Per-row actions (edit, resend, delete) go in a Menu off the last column. Put the destructive item last, visually separated, in danger. Keep to ~6 items; beyond that, open a panel. Primary page actions are Buttons, not menu items. |
| Empty is a designed state | An emptied search or a cleared queue renders an EmptyState, never a bare table with a blank body. |
Three distinct moments, three distinct tones. Each is deliberately designed: a blank region or a lone spinner is never the answer.
| State | Component | The rule |
|---|---|---|
| Empty | EmptyState | A centred icon, a title, a line of guidance and an optional next action. Say what goes here and how to start it. Use it for an empty queue, a cleared search, a not-yet-configured surface; never for an error. |
| Loading | Skeleton | Mirror the shape of what's coming so the layout doesn't jump when data lands. Prefer it to a spinner for structured content. A whole-page or brand moment uses QiriLoading. If a skeleton is up for more than a couple of seconds, that's really an error. |
| Error, recoverable | Toast | Flag it without interrupting the screen: a card with a tonal left rail, top-right. Say what happened. Use a toast only for something the user doesn't have to act on. |
| Error, blocking | designed error state | When the surface can't proceed, explain what went wrong and how to recover. No apology, no vagueness, no raw stack. |
An empty queue is a good state: it means the pharmacist is caught up, and it should feel that way. An error is a different, tonal pattern. Don't reuse the reassuring empty icon for a failure, and don't dress a failure up as "nothing here".
The console's spine is a queue the pharmacist works down. Filtering narrows what is already on screen; it never becomes the only path to an item.
| Rule | How |
|---|---|
| Search narrows, never gates | SearchField filters the visible list as the pharmacist types (leading glyph, clear affordance the moment there is text; debounce expensive filtering in the caller). The list stays browsable without it. |
| Status is a facet, not a folder | Verdict pills (Badge tones) mark state in place; the list/board toggle re-arranges the same items rather than hiding them. A script never disappears because of a filter the pharmacist forgot. |
| Counts tell the truth | The nav badge and the list agree at all times; the count changes only when the list does (see the verdict-motion spec: the row leaves, then the count follows). |
| Empty is the goal state | A cleared queue renders EmptyState as a good outcome ("Queue's clear"), never as an error or a blank void. |
The whole clinical loop in one recipe: what the pharmacist sees, presses and hears back, from opening a script to the row leaving the queue.
| Step | What renders |
|---|---|
| 1 · Open | The four-beat verdict arc (see Clinical safety above): drug, verdict banner, reasoning rows, actions. One primary action per view. |
| 2 · Decide | Agreeing with Qiri: the filled primary ("Approve & dispense"). Going against a flag: a danger outline that opens the override flow, where a reason is required and the act is traced. Never a filled red block. |
| 3 · Confirm | Destructive or irreversible branches get a ConfirmDialog; the button names the act, the dialog reports consequences plainly. |
| 4 · Hear back | A past-tense Toast ("Signed off: script approved and dispensed"). No apologies, no vagueness, no raw stack. |
| 5 · Move on | The row exits, the queue count decrements after it, and the audit record exists before the toast fades: every decision needs its trace (inputs, rules fired, pharmacist action). |
Steps 4 and 5 are the trust loop: the pharmacist must hear what happened in the past tense and see the queue agree, every time, or the console becomes another system whose confirmations mean nothing. The choreography lives in the verdict-motion spec under Motion.
Where the console has pattern recipes, the patient app has two screen scaffolds: templates that assemble the primitives into a whole screen, so every detail and onboarding step lands identically without each screen re-solving the layout. They are the reason the header, footer and journey feel the same everywhere.
| Scaffold | Composes | What it gives you |
|---|---|---|
DetailScaffold | AppHeader · title/subtitle · scrolling content · QiriFooter | Any pushed detail screen: the standard header (back, wordmark, bell), an optional intro block, then content. Pass background to drop a full-screen AmbientBloom behind it, matching the tab screens' ambient wash. Omit the title for a centred empty-state moment so the content owns the space. |
OnboardingScaffold | StepProgress · title/subtitle · content · QiriButton (primary + optional secondary) · keyboard avoidance | Any onboarding step: the step meter, the copy, the content, and a pinned action pair, all keyboard-aware. The six-step journey (account, consent, identity, ASL, pharmacy, PIN) reads as one coherent flow because every step is the same shape. |
Because the header is placed by the scaffold, not by each screen's own padding, it lands in the exact same spot every time. And OnboardingScaffold's standalone mode hides the Back control and the step meter for a force-routed single-step screen (like a complete-your-profile backfill), so a patient can't be stranded or misled by a step count they never chose to start.
"Kiosk-ready by default" is a principle above; this is the floor that makes it auditable. Phase 2 puts Qiri on a standalone touchscreen used at standing distance, with no keyboard, no hover, gloved and older hands, and intermittent connectivity. Every kiosk-bound surface must clear this floor; the console notes say what already conforms today and what is desktop-only.
| Requirement | Floor | Status today |
|---|---|---|
| Touch targets | 44×44px minimum for any tappable control (48px preferred for primary actions); 8px minimum spacing between adjacent targets so a miss lands on nothing, not on the wrong action. | The Counter is the first audited tablet-conformant surface: 44px targets throughout, a full-height cart rail, camera scanning, and an installable chrome-free web app. The rest of the console clears the floor on the primary flows; the full per-component audit ships with the kiosk build. New components must not go below the floor. |
| Type at standing distance | Body text 16px minimum on kiosk surfaces; interactive labels 16px+; the verdict label and primary action 18px+. Nothing the patient must read below 14px, ever. | The console's 13px default body is a desktop-only density for a pharmacist at arm's length from a monitor; it does not travel to the kiosk. The 10px/9px mono machine floors are console chrome and never appear on a patient touchscreen. |
| Focus & input | Visible 2px solid accent focus ring (the global :focus-visible rule); no keyboard assumptions: numeric entry uses on-screen pads, free-text entry is a design smell on a kiosk. | Focus ring ships today, console-wide. Patient PIN entry already uses QiriPinInput. |
| No hover dependence | Touch has no hover: nothing may be reachable only via hover (tooltips, hover menus, hover-revealed actions). Whatever a tooltip says must be readable another way on a touch surface. | Console uses hover tooltips as hints only; anything load-bearing is also in visible copy. Keep it that way. |
| Motion | Every animation degrades to a static state (the Reduce Motion contract); nothing meaning-bearing lives only in motion. Progress indication is the one exception that keeps moving, because it is information. | Contract documented in Motion and Signature aura above; honoured in the patient app and this site. |
| Intermittent connectivity | Every action has a pending state and a degraded answer: no dead taps, no silent spinners past a couple of seconds (a long skeleton is really an error), and a clear "come back / seek help" path when the network is gone. | Pattern documented (Empty, loading & error states); the kiosk build adds an offline queue for dispense-critical actions. |
| Forgiveness | Destructive or irreversible actions need distance from common taps plus an undo or an explicit confirm; HoldToConfirm is the house primitive for deliberate irreversible acts. | HoldToConfirm ships in the patient library; the kiosk collect flow adopts it for identity-bound handover. |
The five-word test from the operating principles: "if a feature only works on a 27-inch monitor with stable wifi, it is wrong." A floor is checkable per component before the kiosk exists; a guideline is an opinion someone can defer. When the Phase-2 build starts, this table becomes a per-component checklist and a release gate.
The real Qiri components — the ones the apps actually ship — are published as live design-system libraries. Design work builds directly with them, so every screen produced is on-brand and maps one-to-one onto shippable code. React DOM and React Native can't share one bundle, so each runtime has its own library.
| Library | Surface & runtime | Components | Open |
|---|---|---|---|
| Qiri Design System | Pharmacist console — React (web) | 51 — the app shell & navigation, the 16 atomic primitives (Button, Icon, Badge, TextField, Select, Checkbox, RadioGroup, Switch, DateField, SearchField, Modal, Avatar, DataTable, Tabs, Toast, Tooltip), 5 pattern components, the Pulse retail dashboard broken into 8 molecules, plus clinical, patient, consult and brand | Design library |
| Qiri Patient App | Patient app — React Native (via react-native-web) | 25 — foundation, inputs, feedback, navigation, brand chrome, screen scaffolds, the payment-marks strip, the pharmacy contact sheet, and the signature aura & motion family (living aura, state blooms, radial glow, brand loading, hold-to-confirm) | Design library |
Each library is built from the app's own compiled components — every preview renders the real exported component with realistic content, and they compose into each other (a table built from avatars and badges, a modal built from an icon, a field and a button). Nothing is a reimplementation, so a design and the code it maps to never drift.
Every published component, rendered. These are static previews of the real components in the two libraries above, so you can see the whole system at a glance without leaving this page. Open a library to build with them live.
"One brand at two temperatures" proven per component: the same concept from each library, side by side. The grammar never changes (pill buttons, one primary per view, tinted status, errors that teach); the temperature does (cool charcoal density for the clinic, warm full-width calm for the patient).



















































































The libraries carry the components that render standalone today. A few surfaces sit outside them by design.
| In the libraries | Out of scope |
|---|---|
| The console app shell — sidebar, top bar and the assembled frame — plus the atomic primitives and presentational components: buttons, fields, icons, badges, tables, cards, notices, tabs, sheets, brand marks, retail dashboards. | Deeply app-coupled surfaces (live sign-off modals, API-bound flows) that only make sense inside a running app. |
| The shared token set and brand typefaces ship with every library, so the design tool renders in true Qiri colour and type. | The marketing site is static HTML/CSS on the same tokens — it shares the design language but has no React components to publish. |
What has shipped, most recent first. The design system is built in the open alongside the product, so every entry here maps to real components and live surfaces.
| Date | Shipped |
|---|---|
| 2026-07-22 | Accent wash on the light ground: the console, outreach, and the marketing site gained the soft Alaskan Blue page-ground cast from the Ask Qiri launch layout, so the three surfaces read as one product in light mode. Strength lives in two per-app knobs (--wash-cover / --wash-band); dark mode zeroes both. Documented above as the one named exception to the synced token set. |
| 2026-07-22 | Hero figures took the display face: the Counter statement total, the payment-modal amount, and the Pulse hero and donut-centre figures now set in Playfair Display, with chart marks aligned to the Pantone palette. The typography table now states the rule: a number takes the display face only when it is the moment. |
| 2026-07-22 | The Counter became the first audited tablet-conformant surface: 44px touch targets throughout, a glass top bar with a full-height cart rail, camera barcode/QR scanning, and installation as a chrome-free web app. The kiosk conformance floor's status column updated to cite it. |
| 2026-07-22 | The drift gate moved to write time: a hook now fails a ratchet violation at the moment the file is written, not at CI, and the em-dash rule was scoped to user-facing copy so code and docs prose stop tripping it. |
| 2026-07-20 | Dark refinements from live use: the dark card treatment extended past the dashboard, tile borders softened with height carried on elevation, and the light-mode side rail warmed to match the standalone surface. |
| 2026-07-15 | Warning moved to true amber (#C9861F) and the drug-interaction severity grammar was written down: per-tier chip colours matching what Micromedex and Lexicomp trained pharmacists to expect, only the top tier escalating the whole card, and "checked, clean" rendered distinctly from "not checked". Per-status derived text tokens hold status text at AA-normal on both grounds (2026-07-14 decision). |
| 2026-07-13 | Dark mode shipped in the pharmacist console, driven entirely by the canonical dark tokens: a System / Light / Dark control in Settings › Appearance, a boot script that stamps the theme on the root before paint (no flash), and a per-device choice that persists and wins over the OS in both directions. Elevation inverts on dark (surface-lift and subtle cream borders instead of shadow, which barely reads on near-black); the verdict grammar (blocked, held, cleared) and the status chips were recomputed to stay calm and AA-legible on the dark grounds, with danger a quiet deep red rather than an alarm. The Counter is light-locked, keeping its warm bench rail even when the console is dark. |
| 2026-07-13 | Dark theme, token-first: the canonical set gained its designed dark counterpart (Carbon becomes the card ground, Snow White the ink, Alaskan Blue the accent, status hues lifted for the dark grounds), every text-on-surface pair recomputed and gate-checked on those grounds, the patient app's generated token module now emits the dark set, and this docs site adopts it (OS preference plus the toggle in the nav). |
| 2026-07-13 | The verdict moment completed: the queue-exit choreography shipped in the console. On sign-off the row leaves by height collapse + fade, the queue counts (nav badge, filter tabs, board columns) move only after it has gone, and the past-tense toast enters last; Reduce Motion keeps the sequence and drops the movement. Both halves of the verdict-motion spec are now live product behaviour. |
| 2026-07-13 | The gate turned outward: the drift gate now lints the product component sources themselves (console-v8, the patient app, and the DS preview copy that feeds the published cards) for off-token colours, off-scale spacing values in inline styles, and em-dashes in user-facing copy. Legacy counts are frozen as per-file ratchet ceilings that only go down; any new drift fails the build in CI. Shipped with a first cleanup pass: 18 em-dashes removed from live console copy and their ceilings lowered. |
| 2026-07-13 | Payment marks consolidated: the four singleton mark cards (Visa, Mastercard, Apple Pay, Google Pay) became one PaymentMarks strip component with a brands prop, matching how checkout surfaces actually use them (28 → 25 patient components). |
| 2026-07-13 | The console atom gaps closed: Select, Checkbox, RadioGroup, Switch, DateField and SearchField built as real console components (native inputs behind the house chrome, the shared field classes, token colours, the global focus ring), published to the library (45 → 51), captured, and documented on the generated reference page. |
| 2026-07-13 | Per-component reference pages, generated: Console components (45) and Patient app components (28), each entry built from the component's own source at generation time: its JSDoc guidance, its typed props with their inline comments, its committed preview as a pasteable snippet, and its capture. Not hand-authored, so the reference cannot drift from the code; the drift gate checks the entry counts. |
| 2026-07-13 | The gallery becomes a pipeline: one command regenerates every gallery image from the freshest capture cells (no more by-hand snapshots), the thin single-state thumbnails were replaced by each component's richest cell (Button now shows all six intents, Icon all 27 glyphs), and a "Two temperatures, componentwise" section pairs console and patient versions of the same concept side by side. |
| 2026-07-13 | Voice depth + the last motion piece: a say/avoid glossary, the counselling register shown as three not-this/this rewrites, the console verdict-moment motion spec (never animate alarm), the reserved blue-fill rule written down, and house-mechanics lint (em-dash ratchet + retired-brand and banned-typeface checks) added to the drift gate. |
| 2026-07-13 | The signature motion, visible: the living orb now breathes and performs its arrival live on this page, rendered from the same timing numbers as the spec (not a video), with an arrival timeline and a working HoldToConfirm. Fully static under Reduce Motion, exactly as the app behaves. |
| 2026-07-13 | Drift gate in CI: contrast maths, token-sync blocks, component counts and motion timings are recomputed from source on every push and compared everywhere they are stated — a contradiction now fails the build. Contrast table corrected to the recomputed values (incl. muted ink honestly rated AA-large at 4.21:1). |
| 2026-07-13 | Clinical safety as a visual language: the flagship page — verdict grammar (BLOCKED / HELD / CLEARED BY QIRI), drug-interaction severity tiers, the four-beat arc, with the AA decisions and clinical sign-off attached. |
| 2026-07-13 | Kiosk conformance floor: the Phase-2 gate — touch targets, standing-distance type, focus & input, hover independence, motion, connectivity and forgiveness minimums. |
| 2026-07-13 | Award-review defect log cleared: docs site consumes the canonical tokens, global mono rule, reduced-motion guard, heading order, reconciled counts & contrast, Spacing + Radius sections, letterboxed tall captures, real icon glyphs in the patient gallery. |
| 2026-07-12 | PharmacySheet published to the patient library — the pharmacy contact sheet (logo, call, directions, hours): 28 patient components. |
| 2026-07-12 | Connective tissue: design principles, this changelog, logo & brand-mark guidance, and a contribution guide. |
| 2026-07-12 | Patient brand & motion published to the patient library (RadialGlow, AmbientBloom, QiriLoading, HoldToConfirm, AppHeader, QiriFooter): 21 patient components. Plus the Signature aura & motion foundations page. |
| 2026-07-12 | Patterns page: the house recipes for forms & validation, data & tables, and empty/loading/error states. |
| 2026-07-12 | Pantone palette true-up: the five core brand colours snapped to exact Pantone across every surface, with AA-safe text tokens added. |
| 2026-07-12 | Foundations published: type scale, colour + WCAG contrast, elevation, motion, iconography, and voice & terminology. |
| 2026-07-11 | Console library: the app shell & navigation, the 10 atomic primitives, and 5 pattern components (35 console components). |
| 2026-07 | Patient library established (via react-native-web): the 15 base primitives. |
The design system has no separate component library to maintain: it is pointed straight at the app's own source, so the way you change a component is to change the app. A short map of how it fits together, and how a change lands.
| To change… | Do this |
|---|---|
| A token (colour, type, spacing, radius, elevation, motion) | Edit the canonical set in design-system/qiri-tokens.css and run the sync, which propagates it into every surface's @qiri-tokens block. Never hand-edit a token in one app. |
| A component | Edit the real component in app source (console-v8/components/* for the console, patient-app/src/components/* for the patient app), then re-sync so the published library rebuilds from it. There is no separate copy to keep in step. |
| These docs | Edit design-site/src/ and rebuild. Foundations and guidance live here, not in the component libraries. |
| Two runtimes, two libraries | Console (React DOM) and patient (React Native) can't share a bundle, so each has its own published library. A change to a shared idea lands in both, on the one token set. |
Whatever you add follows the same conventions as everything else: no em-dashes in published prose, the mono diet (machine font for machine data only), and "interaction" means a drug interaction and nothing else. When in doubt, the Voice & terminology and Design principles sections above are the tie-breakers.