MedX design system
Every component used in the prototype. Class names map to future React components.
Colours
Steel blue sampled from pics/color reference.jpg. #72a6bb is for decoration only (white text on it is 2.7:1). Buttons use blue-600 (4.9:1 with white), links and active text blue-700 (7.0:1). Light is the default; dark applies only when chosen in the account menu.
Typography
Heading 1 · 28px · Atkinson Hyperlegible Next
Heading 2 · 21px
Heading 3 · 17px
Heading 4 · 15px
Body text is 16px. Tables are 15px, labels 14px, small text 13.5px, and nothing is smaller than 13px. Muted text. Small text.
One family everywhere, designed by the Braille Institute so that look-alike characters never look alike: Il1 O0 B8 rn m · MR 10-24781 · K⁺ 6.8 (Atkinson Hyperlegible Mono for IDs).
ایک گولی صبح، ایک گولی شام کھانے کے بعد — Noto Nastaliq Urdu
Form controls
Chips · pills · tags · flags · badges
Avatars
Dose & Rx
Print helpers
KPI tiles
Vitals
Card with table
Action →| Patient | MRN | Amount | Status |
|---|---|---|---|
MI Muhammad Imran 45 y · M | 10-24411 | 2,600 | Paid |
| Zainab Bibi | 10-11873 | 1,850 | Critical |
| Total | 4,450 | ||
Tabs (CSS radio)
Alerts
Follow-up is free within 7 days.
Same phone and similar name found.
Patient is allergic to Penicillin.
Prescription signed and sent on WhatsApp.
Timeline
Stepper & progress
Bar chart
Token cards
Calendar appointment variants
WhatsApp bubble
Lists, key-value, modal
- MRN
- 10-24411
- CNIC
- 35202-1234567-1
- Panel
- Engro Fertilizers
Loading skeletons (shimmer)
Use for component-level loads after the page frame is visible; skip for waits under ~200 ms. Shape matches the real content so nothing jumps when it arrives.
Toasts, tooltips & live state
Motion & theme system
- Theme: light by default on every device, even when the computer is set to dark. "Dark mode" is a switch in the account menu and is remembered per device (assets/js/theme.js).
- Motion: short colour and border changes on hover and focus, and dialogs fade in. No page entrance animation, shimmer, glow or gradients.
- Accessibility: all motion is removed when the computer asks for reduced motion. Red, amber and green always come with a word or an icon.
Status colour map
One meaning per colour across every module (badges, pills, banners, nav counts). Defined in tokens.css as --st-*, aliased to the semantic tokens so dark mode follows automatically. Never use red for anything except danger or critical.
App shell · one sidebar
Generated into every app page by tools/build_shell.py from one table (NAV). Run it after adding a page. Everything works without JavaScript: the nav is plain links, menus use <details>, dialogs use :target.
v4 building blocks
Used on the role homes and the patient visit pages. One primary action per screen; at most 2 secondary.
- Check-in
- Vitals
- Doctor
- Billing
- Pharmacy or lab
- 6To collect
- 64Need review
- 72.8%Checked automatically
Table kit · views, filters, row actions, bulk bar, pager
Used on Patients, Lab worklists and Inventory. Tick a row to see the bulk bar; the count comes from a CSS counter, and Clear is a form reset (no script). Untick a column in Columns on the Patients page to hide it.
Empty, no-results, error & loading states
Every list needs all four. Say what happened, why, and the next best action, never just "No data".
Bags from collection centres appear here when riders hand them over.
MedX Bridge at Gulberg is offline. Results are buffered and will sync; nothing is lost.
| Patient | MRN | Last visit | Payer |
|---|---|---|---|
Forms · validation, sections, stepper, sticky footer
Errors appear only after the user leaves a field (:user-invalid), never on first load. Hints sit under the field and are replaced by the error. Live example: Register patient (also in Urdu).
- Contact
- Identity
- Address
- Payer
Urdu & right-to-left
Put dir="rtl" lang="ur" on a container. Nastaliq gets taller fields and line-height; numbers, MRNs, phones and CNICs stay left-to-right (.mono, .ltr, type="tel"). A language switch is a .i18n wrapper with radios value="en"/"ur" and [data-lang] blocks.
Clinical safety
Safety cues are never decoration: they use the status map, always pair colour with an icon and words, and cannot be dismissed while the risk is open.
Patient banner .patient-banner
First child of <main> on every screen that acts on one patient. Sticky under the topbar. Allergy pill always visible (.pb-allergy, --none for NKDA); chips .pb-chip--warn/--crit/--info/--payer; encounter .pb-enc; "Not this patient?" opens the palette.
Critical-value banner .crit-banner
No close button. Shows the call-timer (.crit-timer) and a single primary action. Clears only after a call is logged with read-back (.readback).
Other patterns
| Pattern | Classes | Live example |
|---|---|---|
| Allergy hard stop: modal with no close, required override reason, safer alternatives | .modal--hardstop .hardstop__* .alt-chips .rx-suggest | consultation.html#allergy-stop |
| Two-identifier check before labels print (CSS :has(:checked) gate) | .id-check .needs-id-check .when-pending .when-ready | lab-accession.html |
| Destructive confirm: reason code + type-to-confirm + undo toast | .danger-confirm .needs-confirm .consequences .confirm-type | billing.html#cancel-invoice |
| Toast with undo (any element with data-toast + data-undo) | .toast-stack .toast |