safety.viz demo app, v1.10.0: design review and proposal

A review of the released demo app against four asks, with a proposal for each. Nothing here has been built or changed; every item is yours to accept, change or drop.

Decisions

The calls that are yours. Each has my recommendation first and the alternative after it. The sections below hold the evidence.

  1. Where does the single Experimental label sit in the app?

    Recommend
    At the top left of the open view, above the chart. It is the same place for a chart and for the RBQM tab, it is the easiest thing to tap on a phone, and its detail opens in the page instead of floating over it.
    Or
    Beside the open chart's name in the header. It costs no height, but it is a small target, it scrolls sideways with the chart names on a phone, and it lands in two different rows depending on the view.
  2. What happens to the amber banner that a chart draws inside itself?

    Recommend
    The page that hosts a chart shows the label once, and the chart stays quiet. A chart with no host around it, such as an R widget, shows the same small label itself. Today only one of the five experimental charts draws the banner at all.
    Or
    Keep the banner in every experimental chart and drop the label from the app and the docs pages.
  3. Should each experimental chart say why it is experimental?

    Recommend
    Yes, one sentence each, kept beside the status in the site's settings file. Only the Time-to-Event Explorer has a written reason today. The Hepatic ALT Waterfall, Nephrotoxicity Explorer, Participant Profile, QT Safety Explorer and the RBQM tab need a sentence from you.
    Or
    Show only the general meaning of the word and add reasons later.
  4. One R for the whole app now, or one presentation first?

    Recommend
    One presentation and one wording first, on the two copies of R the app starts today. That is a change to safety.viz alone. Then move to a single shared R as a second step: it needs a small bio.viz release and one real run to prove both workloads fit in one R.
    Or
    Wait and ship both together, so the reader never sees Start R twice.
  5. Where does the Start R control live?

    Recommend
    At the top of any view that uses R, as the same strip in both places. For the biomarker charts that means it leaves the row of chart names.
    Or
    One control in the app header, visible from every tab. It makes most sense once there is a single shared R.
  6. What colour are the hexes for Biomarkers and RBQM?

    Recommend
    Reuse two colours the app already has: amber for Biomarkers and pink for RBQM. No new colours, and the rainbow strip stays as it is. The costs: amber also marks a guessed column on the Data tab, and pink is the colour kept for charts outside the standard domains, which has no tab today.
    Or
    Add two new colours to the palette, or give Biomarkers the blue of the labs file it reads.
  7. Should the RBQM file box stay closed by default?

    Recommend
    Yes, but say on its closed face that this is where your own files go, and keep it open after a drop so the reader sees what was recognised.
    Or
    Always show the drop area, at the cost of pushing the results down.
  8. Two wider consistency items: worth their own piece of work?

    Recommend
    Yes to both, outside this polish pass. First, the same three treatment arms are drawn in three different colour sets across the app. Second, safety.viz chart names use capitals on every word and bio.viz names do not.
    Or
    Leave both; neither is new in this release.

1Standardise how R is loaded and run

The ask: map every place the app loads or runs R, then propose one pattern for the whole app.

What is there today

R runs in two places. They were built one release apart and share almost nothing the reader can see.

