Diabetes medicines app
A whole single-page app assembled only from opsinjs parts. It records the medicines somebody takes, reminds them, and refuses to do anything else.
What this screen does
Somebody takes four medicines for their diabetes. Two are tablets, one is a weekly injection and one is a pen they use at bedtime. They open this app to answer three questions, in this order: what am I meant to take today, what have I already taken, and what did I tell this app over the last week. It answers those three, it reminds them at times they chose themselves, and it hands the record back in a form they can take to an appointment.
It is the first specimen on this site that is an application rather than one screen. The results screen proves that seven parts compose. This one asks a harder question: whether a product can be built out of the kit, with four destinations, five overlays, a form, a consent decision and a history, and still hold the rules the rest of this section argues for. Thirty-five components meet here, which is over half the roster.
What it deliberately does not do, and this is the design rather than a gap. It calculates no dose. It changes no dose. It says nothing about what to do about a dose somebody did not take. It checks nothing against anything: no interactions, no duplicate medicines, no glucose, no thresholds of its own. It marks nothing as missed on its own authority, because it cannot know. And it stores nothing: there is no account, no sign-in, no server and no network call, which is the honest answer to "where do my medicines go" and is also what keeps the product out of a regime it could not satisfy.
Who this specimen is for. A team building a medicines surface, who need to see what the boundary looks like when it is drawn in components rather than in prose. And a reviewer, who can hold the rendered app against the three screen-level checks in Screens without taking anything on faith.
Composition
No composition tree yet for diabetes-medicines-app. If it turns out to be a single element rather than a compound component, say so here in a sentence. An empty tree and a deliberately flat component look identical, and only one of them is finished.
One file, one default export, and four destinations inside it. Rungs are given by position rather than by name so this page cannot drift from The ladder.
DiabetesMedicinesApp page ground
├── TermGlossaryProvider two words, expanded in place
├── Header page ground
│ ├── Title and date one H1 for the whole application
│ ├── Badge the demonstration marker, on the surface
│ └── IconButton opens the about sheet
├── Main one of four, focus target on change
│ ├── Today
│ │ ├── CareCard the one dose nobody has answered for
│ │ ├── Callout what the app will not say about it
│ │ ├── Card the confirmation, with Undo beside it
│ │ ├── DoseTracker what was recorded, and only that
│ │ ├── Card + Badge what is still to come, which is a plan
│ │ ├── Button opens the log sheet
│ │ └── SourceCitation where every figure on the screen came from
│ ├── Medicines
│ │ ├── Callout the record against the prescription
│ │ ├── CareCard reorder, at the count the person chose
│ │ ├── Card + Badge + Menu one per medicine
│ │ ├── Card + Stepper + Combobox add a medicine, four steps, on the page
│ │ └── Accordion stopped medicines, kept with their dates
│ ├── Record
│ │ ├── SegmentedControl how far back to read
│ │ ├── Card + Value the count, never a grade
│ │ ├── Divider + TimelineEntry one list per day
│ │ └── Card + Field + Textarea the summary, ready to copy
│ └── Reminders
│ ├── Callout what a browser reminder cannot do
│ ├── Card + Switch on, and per medicine
│ ├── Card + Select + RadioGroup when, and what may reach a lock screen
│ └── Card + Checkbox + NumberField reorder, at the person's own number
├── DisclaimerNote page ground, lowest emphasis, every section
├── TabBar sticky, four destinations
└── Overlays
├── LogSheet + RadioGroup record a dose, with no popup inside it
├── Sheet about this demonstration
├── ConsentSheet turning reminders on
└── Dialog confirming a medicine has been stoppedFour composition rules this app exists to demonstrate.
A log and a plan are different objects, and only one of them is a log.
DoseTracker holds what was recorded and nothing
else. A dose that has not happened has no recording instant, and the day's
remaining doses are drawn beside the log as plain cards. The reason is visible in
the console: DoseTracker forwards every instant to
RelativeTime, which treats an instant later
than now as two clocks disagreeing and says so, and which prefixes every entry
with the word "Recorded". Both are correct for a log and both are wrong for a dose
still to come.
The card is not the only surface off the page ground, because an app is not one screen. Four destinations each lift their own groups, and the sticky bar sits on the translucent rung above all of them. What is preserved is the relative claim: a card is one rung above the section it sits in, and nothing on a section competes with the thing that section exists to answer.
Every overlay is somewhere the reader chose to go. Nothing opens on arrival. No dialog, no permission prompt, no toast and no interstitial fires without a press, which is what makes the consent sheet an honest question rather than a gate.
No popup control sits inside a sheet, and that rule was learned rather than chosen. A Select inside the log sheet and a Combobox inside an add sheet both render their list and both are unpressable: the popup and the sheet's own surface sit at the same stacking level, the sheet comes later in the document, so it paints over the open list and a press lands on the sheet behind it. Measured in the browser, not argued from the source. So the log sheet asks which medicine with a radio group, which is the better control for four items on a phone anyway, and adding a medicine is a task on the page rather than a sheet over it, which is what the medications pattern asks for in any case. The workaround is good design either way. The defect is real and is recorded below.
The kit is composed, never re-skinned. Where a part did not exist the product wrote plain markup out of the same tokens, and said so in a comment: the day's plan, the visible labels on the two controls that ship none, and the polite live region on the confirmation.
Preview
The whole app, rendered under the opsinjs product theme. The four destinations at the foot are live, the log sheet saves, and the undo beside a saved record puts it back. Every medicine, dose, time and count in it is invented.
Open the frame at tablet and desktop width and the app stays one column, because the reading order is the same order at every width and a second column would put the thing somebody came for beside something they did not. Switch the frame to dark and every state survives, because no state here is carried by a colour.
Everything in it is synthetic (ADR 0012). The medicines are called "Morning tablet" and "Bedtime pen", never a real drug and never a real strength. The instants are fixed, so the app says the same thing every time it is built. A screenshot of this page must never be mistakable for somebody's own medicines record, which is why the demonstration marker sits on the surface rather than in an about screen nobody opens.
Safety notes
It carries neither colour axis, and that is the finding. No surface in the app
takes data-status or data-category, and none is tinted from either ramp. A
medicines record states no clinical level and names no category, so painting one
would be a verdict the product has no right to. A dose with nothing recorded
against it is never painted the red that means "act now", because whether it
matters is a clinical judgement this product does not own, and a red that means
"you have not logged this" cannot share a screen with a red that means "contact
your care team". The two colour axes is the rule;
this screen is what obeying it all the way looks like.
The app never marks a dose as missed. It cannot know. The missed state
reaches DoseTracker only where the person themselves pressed a control that says
"I did not take it", and a dose nobody has answered for stays at scheduled
however long ago it was due. No clock moves it. This is the one place a medicines
app is most likely to assert something it has not got, and it is the reason
DoseTracker's own specification insists it reaches no verdict about any entry it
draws.
A dose that was not taken routes to a person, and stops. The app does not say "do not double up", it does not say "take it as soon as you remember", and it does not say anything conditioned on which medicine it was or how late it is. The answer genuinely differs by medicine and by person, and a generic answer attached to a specific medicine is treatment advice however it is worded. What the screen does is name who can answer: the pharmacist, the prescriber, and the leaflet in the box.
No number in the app belongs to opsinjs, and none belongs to the product either. The doses are strings the person transcribed from their own label, shown back unchanged rather than parsed into a quantity, because a dose is a written instruction and not a measurement. The count at which a reorder reminder appears is a number the person set. The emergency routing is the product's own copy, carried here precisely because opsinjs ships no emergency number, in the same breath as it ships no reference range: it does not know where the reader is.
The emergency routing is never triggered. It sits in the disclaimer at the foot of every destination and it fires on nothing. A modal that opens because a value crossed a line is the product making a triage judgement, and Emergency and escalation is where that path is specified for a product that owns one.
A dose is written back, never parsed. The add flow offers names and forms and nothing else. There is no strength picker, no prefilled dose and no dose-shaped placeholder, because a list of strengths to choose from is a list of doses and a default is a recommendation. The word "units" is written out in full everywhere it appears, never shortened to a single letter after a number.
Accessibility
Landmarks and headings. One header, one main, one footer and the nav
that TabBar supplies. One H1 names the application, one H2 names the destination
showing, and H3s name the groups inside it. The heading outline read on its own is
a usable summary of whichever destination is open.
Focus follows the destination. The bar swaps the whole of main, so main
carries tabIndex={-1} and takes focus when a destination changes. Without that, a
screen-reader user and a keyboard user are left at the foot of a page that is no
longer the page they were on. The focus ring is never suppressed, and the sticky
bar is the one thing on the screen that can cover a focused control at the end of a
long list. Keyboard and focus sets the
rule.
The confirmation is announced, and it does not leave. A saved record puts a
persistent panel on the surface carrying what was recorded and the undo beside it,
in a role="status" region. There is no toast anywhere in this app, and that is
deliberate: a message that dismisses itself after a few seconds is unreadable at
the reading speed this audience reads at, and it takes the undo with it when it
goes. Timing adjustable is the
constraint; nothing here expires, and no dose changes state while somebody is
reading it.
Colour independence. Nothing on the screen is told by colour. Every dose state carries a word and a distinct lucide silhouette, every control carries its label, and the whole app reads the same in greyscale and under all three simulated colour-vision deficiencies. Colour independence is the rule, and this screen is the easy case for it because neither clinical palette is in use.
Targets and text. Every control floors at the published target size in rem, so it grows with the reader's own text size rather than staying put. Type comes from the opsinjs steps and never from a pixel value, so a reader at 200 per cent gets a larger reading of their own medicines. Nothing in the content truncates: a medicine name wraps, and the dose beside it wraps with it. Target size and motor and Text resizing and zoom set both floors.
One thing does not reflow, and it is measured rather than argued. At 200 per cent text in a 390 pixel frame the document scrolls sideways by 115 pixels, and every element past the edge belongs to the tab bar. Four destinations, each floored at the target size in rem and each carrying a word, come to more than the width they have. Nothing the app owns overflows at that setting, and the app cannot repair the bar from outside it. The question is recorded below rather than worked around, because a workaround here would hide a defect in a component other products will meet.
Two controls ship no visible label, so the product draws one. Select and
NumberField apply their label as an accessible name and expose no id, so the app
renders the same words above each one as a plain span. A label element with
nothing to point at is markup that looks associated and is not, which is why it is
a span, and the visible words match the accessible name so voice control reaches
the control by what a person can see.
Reading order matches visual order on every destination: what needs an answer first, then what was recorded, then what is still to come, then the way to add something, then where it all came from, then the boundary. The boundary sits in the same place on all four, so it is findable rather than merely present.