Biomarkers tab (five bio.viz charts)RBQM tab
What R doesWorks out the statistics line under a chart. Four of the five charts ask as soon as they draw; Group comparison asks once a biomarker is opened.Runs gsm's workflows on the loaded study and returns the tables the charts draw.
TriggerA Start R pill at the front of the row of chart names.A Start R button in a box at the top of the tab.
What it downloadsR alone, about 13 MB, from webr.r-wasm.org. No packages.R again, then 35 packages (about 40 MB) from repo.r-wasm.org, then gsm's four packages (about 2 MB) from the page.
Which Rbio.viz's own copy.The app's own copy. A second, separate R.
Before the pressPill plus "About 13 MB, once". The full sentence is in the chart's statistics line, below the chart.One long sentence beside the button.
While it loadsPill greys to "Starting R…". The statistics line says "waiting for R…". No steps, no clock.Button greys to "Starting R…", then "Running…". A sentence names each of six steps with seconds so far. The tab says "starting", then "running".
Once readyA greyed "R started" pill stays in the row for good.Button becomes "Run again"; the box reports the run.
If it fails"Try R again" pill. The reason is in the statistics line, in plain ink, far from the pill."Try R again" button. The reason is beside it, in red. The tab says "no R".
Measured1.7 to 4.6 seconds to "R started"; first statistic at about 4.5 seconds.21 to 43 seconds to first results on the pilot study, 30 on the RBQM study. Up to 28 of those seconds sit in one step.
The app header on the Biomarkers tab, with a Start R pill at the front of the chart names
Biomarkers: the control is in the row of chart names.
The RBQM tab's box with a Start R button and a long sentence
RBQM: the control is in a box in the tab.
The app header on a phone on the Biomarkers tab: the Start R pill and its hint fill half the row of chart names
Biomarkers on a phone: the pill and its hint take half the row of chart names.
The RBQM run box during the step that loads gsm's packages
RBQM during its slowest step, up to 28 seconds with only the count changing.
A biomarker chart while R starts: a grey Starting R pill and a waiting line under the chart
Biomarkers while R starts. The only signs are the grey pill and one line under the chart.
The RBQM tab while R starts: a sentence naming the step and the seconds so far
RBQM while R starts. A sentence names the step and counts seconds.

What is inconsistent or wrong

  • Two copies of R. Starting R for a biomarker chart does not start it for RBQM: one place says "R started" while the other still says "Start R". On one load the page made two R workers and asked for the 12.3 MB R file twice.
    Where: bio.viz starts its own, site/vendor/bio.viz/bio.viz.js:255-284; the app starts another, src/app/r-browser.js:159-174, made in src/app/rbqm-view.js:286-295.
  • Two homes for the control. One is in the header's row of chart names, the other inside the view. On a phone the biomarker pill and its hint take about half of that row.
    Where: src/app/page.js:414-421 and 477-497; src/app/rbqm-view.js:656-662.
  • Two button styles for the same action, close but not the same size.
    Where: src/app/styles.js:82-85 against 90 and 106-107.
  • Two wordings for the same promise. One says "the study's data stays in this browser", the other "The files stay in this browser, and R runs here."
    Where: src/app/r-on-request.js:43-46; src/app/rbqm.js:69-75.
  • Progress is shown in one place and not the other. Neither shows a list of steps, so a reader cannot see how far along it is.
    Where: src/app/r-on-request.js:96-105; src/app/rbqm.js:92-106.
  • Two steps each claim to be the slowest. One sentence ends "This is the longest step." and a later one says "takes the longest". In my runs the later one was the slow one.
    Where: src/app/rbqm.js:98 and 102.
  • Failures read differently and both leak the browser's own error text. The biomarker one also stacks two prefixes: "R did not start: Statistics are unavailable: R could not be started (Failed to fetch dynamically imported module: …)".
    Where: src/app/r-on-request.js:47-51 wraps bio.viz's sentence from bio.viz.js:429-431; src/app/rbqm.js:114-119; red text only in src/app/styles.js:109.
  • The ready state is a dead control in one place. "R started" is a disabled pill that never leaves the row of chart names.
    Where: src/app/r-on-request.js:99-104.
  • The release notes say starting R for RBQM "takes a minute or two". I measured 21 to 43 seconds. Worth restating once it is measured on a slow connection.
A biomarker chart after R failed to start: a long sentence in ordinary text under the chart
Biomarkers after a failed start (R's host blocked on purpose). Plain ink, two prefixes, the raw browser error, and the retry control is out of sight at the top.
The RBQM tab after R failed to start: red sentence beside a Try R again button
RBQM after the same failure. Red text, retry beside it, but the box keeps its plum edge and the raw error is still there.

The proposed pattern

  • One trigger model: R on request, as now. Nothing downloads until the reader presses Start R.
  • One component, the R strip, at the top of any view that uses R. Same button, same layout, same four states.
  • One wording, built from one template:
    • Off: "R runs in this browser. Starting it downloads about N MB once, from (hosts). Your data stays here."
    • Starting: a short list of steps that tick off, with seconds so far. No progress bar, because the app does not know the bytes.
    • Ready: "R is ready", with R's version. In RBQM the button becomes Run again and the result follows.
    • Failed: "R did not start", a plain reason, Try again beside it, and the browser's own words behind a "What the browser said" line.
  • The statistics line under a biomarker chart shortens to "Statistics need R. Start R at the top of this chart." The app already writes that sentence, so bio.viz does not change.
  • Tabs keep saying their state in a word or two, in both places or neither. I suggest neither for Biomarkers, since its tab counts charts.
  • One shared R, as the second step:
    • It looks feasible from the code. bio.viz's connection already accepts an R engine handed to it.
    • It is not free. bio.viz keeps the small piece of R it needs private, so bio.viz has to offer an engine that takes an R already running. That is a bio.viz release and a fresh copy here.
    • It is unproven. Nobody has run the statistics file and gsm's packages in one R in a browser. Check memory, and check that their function names do not collide.
    • Once shared, Start R anywhere means R is ready everywhere, and RBQM's first run only adds its packages.
    Where: the engine hand-over is at bio.viz.js:326 and 344-352; the private R helper is at bio.viz.js:164 and 277.

Mockup: the standard R strip

Press Start R. Timings are shortened for the mock; the step names and megabytes are the real ones.

Where
Try

2One Experimental label for the whole app

The ask: one label, with the details on hover and on click. The tiers themselves are not in question: Prototype stays out of the app and uncounted, Experimental ships with a label, and both are set in the site's settings file.

What is there today

Four looks, three wordings, and no way to read the detail on a phone.

FormWhere it appearsDetail
Orange pill in a page titleDocs site: gallery cards, and the demo, evidence, API and guide page of each experimental chart. Once more in a row of the kit page.Hover only.
Tan pill, "Prototype"Docs site: the Patient Journey Explorer's card and pages.Hover only.
Amber banner inside the chartHepatic ALT Waterfall, everywhere it draws, including the app. One view of the Hepatic Explorer, with its own sentence.Always-visible sentence.
Plum outline pill on a tabApp: the RBQM tab. Hidden between 761 and 1339 pixels wide.Hover only.
Plum outline pill in a sentenceApp: the end of the RBQM tab's opening line.Hover only.
NothingApp: Nephrotoxicity Explorer, Participant Profile, Time-to-Event Explorer and QT Safety Explorer. All four are experimental in the settings file and carry no mark in the app.None.
The docs page for the Hepatic ALT Waterfall: an orange Experimental pill by the title and an amber Experimental banner in the chart
Docs site, one page: an orange pill by the title and an amber banner in the chart, with different words.
The Hepatic ALT Waterfall in the demo app, with the amber banner inside the chart
Demo app, same chart: only the amber banner.
The amber banner inside one view of the Hepatic Explorer, with its own sentence
Demo app, one view of the Hepatic Explorer: the banner again, with a third sentence.
The app header at 1440 pixels wide with an Experimental pill inside the RBQM tab
Demo app at 1440 wide: a plum pill inside the RBQM tab.
The app header at 1280 pixels wide with no Experimental pill on the RBQM tab
Same header at 1280 wide: the pill is gone.
The RBQM tab's opening sentence ending in an Experimental pill
RBQM tab: the pill again, at the end of a sentence.
A gallery card with an orange Experimental pill after the chart's name
Gallery card, Experimental.
A gallery card with a tan Prototype pill after the chart's name
Gallery card, Prototype.
The Time-to-Event Explorer in the demo app with no experimental mark anywhere
Time-to-Event Explorer in the app: experimental, and nothing says so.

What is inconsistent or wrong

  • In the app, four of the six experimental things carry no label. The standing rule is that Experimental ships with one.
    Where: a chart's name in the header is built with no status at src/app/page.js:348-361; the settings are at site/config.json:76, 91, 106, 121 and 170.
  • The in-chart banner is called by one chart and one view. The other four experimental charts never call it.
    Where: src/hep-waterfall.js:232; src/hep-explorer/views/migration.js:867-870; the banner is src/shell.js:47-56, styled at 241-243.
  • Three wordings for one meaning.
    • Pills: "Still being worked on, and fine to use: its behaviour and settings may change."
    • Banner: "This chart is experimental: it is tested and documented, but its behaviour and settings may change."
    • The Hepatic Explorer's view has a third sentence of its own.
    Where: scripts/app-libraries.mjs:120-121; src/shell.js:52-53; src/hep-explorer/views/migration.js:868-869.
  • Every pill explains itself through a hover tooltip. A phone has no hover, so on a phone the word is all there is.
    Where: scripts/site-lib.mjs:1523-1530; src/app/page.js:432-439; src/app/rbqm-view.js:651-655.
  • The RBQM tab's pill disappears at common laptop widths.
    Where: src/app/styles.js:216-218.
  • Why a chart is experimental is written down for one chart only, and only on the kit page.
    Where: scripts/site-lib.mjs:1296-1301.
  • Small things: the orange pill takes the serif face inside a title and the sans face elsewhere; a Prototype banner exists in the code and nothing calls it.
    Where: site/site.css:107-123 sets no font; src/shell.js:63-71.

The proposed single label

  • What it is: one small button that says "Experimental", with a caret to show it opens.
  • What it does:
    • Hover opens the detail on a desktop.
    • A click or tap opens it and keeps it open, on any device.
    • Escape, the close button or a second tap closes it. It is reachable by keyboard.
  • Where it sits in the app: top left of the open view, above the chart, for charts and for the RBQM tab alike. Stable charts show nothing, so they lose no height.
  • Where it sits on the docs site: after the chart's name, where the orange pill is now, on cards and page titles.
  • What the detail holds, always in this order:
    • The name: "Hepatic ALT Waterfall is experimental".
    • The one meaning, in the pill's existing words.
    • Why this one: a sentence written for the chart, from a new optional line beside its status in the settings file.
    • Links to its clinical guide and test evidence, the same ones the footnote under the chart already has.
  • A chart with one experimental view, such as the Hepatic Explorer, shows the same label beside that view's heading, inside the chart. The detail's "why" names the view.
  • How Prototype relates: same label, same detail, different word and a dashed outline. It never appears in the app, because prototypes are not in the app. On the docs site its detail says "Not ready for production: on the docs site only, and not in the demo app."
  • What goes away: the amber banner as a separate look, the pill inside the RBQM tab, the pill inside the RBQM sentence, the width rule that hides it, and every hover-only tooltip.

One thing to know before saying yes

The label has to live in the charts' shared shell so that a chart on its own can show it. A change there touches every chart's test evidence, so plan one evidence refresh for the piece.

Mockup: the label and its detail

Tap the label. Switch the view to see how each chart's own reason fits the same detail, and switch the placement to compare the two options in decision 1.

View
Placement
Proposed
The same label on the docs site

Patient Journey Explorer

Same shape, same detail, a different word and a dashed edge. The docs site keeps its own colours.

3Polish for the RBQM tab

The ask: he is happy with what it does, so no change to behaviour. Layout, spacing, hierarchy, type, tables, states, the upload path, wording and fit with the rest of the app.

What is there today

The RBQM tab after a run on the pilot study, at desktop width
After a run on the pilot study, 1280 wide. Three of eight metrics run.
The RBQM tab after a run, at phone width
The same at 390 wide. Nothing overflows.
The RBQM tab's file box open on the RBQM demo study, at phone width
The file box open, on the RBQM demo study.
The RBQM tab after dropping three files while R is running, at phone width
After dropping three files of my own with R already running.

It works, it holds at 390 pixels, and it already uses the app's cards, type and pills. The items below are finish, in rough order of how much they show.

What needs a tweak, and the proposed change

Layout and alignment

  • Two right edges. The opening line, the run box and the file box stop at a reading width while the two result cards run the full width, so the page has a step in it.
    • Proposed: let the run box and the file box take the cards' width. Keep the reading width for the opening sentence only.
    Where: src/app/styles.js:104, 105 and 110 against 123.
  • Notes land in two places. Notes about a load sit above everything; notes from R sit between the file box and the results.
    • Proposed: one place for both, directly under the run box.
    Where: src/app/page.js:546; src/app/rbqm-view.js:409-413.
  • The scatter plot has a caption and the bar chart has none, so the two charts sit at different heights.
    • Proposed: move the caption under the pair.
    Where: src/app/rbqm-view.js:353-362.
The top of the RBQM tab showing narrower boxes above a full-width card
The step between the top boxes and the cards below.

The run box

  • One paragraph carries the result, two timings, a date, five R versions and two more versions. The result is the part the reader wants and it is not set apart.
    • Proposed: a headline, "3 of 8 metrics ran on adsl.csv and adae.csv".
    • Under it, a small line in the app's label face for the timing, the snapshot date and the versions.
    Where: the sentence is src/app/rbqm.js:223-247; it is placed at src/app/rbqm-view.js:656-667.
  • A failure turns the words red and leaves the box's edge plum. Everywhere else in the app a problem box has a red edge.
    • Proposed: the box follows the app's problem style. The R strip in section 1 covers it.
    Where: src/app/styles.js:95 against 105 and 109.
  • Two steps say they are the longest.
    • Proposed: keep the claim on the step that loads gsm's packages, which is the slow one.
    Where: src/app/rbqm.js:98 and 102.
The RBQM run box after a run: one paragraph holding the result, timings, a date and seven versions
The run box after a run: the result is the opening clause of one long paragraph.

The site overview table

  • The table is cut through the middle of a row, with no count and no sign that it scrolls. On the 150-site study it shows about 14 rows.
    • Proposed: size the window to whole rows.
    • Say how many sites there are beside the heading.
    • Add a quiet line at the foot, "136 more, scroll the table".
    • With 20 sites or fewer, show them all.
    Where: src/app/styles.js:125; the heading is src/app/rbqm-view.js:417.
  • On a desktop the number columns are centred and spread across the whole card, so a site's row is hard to follow across.
    • Proposed: right-align the numbers and let the table take only the width it needs.
    Where: src/app/styles.js:126-129.
  • On a phone the column headings are 8 pixels tall.
    • Proposed: a floor of about 10 pixels, with "Red" and "Amber" for the two flag counts at that width.
    Where: src/app/styles.js:219-224.
  • Choosing a cell opens that metric below, and nothing says so.
    • Proposed: one line under the table, "Select a cell to open that metric below."
    Where: src/app/rbqm-view.js:433-438.
  • Not confirmed: the first column is headed "Group" where a reader would expect "Site", and a cell's numbers show only on hover. Both come from gsm.viz. I did not check whether gsm.viz lets the app change either.
The RBQM tab on the 150-site demo study: the table shows fourteen rows and gives no count
The 150-site study: fourteen rows show, and nothing says there are 136 more.
The site overview table cut through its last visible row
The cut row at the foot of the table.
The site overview table on a phone with very small column headings
Phone: headings at 8 pixels.

The metric buttons and charts

  • The hex on a metric that ran is grey. Everywhere else in the app a ready thing wears its tab's colour, and grey means nothing to read.
    • Proposed: the RBQM colour from decision 6, once the tab has one.
    Where: src/app/rbqm-view.js:459; the grey is the fallback in src/app/styles.js:59.
  • The two charts use gsm.viz's own type and its brighter flag colours. They read as a guest, which they are.
    • Proposed: leave them. Not confirmed: whether gsm.viz would take the app's typeface if asked.
The row of metric buttons with grey hexes on the metrics that ran
Grey hexes on metrics that ran, red on those that did not.

The file box and the upload path

  • The way to use your own files is hidden. The closed box reads as a status sentence; nothing on it says files go here.
    • Proposed: keep the sentence and add "Use your own gsm files" at its right, in the app's action style.
    Where: src/app/rbqm-view.js:498-505; src/app/styles.js:110-112.
  • After a drop with R already running, the box closes. The line that says a file was not recognised, or lacks columns, is then out of sight.
    • Proposed: keep the box open after a drop until the reader closes it.
    Where: src/app/rbqm-view.js:502.
  • The lists are wordy. Nine lines read "Raw_SUBJ.csv is Raw_SUBJ, by its name." and eight end "the files and columns it needs are loaded."
    • Proposed: two short columns for files, the file and the domain it was placed in.
    • For metrics, the short name, the full name, and either a tick or what is missing.
    • Keep the full sentence for the rows that have a problem.
    Where: src/app/rbqm-view.js:556-596; the sentences are in src/app/rbqm-files.js.
  • The drop area matches the Data tab's. Good; no change.
The RBQM tab with its file box open at desktop width
The file box open on the pilot study. Closed, only its first line shows.

Empty, loading and error states

  • Nothing loaded: a clear sentence, a disabled button, the file box open. Good; no change.
  • Loading: covered by the R strip in section 1.
  • No metric ran: "No metric ran, so there is no overview to draw." Good; no change.
  • A metric that did not run: its own sentence in a red-edged box. Good; that box should also take the card's width.

Mockup: the top of the tab after these changes

Today
The top of the RBQM tab today
Proposed
Experimental

Risk-based quality monitoring: gsm's site metrics, worked out by R in this browser on the loaded study and drawn with gsm.viz.

3 of 8 metrics ran on adsl.csv and adae.csv

4.4 secondssnapshot 2026-10-09R 4.6.0gsm.core 1.3.1gsm.mapping 1.1.6gsm.reporting 1.1.7workr 1.1.0workflows gsm.kri 1.7.0charts gsm.viz 2.4.1
The loaded study supports 3 of 8 metrics.Use your own gsm files
Site overview 17 sites
SiteEnrolledRedAmberAESAESDSC
7051610▼▼✓✓
7181310✓▲▲✓
715801▼✓✓
7162401▼✓✓
7014100✓✓✓

12 more, scroll the table

Select a cell to open that metric below.

AE ran SAE ran PD did not run SDSC ran

4Other formatting, layout and navigation details

The ask: anything else across the demo page that needs a tweak, starting with the black hexes, and others of the same kind.

The black hexes

  • What you see: the Biomarkers and RBQM tabs, and all five biomarker chart names, have a near-black hex. Every other tab has a colour.
  • The cause: it is written that way. Any tab that comes from a second chart library is given one fixed colour, graphite, the centre hex of the logo. Biomarkers and RBQM are the only two such tabs, so both are graphite.
    Where: the graphite is --lib:#4a525c at src/app/styles.js:47; it is applied to every such tab at src/app/styles.js:56; the rule that picks it is hueClass at src/app/libraries.js:323-325; the RBQM tab is given the same class at src/app/page.js:427.
  • The fix:
    • Let each added tab name its own colour, and fall back to graphite only when it names none.
    • Set the two colours where the app lists its added libraries, so bio.viz does not change.
    • Put the same colour on the RBQM view, which also fixes the grey hexes on its metric buttons.
    Touches: src/app/libraries.js, src/app/styles.js, src/app/page.js, scripts/app-libraries.mjs. Small.
Today
The app header with near-black hexes on Biomarkers, RBQM and the five biomarker charts
Proposed, with the recommended colours

today, both Biomarkers, amber RBQM, pink

Others of the same kind: things the newer tabs did not pick up

  • No place on the docs site. The gallery, its menu and its count cover safety.viz's own charts only. A reader of the docs cannot tell that the app also has five biomarker charts and an RBQM tab.
    • Proposed: a short "Also in the demo app" strip on the gallery page, one line each, linking into the app.
    Where: the RBQM tab's title and one-line description sit unused at site/config.json:6-13; only its status is read, at scripts/app-libraries.mjs:133-137.
  • No footnote under RBQM. Every chart ends with links to its guide and evidence; the RBQM tab ends with nothing.
    • Proposed: the same footnote line, linking to gsm's own documentation and to the tab's test evidence.
    Where: charts get it at src/app/page.js:572-573; a tab's view at 543-562 does not.
  • Chart names in two styles. "Outlier Explorer" and "Results Over Time" beside "Group comparison" and "Cross-tabulation".
    • Decision 8. The names come from each library's own chart list.
  • Treatment arms in three colour sets. The same three arms are blue, red and green in the QT Explorer; blue, red and orange in the Time-to-Event Explorer; and blue, green and orange in the biomarker charts.
    • Decision 8. Each chart carries its own list of colours; there is no shared one.
    Where: src/qt-explorer/getPlugins.js:15; src/time-to-event/getPlugins.js:19; site/vendor/bio.viz/bio.viz.js:3562.
QT Explorer legend: placebo blue, high dose red, low dose green
QT Explorer: blue, red, green.
Time-to-Event Explorer legend: placebo blue, high dose red, low dose orange
Time-to-Event Explorer: blue, red, orange.
Biomarker chart legend: placebo blue, high dose green, low dose orange
Biomarker charts: blue, green, orange.

Navigation

  • On a phone the open tab can be off screen. The tabs scroll sideways, and a link straight to RBQM opens with the strip at its start, so no tab looks open. The same holds for a chart far along its row.
    • Proposed: after each draw, bring the open tab and the open chart name into view.
    • Add a soft fade at the right edge of both rows so it is clear there is more.
    Where: the tabs are rebuilt with no scroll step at src/app/page.js:366-443; the rows scroll from src/app/styles.js:79 and 226-227.
  • Tap targets are 30 pixels tall on a phone, for tabs and chart names alike.
    • Proposed: 40 pixels at phone width.
    Where: src/app/styles.js:72 and 81.
  • The browser tab always reads "safety.viz demo", whichever view is open.
    • Proposed: put the view's name first, such as "RBQM · safety.viz demo".
    Where: scripts/site-lib.mjs:1667; the view changes in select at src/app/page.js:1031-1037.
  • The only way from the app to the docs is a link in the footer. The docs header has a clear way in to the app.
    • Proposed: make the wordmark a link to the docs home.
    Where: src/app/page.js:272-279.
  • Two favicons: orange bars on the docs site, the seven-hex mark on the app.
    • Proposed: the hex mark on both.
    Where: site/shell.html:8-11; scripts/site-lib.mjs:1650 and 1668.
The app on a phone, opened straight at the RBQM tab: the tab strip shows Data, Labs and vitals and part of ECG, none marked open
Opened straight at RBQM on a phone. RBQM is the open tab and it is off to the right.

Holding at 390 pixels

  • Demo app: every view holds except one. In the QT Explorer the table under the chart is 529 pixels wide in a 340 pixel space, and the whole page scrolls sideways.
    • Proposed: let that table scroll inside its own box.
    Where: the table is made at src/qt-explorer.js:829 inside a box with no scroll, styled at src/qt-explorer.js:96.
  • Docs site: three pages run wider than the screen. The site hides sideways overflow, so the extra is cut off and cannot be reached.
    • Any chart's API page: the "On this page" card.
    • The QT Explorer's demo page: the same table.
    • The Hepatic Explorer's demo page.
    Where: the cut-off is site/site.css:72; the card is site/site.css:889.
The QT Explorer's table on a phone, running off the right edge
QT Explorer in the app: the table runs off the right.
A docs API page on a phone, with its contents card cut off at the right edge
Docs API page: the card is cut at the right.

Small things

  • The docs home page still describes itself to search engines as "Nine classic clinical-safety graphics". There are fourteen.
    Where: scripts/site.mjs:78.
  • Focus rings are plum on the app's own controls and blue inside the charts. Both are visible. No change proposed.
  • Neither the app nor the docs site has a dark theme. That is consistent. No change proposed.
  • The app and the docs site are two skins: paper, graphite and plum against espresso and orange. That reads as deliberate. No change proposed beyond the favicon.

Defects in the released app, as opposed to design

  • Hepatic ALT Waterfall on the pilot study: the two arm titles above the chart print on top of each other, and the right-hand panel's title is squeezed until it cannot be read. The active arm's name is every non-placebo arm joined together, which is too long for the space.
    Where: the title is set at src/hep-waterfall.js:853.
  • QT Explorer at phone width: the sideways page scroll described above.
  • Docs site at phone width: content cut off on the three pages described above.
  • Console: starting R logs one error, "Refused to get unsafe header Content-Encoding", once per copy of R, and one warning about its message channel. Both come from the R runtime, not from the app, and nothing breaks. Not confirmed: the exact line in the runtime.
  • No other console errors and no failed requests on any view, at either width.
The top of the Hepatic ALT Waterfall chart with two titles overlapping and a third squeezed to an unreadable size
Hepatic ALT Waterfall, pilot study, 1280 wide.

Every proposed change, quick wins first

One row each. Small is an hour or two with its tests; medium is a day or so; large is several days or reaches into another repository.

ChangeFiles it touchesSize
Give Biomarkers and RBQM their own hex colour, and colour the RBQM metric buttons to matchsrc/app/libraries.js src/app/styles.js src/app/page.js scripts/app-libraries.mjssmall
Bring the open tab and chart name into view on a phone; fade the row's right edgesrc/app/page.js src/app/styles.jssmall
Correct the docs home page's "Nine classic" descriptionscripts/site.mjssmall
Only one RBQM step says it is the longestsrc/app/rbqm.jssmall
Let the QT Explorer's table scroll in its own boxsrc/qt-explorer.jssmall
Stop the docs API card and the Hepatic Explorer demo page being cut off at phone widthsite/site.csssmall
RBQM: one right edge for the boxes and cards; caption under both chartssrc/app/styles.js src/app/rbqm-view.jssmall
RBQM table: whole rows, a site count, a "more" line, right-aligned numbers, larger phone headings, a hint that cells open a metricsrc/app/styles.js src/app/rbqm-view.jssmall
RBQM run box: a headline for the result and a small line for timing and versionssrc/app/rbqm.js src/app/rbqm-view.js src/app/styles.jssmall
RBQM notes in one place, under the run boxsrc/app/page.js src/app/rbqm-view.jssmall
View name in the browser tab; wordmark links to the docs; taller tap targets on a phonesrc/app/page.js src/app/styles.jssmall
One favicon for the docs site and the appsite/shell.htmlsmall
Hepatic ALT Waterfall: titles that fit when the active arm's name is longsrc/hep-waterfall.js, and that chart's evidencesmall
RBQM file box: say that own files go here, stay open after a drop, shorter listssrc/app/rbqm-view.js src/app/rbqm-files.js src/app/styles.jsmedium
A footnote under RBQM, and an "Also in the demo app" strip on the gallery pagesrc/app/page.js scripts/app-libraries.mjs scripts/site-lib.mjs site/config.json site/site.cssmedium
The standard R strip: one control, one wording, steps, a plain failure with the browser's words behind a line. Still two copies of R behind itsrc/app/r-on-request.js src/app/page.js src/app/rbqm-view.js src/app/rbqm.js src/app/styles.js scripts/app-libraries.mjsmedium
The single Experimental label: the component, its place in the app and on the docs pages, a reason per chart, and the old forms retiredsrc/shell.js src/hep-waterfall.js src/hep-explorer/views/migration.js src/app/page.js src/app/rbqm-view.js src/app/styles.js scripts/app-libraries.mjs scripts/site-lib.mjs site/site.css site/shell.html site/config.json, and every chart's evidencelarge
One shared R for the whole appsrc/app/r-browser.js src/app/r-on-request.js src/app/rbqm-view.js scripts/app-libraries.mjs, a bio.viz release, and a fresh copy of bio.viz herelarge
One set of treatment-arm colours across every chartEach safety.viz chart's colour list, bio.viz, and every chart's evidencelarge
One style for chart namesbio.viz's chart list, or src/app/page.js where the names are shortenedsmall

What I could not verify

  • The single-file download of the app. I read its two "cannot start R" sentences in the code and did not download and open the file.
  • R on a slow connection or on a real phone. Timings are from this Mac, in the built-in browser and in a headless browser, at phone size.
  • Whether one R can hold both workloads. It is a reading of the code, not a run.
  • Whether gsm.viz lets the app rename the table's first column, show a cell's numbers on tap, or change the charts' typeface.
  • The Time-to-Event Explorer's arm colours are read from a screenshot; the other two sets are measured from the page.
  • The R widgets in gsm.safety. The proposal for a chart with no host is from the shell's code, not from a widget.

This review was drafted by Claude Code using Opus 5.5 and reviewed by @jwildfire