Mockup work for the new GRID 2.0 website using Penpot
Find a file
Orchestrator (Claude Opus 5) c99eb07141 docs(penpot): move frame-renumber link gotcha into PENPOT.md
task(013): mark in_progress, claim under orchestrator
2026-09-19 14:34:56 +02:00
.cctx docs(penpot): move frame-renumber link gotcha into PENPOT.md 2026-09-19 14:34:56 +02:00
.claude Promote recurring structures to components and add the search suggestions state 2026-09-14 00:14:13 +02:00
.design docs: record the interaction, line-height and identity-corner decisions 2026-09-16 07:15:00 +02:00
images ifcc grid logos 2026-09-15 16:07:17 +02:00
.gitignore Claude skill updates and Penpot reference documentation 2026-09-13 14:57:32 +02:00
.mcp.json Record the directTools fix that made the penpot MCP tools load 2026-09-14 00:16:27 +02:00
.mise.toml feat(spine): split Level 1 from Level 2a, renumber the flow, extend the dataset to 11 2026-09-16 07:45:43 +02:00
CLAUDE.md docs: add HANDOVER.md for the next session 2026-09-15 02:04:00 +02:00
CMUX.md claude and cmux 2026-09-12 12:25:45 +02:00
DESIGN.md feat(partitions): F4c, the Level 1 a partition scheme actually needs 2026-09-16 08:38:51 +02:00
HANDOVER.md fix(prototype): Compare reached the wrong card, and the rib was half wired 2026-09-16 08:47:35 +02:00
PENPOT.md docs(penpot): move frame-renumber link gotcha into PENPOT.md 2026-09-19 14:34:56 +02:00
README.md first commit 2026-09-12 12:12:33 +02:00

GRID 2.0 — Functional Design Document

Purpose. This document specifies the user experience of GRID 2.0 in enough detail to produce a functional mockup. It covers information flow, what is displayed, how it is displayed, what must not be displayed, and how the interface behaves under interaction.

It is deliberately not a visual design document. Typography, colour tokens and final styling belong to the design phase that follows. Where this document names a colour or a shape, it is describing semantics (this state must be visually distinct from that state), not a final aesthetic choice.

Status. Draft for internal review, ahead of Task Force review of the functional mockup. Open decisions are collected in §12.


1. Audiences and their jobs

Two distinct audiences share one platform. They should barely notice each other.

1.1 Public users — researchers and laboratory professionals

They arrive with a question about one analyte. Three variants, in rough order of expected frequency:

Job What they bring What they want back
Compare an interval One reference interval (lower, upper, demographic) Whether published intervals agree with theirs, and which do not
Validate a partition scheme A full set of age- and sex-partitioned intervals Which of their partitions match the literature, and where their partitioning may be too coarse
Browse the evidence Nothing An organised view of what has been published for this analyte

They are scientifically literate but not necessarily familiar with GRID. They do not want to learn an interface. The first screen must be almost self-explanatory.

1.2 Task Force members — contributors, maintainers, administrators

They arrive to do a specific job on the database. They are experts in laboratory medicine, not in software. Their needs are the inverse of the public user's: they will return regularly, they need density and efficiency, and they are willing to learn an interface if it is consistent.

Role Can
Contributor Add and edit analytes and reference intervals, via form or CSV import; view own submission status
Maintainer All of the above, plus review, approve, reject and cherry-pick submissions
Administrator All of the above, plus invite members, assign roles, remove access

2. Governing principles

These apply everywhere. Where a later section appears to conflict with one of these, the principle wins.

2.1 Comparison, never verdict

GRID compares a user's data against published literature. It does not determine whether a value is normal, healthy, correct or acceptable.

This is partly scientific — a published interval is specific to a population, method and analyser, so an interval measured elsewhere is not strictly commensurable — and partly regulatory. Software that produces a clinical determination about an individual can fall within the scope of medical-device regulation. GRID must stay clearly outside that boundary, by design and by wording.

Consequences for the interface:

  • Never use normal, abnormal, pass, fail, healthy, correct, valid.
  • Use agrees with, differs from, falls within, falls outside, is covered by.
  • Colour encodes agreement and divergence, not good and bad. Divergence is information, not error — a red-for-wrong palette is misleading here and must be avoided.
  • Every result screen carries a persistent disclaimer stating that the comparison is against published literature and is intended for professional and research use.
  • The number of publications behind any statement is always visible next to that statement. A result derived from two publications must not look identical to one derived from forty.

2.2 Nothing is invented

The Task Force has been explicit: reference intervals from different studies are not to be averaged or merged unless a Task Forceapproved statistical method is defined. This document takes the stricter reading and extends it to every visual form:

  • No averaging. No synthetic interval is computed from several published intervals and presented as a single result.
  • No interpolation. Published intervals are reported as steps over age partitions. Smoothing them into a continuous curve asserts limits at ages nobody measured. Step edges are preserved. (Where a publication reports a genuine continuous reference curve, it is drawn as a curve, because that is what was published.)
  • No implied precision. An envelope from the lowest lower limit to the highest upper limit is not used as a summary, because one aberrant study widens it until it means nothing.

What is permitted is counting. "Seven of nine selected publications cover this value" is a fact about the dataset, not a statistical estimate. Everything in this document that looks like aggregation is a count, and is labelled as one.

If the Task Force later approves an aggregation method, it is displayed as a visually distinct, explicitly labelled derived layer, never mixed in among published values.

2.3 Publication identity survives

Every interval remains attributable to its source. No view may leave the user unable to answer "which publication produced this?". Where a view necessarily loses identity — the coverage field in §6.3 does — a route back to per-publication detail must be one interaction away.

2.4 Layered complexity

The first screen of any result answers the question in one sentence and one graphic. Everything else is available but not shown.

The layering is consistent across the product:

Level 1   one-sentence answer + one simple graphic
Level 2   the distribution of evidence  (coverage / forest)
Level 3   the full picture over age     (age-axis view)
Level 4   the underlying numbers        (table)

The same rule governs smaller components. The publication set is a single summary line until expanded. Filters are a closed control until opened. Advanced input options are hidden until requested.

Users must never be required to descend a level to get an answer, and must never be prevented from descending.

2.5 Affordance through icons

Every interactive element carries an icon alongside its label. Icons are not decorative and never appear alone unless the metaphor is unambiguous and reinforced by a tooltip (search, close, expand).

This matters more than usual here because the data itself is dense. If the controls are instantly recognisable by shape, the user's attention is free for the content.

2.6 Motion explains, never entertains

Every state change that moves, hides, reveals or reorders something is animated, for one reason: to show the user what happened and where the thing went.

  • Durations: 150250 ms for state changes, 250400 ms for reordering or layout shifts.
  • Easing: standard ease-out for entrances, ease-in-out for reordering.
  • Nothing animates on a loop. Nothing animates purely for delight.
  • prefers-reduced-motion is respected: transitions become instant, but the outcome stays identical.

2.7 Calm density

The data is busy, so the frame around it must not be. Generous whitespace, restrained rules and dividers, one accent used sparingly, no card borders where spacing alone will do. The interface should feel like a well-set scientific figure, not a dashboard product.


3. Information architecture

PUBLIC
  /                          Frontpage — search
  /analyte/{slug}            Analyte landing — two paths
  /analyte/{slug}/browse     Browse published intervals (no user data)
  /analyte/{slug}/compare    Comparison result (after input)

  [user icon]  →  login  →  DASHBOARD

DASHBOARD  (authenticated)
  /dashboard                 Hub — large task icons
  /dashboard/analytes        Analyte browser
  /dashboard/intervals       Reference interval browser
  /dashboard/submit          Submission (form or CSV)
  /dashboard/queue           Approval queue          [maintainer]
  /dashboard/history/{id}    Revision & diff view
  /dashboard/members         User management         [administrator]

The two zones share a header and nothing else. A public user never sees dashboard navigation. A logged-in Task Force member browsing the public site sees their user icon in an authenticated state, and clicking it returns them to the dashboard.


4. Public flow — entry and search

4.1 Frontpage

A single-purpose page. Its job is to get the user to the right analyte.

┌──────────────────────────────────────────────────────┐
│  [GRID logo]                            [user icon]  │
│                                                      │
│                                                      │
│         The Global Reference Interval Database       │
│         One sentence of mission text.                │
│                                                      │
│    ┌────────────────────────────────────────────┐    │
│    │ ⌕  Search an analyte, synonym or LOINC…    │    │
│    └────────────────────────────────────────────┘    │
│                                                      │
│                                                      │
│      1,240 intervals   ·   186 analytes   ·   94     │
│                              publications            │
│                                                      │
└──────────────────────────────────────────────────────┘

The search field is the hero. It is focused on load, wide, and visually the largest element on the page. Everything else is subordinate.

The statistics line is live from the database, not decorative. It communicates scale and freshness in one glance, and it is the honest answer to "is this thing worth using?".

4.2 Search behaviour

Powered by Typesense. Typo-tolerant, instant, and matching across several fields at once.

Matches on: analyte name, synonyms, LOINC code, LOINC long common name, and translated names where available.

Suggestion row anatomy:

┌──────────────────────────────────────────────────────┐
│ ⌕ alkaline phos                                      │
├──────────────────────────────────────────────────────┤
│ ▸ Alkaline phosphatase                               │
│   ALP · LOINC 6768-6            42 intervals · 11 ▸  │
│                                        publications  │
│ ▸ Alkaline phosphatase, bone isoenzyme               │
│   BALP · LOINC 1783-0            6 intervals · 2 ▸   │
│                                        publications  │
└──────────────────────────────────────────────────────┘

Each suggestion shows:

  • Primary name — the canonical analyte name, with the matched substring emphasised.
  • Why it matched — if the match came from a synonym, a LOINC code or a translation rather than the primary name, that term is shown on the second line. A user who typed a synonym must see why this result appeared, or the result looks arbitrary.
  • Data volume — interval count and publication count. This sets expectations before the user commits, and quietly discourages clicking into an analyte with one publication expecting a rich comparison.

Behaviour details:

  • Suggestions appear from the first character, debounced ~120 ms.
  • Full keyboard navigation: arrows, enter, escape. The list is a listbox with proper ARIA roles.
  • Zero results is not a dead end. It offers the closest matches by edit distance, and — for logged-in Task Force members — a direct route to "Add this analyte".
  • A LOINC code pasted whole resolves directly.

Translations. Where LOINC translations are available, the search matches them and the suggestion shows the matched translated term on the second line, with the canonical English name remaining primary. GRID's display language stays consistent; translations serve finding, not presentation, so that intervals are never labelled inconsistently between users.

4.3 Analyte landing — the fork

Reached by selecting an analyte. This page exists to route, and does one more thing: it establishes what data is available before the user invests effort.

┌──────────────────────────────────────────────────────┐
│  ← Search                                            │
│                                                      │
│  Alkaline phosphatase                                │
│  ALP · EC 3.1.3.1 · LOINC 6768-6                     │
│                                                      │
│  42 intervals from 11 publications · U/L, µkat/L     │
│  Age 096 years · both sexes · 6 analyser platforms  │
│                                                      │
│  ┌────────────────────┐  ┌────────────────────┐      │
│  │  ⊞                 │  │  ⇄                 │      │
│  │  Browse published  │  │  Compare your own  │      │
│  │  intervals         │  │  interval          │      │
│  │                    │  │                    │      │
│  │  See what has been │  │  Enter your        │      │
│  │  published for     │  │  reference interval│      │
│  │  this analyte      │  │  and see which     │      │
│  │                    │  │  publications agree│      │
│  └────────────────────┘  └────────────────────┘      │
│                                                      │
└──────────────────────────────────────────────────────┘

Two large icon-led targets, equal weight. The summary line above them is doing real work: it tells the user the age range covered, whether both sexes are represented, and how many platforms — all things that determine whether a comparison will be meaningful.

Thin-data warning. If the analyte has fewer than three publications, an inline note appears above the two options: "This analyte has 2 publications. Comparisons will be based on limited evidence." Setting expectations here is cheaper than disappointing the user two screens later.


5. Public flow — data input

Three input paths, one shared data model. Internally, every submission is a set of partitioned intervals — a single interval is simply a set of size one. This keeps the three paths from diverging in the result views.

5.1 Path A — single interval

The default. Deliberately small.

┌──────────────────────────────────────────────────────┐
│  Compare your interval                               │
│  Alkaline phosphatase                                │
│                                                      │
│   Lower limit        Upper limit        Unit         │
│   ┌──────────┐       ┌──────────┐       ┌─────────┐  │
│   │  40      │       │  129     │       │ U/L   ▾ │  │
│   └──────────┘       └──────────┘       └─────────┘  │
│                                                      │
│   Age                              Sex               │
│   ┌────────┐  to  ┌────────┐  ┌──────┐  ┌─────────┐  │
│   │  18    │      │  65    │  │ yr ▾ │  │ Both  ▾ │  │
│   └────────┘      └────────┘  └──────┘  └─────────┘  │
│                                                      │
│   ▸ Add method, analyser or population  (optional)   │
│                                                      │
│                          ┌─────────────────────────┐ │
│                          │  ⇄  Compare             │ │
│                          └─────────────────────────┘ │
│                                                      │
│   ▸ I have several partitions                        │
│   ▸ I have raw measurements                          │
└──────────────────────────────────────────────────────┘

Design notes:

  • Unit defaults to the most common unit for this analyte in the database, so most users never touch it. The dropdown lists only units with a valid conversion path (§5.5).
  • Age unit selector (days / weeks / months / years) sits beside the age fields, not buried. Paediatric users need it immediately, and its presence signals that paediatric data is welcome.
  • Optional metadata is collapsed. Method, analyser and population sharpen the comparison but must not be a barrier to a first result. Collapsed by default, one click to open, and the result screen later invites the user to add them to narrow the comparison.
  • The two alternate paths are text links at the bottom, not competing buttons. They are for a minority of users and should not complicate the common case.

Validation is inline and immediate, but non-blocking where possible:

Condition Response
Lower ≥ upper Blocking. "The lower limit must be below the upper limit."
Value far outside all published data for this analyte Non-blocking note. "This is well outside the published range for ALP. Check the unit?" with a one-click unit switch.
Age range outside published coverage Non-blocking note stating the covered range.
Empty required field Blocking, on submit only — never while the user is still typing.

The unit check is the single highest-value piece of validation in the product. Unit confusion (U/L vs µkat/L, mg/dL vs mmol/L) is the most likely user error and produces a dramatically wrong result that looks plausible.

5.2 Path B — multiple partitions

For a laboratory validating its own partition scheme. Presented as an editable table.

┌────────────────────────────────────────────────────────────┐
│  Compare your partition scheme                             │
│  Alkaline phosphatase          Unit  ┌─────────┐           │
│                                      │ U/L   ▾ │           │
│                                      └─────────┘           │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Age from   Age to   Unit   Sex     Lower   Upper    │  │
│  ├──────────────────────────────────────────────────────┤  │
│  │  0          14       days   Both    90      273    ⊗ │  │
│  │  15         365      days   Both    134     518    ⊗ │  │
│  │  1          5        yr     Both    156     369    ⊗ │  │
│  │  5          12       yr     Both    141     460    ⊗ │  │
│  │  12         18       yr     Female  62      280    ⊗ │  │
│  │  12         18       yr     Male    52      171    ⊗ │  │
│  └──────────────────────────────────────────────────────┘  │
│    ⊕ Add partition        ⇪ Paste from spreadsheet         │
│                                                            │
│    ⚠ Gap between 18 yr and the next partition              │
│                                                            │
│                            ┌─────────────────────────────┐ │
│                            │  ⇄  Compare scheme          │ │
│                            └─────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘

Design notes:

  • Unit is set once for the whole scheme, not per row. Mixed units within one laboratory's scheme are not a real case and the extra column would cost more than it earns.
  • Paste from spreadsheet is the fastest input method and should be prominent. It accepts tab-separated clipboard content and maps columns using the same auto-mapping logic as the contributor CSV wizard (§8.4). This is deliberate reuse: one mapping component, two placements.
  • Structural validation runs live and is advisory, not blocking:
    • Gaps — "no partition covers 1820 yr"
    • Overlaps — "two partitions cover 58 yr" (a real error in a partition scheme)
    • Sex inconsistency — some partitions sex-split, others not, which is legitimate but worth confirming
  • Rows animate on add and remove. A new row slides in and takes focus in its first cell. A removed row collapses in place.

5.3 Path C — raw measurements

Reserved. Not built in the initial phase; the entry point exists in the design so that the information architecture does not have to change later.

Two distinct operations sit behind this path, and they should be presented as separate choices because they demand very different amounts of data:

Operation Input Output
Verify a published interval ~20 measured values Whether the published interval can be adopted
Derive an interval ≥120 measured values A reference interval with confidence intervals

Both consume individual measurements and produce an interval, which then enters the ordinary comparison flow. Architecturally this is a pre-processing step in front of §6, not a variant of it.

Privacy note (unresolved, §12). Individual measured values are closer to patient data than anything else in the product. Whether this path is public, authenticated, or processed entirely client-side is an open question with legal weight, and should be settled before the path is built.

5.4 Persistence

The user's submitted data is held in localStorage under an analyte-scoped key. Consequences:

  • Returning to the analyte re-offers the previous entry: "Compare your previous interval (40129 U/L, 1865 yr) again?"
  • Nothing is transmitted or stored server-side, which is the correct default for the comparison paths and a hard requirement should Path C ever accept measured values.
  • A visible Clear my data control lives in the result screen footer, next to the disclaimer.

5.5 Units

Unit handling is a first-class correctness concern, not a formatting detail.

  • Units are stored using UCUM codes; display strings are separate from stored codes.
  • Conversion happens only along a defined path. Mass↔molar conversion requires a curated molar mass on the analyte record. Where no path exists, the unit is not offered and the data is not converted — it is shown in its own unit and excluded from direct numeric comparison, with the reason stated.
  • The user chooses the display unit once; everything on the result screen — graphs, table, summary — follows it. Axis labels always state the unit.
  • Where a publication reported a different unit and the value shown has been converted, the table exposes the original value on hover or in an expandable detail. Converted values are never presented as if originally reported.

6. Public flow — comparison results

The heart of the product. Four levels, one screen, progressive disclosure.

6.1 Screen structure

┌──────────────────────────────────────────────────────┐
│  ← Alkaline phosphatase                              │
│                                                      │
│  ╭──────────────────────────────────────────────╮    │
│  │  LEVEL 1 — the answer                        │    │
│  ╰──────────────────────────────────────────────╯    │
│                                                      │
│  ╭──────────────────────────────────────────────╮    │
│  │  LEVEL 2 — where the evidence sits           │    │
│  ╰──────────────────────────────────────────────╯    │
│                                                      │
│  ▸ Publications in this comparison (11)              │
│  ▸ Filters                                           │
│                                                      │
│  ▸ Compare across age                     LEVEL 3    │
│  ▸ All intervals                          LEVEL 4    │
│                                                      │
│  ─────────────────────────────────────────────────   │
│  GRID compares your data against published           │
│  reference intervals. It does not provide clinical   │
│  interpretation. For professional and research use.  │
│                          [⌫ Clear my data]           │
└──────────────────────────────────────────────────────┘

Levels 1 and 2 are open on arrival. Levels 3 and 4, the publication set and the filters are collapsed. A user who wants only the answer gets it without scrolling.

6.2 Level 1 — the answer

One sentence, one graphic, one caveat.

┌──────────────────────────────────────────────────────┐
│                                                      │
│   Your interval falls within the published range     │
│   in 9 of 11 selected publications.                  │
│                                                      │
│      30      40         50        60      U/L        │
│      ├───────┼──────────┼─────────┤                  │
│                                                      │
│         ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        │
│         your interval                                │
│      ░░░░▒▒▒▒▓▓▓▓▓▓▓▓▓▓▒▒▒▒░░░                       │
│      published coverage                              │
│                                                      │
│   2 publications report an upper limit below yours.  │
│                                                      │
└──────────────────────────────────────────────────────┘

The sentence is a count, not a verdict. "9 of 11" is verifiable, degrades gracefully (it still means something with 2 publications and with 40), and carries its own uncertainty.

The graphic is the coverage step function (§6.3), rendered small and without axis furniture. Its only job at this level is to show at a glance whether the user's interval sits in the dense region or at the fringe.

The caveat sentence names the disagreement explicitly. Where all publications agree, it says so.

Multi-partition variant. Path B cannot produce one sentence, because the answer differs per partition. Level 1 becomes a compact per-partition strip:

┌──────────────────────────────────────────────────────┐
│   Your scheme compared with 11 publications          │
│                                                      │
│   014 d      ●●●●●○     5 of 6 agree                │
│   15365 d    ●●●●●●     6 of 6 agree                │
│   15 yr      ●●●●●●●●   8 of 8 agree                │
│   512 yr     ●●●○○○○    3 of 7 agree      ▸         │
│   1218 yr F  ●●●●●○     5 of 6 agree                │
│   1218 yr M  ●●●●○○     4 of 6 agree                │
│                                                      │
│   Your 512 yr partition differs most from the       │
│   published data. Open it to see why.                │
└──────────────────────────────────────────────────────┘

Each row is a target that opens the Level 2 view scoped to that partition. The trailing sentence points at the largest divergence, because that is the finding a laboratory came for.

6.3 Level 2a — coverage

A step function over the value axis: at each value, how many selected publications report an interval covering it.

Why a count and not opacity. Overlaying translucent bands saturates — at 20% opacity, four overlaps and nine overlaps are visually indistinguishable, so the reader cannot recover the number. Counting first and encoding the count as height keeps every level distinguishable, survives dark backgrounds, and does not depend on colour perception.

Rendering rules:

  • Y-axis is publication count, integer ticks only, labelled.
  • X-axis is the value axis in the user's chosen unit.
  • Steps are square. No smoothing, ever (§2.2).
  • The user's interval is drawn above the field as a solid bar, visually distinct in weight and colour from the counted field.
  • Vertical guides drop from the user's two limits through the field, so the reader can see the count at exactly those points.

Interaction:

  • Hovering anywhere reads out: "48 U/L — covered by 7 of 11 publications" plus a listing of which ones, truncated with a count.
  • Clicking a step opens the forest plot (§6.4) scoped to that value.

Mandatory caveat. Coverage weights every publication equally: a study of 1,200 subjects counts the same as a study of 40. This is stated in the panel, not hidden in documentation, and sample size is a column in the Level 4 table. If the Task Force later approves a weighting scheme, it becomes an explicit toggle labelled as a derived view.

6.4 Level 2b — forest plot

The identity view. One row per publication, each drawn as its reported interval on a shared value axis, with the user's interval at the top and guide lines dropping through.

   Your interval    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
                    ┊              ┊
   Smith 2019     ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
   Tanaka 2021  ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
   Lopez 2018        ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
   Weber 2022      ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
   Chen 2020    ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
                    ┊              ┊
        30       40       50       60   U/L

Why this form. A forest plot is immediately legible to this audience, preserves publication identity (§2.3), invents nothing, and makes disagreement visible as spatial spread rather than hiding it in an average.

Row anatomy. Publication label, the interval bar, and a compact metadata strip: n, method, analyser. The metadata is what lets a reader explain a divergence — Lopez sitting high may be a different platform, and that is the insight.

Ordering. Default by lower limit ascending, so the shape of the spread is readable. Alternative sorts: publication year, sample size, agreement with the user's interval.

Partition relation badges. When a publication's age partition does not exactly match the user's, the row carries a badge (§6.6).

6.5 Level 3 — the age-axis view

Everything, over age. This is where a partition scheme is really evaluated, and where a single interval gains context.

Axis. Age is proportional, not categorical. Equal-width category slots give a two-week neonatal partition the same visual weight as a twenty-year adult partition, which is a serious distortion for an analyte like ALP. A proportional axis makes paediatric partitions correctly small — which creates the opposite problem, so it is paired with:

  • A range window control — presets (neonatal / paediatric / adult / all) plus a draggable range.
  • A log or piecewise option for age where the analyte's paediatric detail warrants it. Selectable, never the silent default, and always labelled on the axis.

Layers, back to front:

  1. Published intervals, as a two-dimensional coverage field: age columns on X, value on Y, count encoded as fill intensity. Same counting logic as §6.3, computed per age column. Every edge is a real reported boundary; nothing is interpolated between columns.
  2. The user's interval or scheme, drawn in front as an outlined stepped ribbon, solid and clearly foreground.
  3. Guides on hover.

With few publications the field naturally degrades to individual bands, which is correct and needs no special case. With many it becomes a readable density shape.

Sex. Sex cannot share the age axis. Two stacked panels with a shared X-axis, or a toggle. Encoding sex as colour on top of a colour-encoded density field is not legible and must not be attempted.

Boundary proximity warning. Where the user's demographic sits close to a partition edge in the published data — within roughly 10% of the band width — an inline note appears: "Your age range ends at 18 yr, close to the boundary of several published partitions. Limits differ above and below it." This is a statement about the data, not about the person, and so stays within §2.1.

Interaction. Clicking any age column opens the forest plot for that cross-section — the "who" behind the "where".

6.6 Partition matching

When the user's partition does not exactly match a publication's, the relation must be classified rather than silently included or excluded. Four cases, taking a user band of 512 yr:

Relation Example Meaning Default
Exact 512 yr Directly comparable Include, no badge
Broader 018 yr Applies across the band, but that study did not look for variation inside it Include, badge broader
Finer 58, 810, 1012 yr That study found variation inside the user's band — the user's partitioning may be too coarse Include all sub-partitions separately, badge finer
Partial 1015 yr Speaks only to part of the band Include, badge partial overlap, and offer exclusion

The finer case is the most valuable interaction in the product and must not be collapsed. Averaging three sub-partitions into one comparable interval would destroy exactly the finding a laboratory came for — that the analyte moves within their band.

Important: the coverage views (§6.3, §6.5) need none of this, because they are computed per column and geometry resolves inclusion automatically. Matching rules are needed only where a scalar count is produced — the Level 1 sentence and the forest plot. This is a useful separation: the graphics can be built and demonstrated before the Task Force has finalised the matching policy.

Policy ownership. Whether a partially-overlapping publication counts as evidence is a scientific judgement belonging to the Task Force. The interface implements the mechanism, exposes the classification, and takes its defaults from Task Force configuration.

6.7 Level 4 — the table

The numbers, for readers who want them and for export.

Columns: publication, year, n, age range, sex, population, country, method, analyser, unit, lower, upper, statistical method, CI where available, derivation (direct/indirect), relation badge.

Rules:

  • The user's row is pinned at the top, visually distinguished, and labelled Your interval — never given a publication-like label.
  • Cells that differ notably from the user's value are highlighted; the highlight indicates divergence, not error (§2.1).
  • Converted values carry a marker; the original value and unit are available on hover.
  • Sortable on every column. Virtualised beyond ~100 rows.
  • Export to CSV includes every column plus the publication identifiers, so the export is citable.

6.8 The publication set panel

Collapsed by default, one summary line:

▸  Publications in this comparison   11 of 14 included    ⓘ

Expanded, a table of rows that behave like items, not spreadsheet cells:

▾  Publications in this comparison   11 of 14 included

   ⌕ Filter publications…

   ●  Smith 2019      n=412   Roche Cobas    2 partitions   ⊖
   ●  Tanaka 2021     n=1,203 Abbott Arch.   6 partitions   ⊖
   ●  Lopez 2018      n=88    Siemens        3 partitions   ⊖
   ─────────────────────────────────────────────────────────
   ○  Weber 2022      n=210   Beckman        4 partitions   ⊕
   ○  Chen 2020       n=95    Roche Cobas    2 partitions   ⊕
       excluded

Interaction — this is the signature interaction of the product:

  • Each row has a toggle. Clicking it dims the row, and animates it downward to the excluded group below the divider (~300 ms, ease-in-out). The user sees where it went; nothing vanishes.
  • Excluded rows remain visible, dimmed, with a re-include control. Exclusion is always reversible in one click.
  • Every result view updates as the row moves. The Level 1 count, the coverage field and the forest plot all animate to their new state. The causal link between the action and the consequence is the entire point.
  • The panel is searchable, because publication counts will grow.
  • Bulk controls at the panel head: include all, exclude all, and invert.

Why exclusion rather than filtering. Filters express a rule; exclusion expresses a judgement. A researcher who knows a particular study used a superseded method wants to drop that study specifically, not construct a filter that happens to catch it. Both mechanisms exist and both feed the same set.

6.9 Filters and chips

Filters narrow which publications are in scope. Available on: age, sex, unit, specimen type, method, analyser/platform, country/region, publication year, sample size, derivation (direct/indirect), and — when the Task Force defines it — evidence grade.

▾  Filters

   ┌────────────┐ ┌────────────┐ ┌────────────┐
   │ Analyser ▾ │ │ Method   ▾ │ │ Region   ▾ │
   └────────────┘ └────────────┘ └────────────┘

   ⊗ Roche Cobas   ⊗ Abbott Architect   ⊗ Europe

Chips are mandatory. Every active filter appears as a chip below the controls. The user must be able to see the entire active filter state without reopening a single dropdown. Clicking a chip's ⊗ removes that filter.

Behaviour:

  • Chips animate in on selection and out on removal.
  • A Clear all appears once two or more filters are active.
  • Filter counts are live: each dropdown option shows how many publications it would leave.
  • An option that would leave zero publications is disabled, not merely unhelpful.
  • Filters and exclusions compose. The summary line reflects both: "11 of 14 included — 2 filtered out, 1 excluded manually."

Filters exclude, they no longer select. In the current site, filters chose publications for side-by-side comparison. In GRID 2.0 they determine membership of the unified view. Same controls, different role — worth stating explicitly in Task Force review, since returning users will carry the old mental model.

6.10 Empty and edge states

State Response
No publication matches the demographic "No published interval covers a 3-month-old for this analyte." Offer: widen age, or view what is available. Never an empty graph.
All publications excluded The views hold their frame and dim; a note offers Include all.
One publication only Coverage renders as a single band. The Level 1 sentence adapts: "The only publication covering this demographic reports 45125 U/L."
No conversion path to the chosen unit State it plainly, name the affected publications, keep them in the table but out of the numeric comparison.
User interval far outside everything Prominent unit check offering a one-click switch, before showing a result that would look alarming.

7. Dashboard — entry

7.1 Authentication

The user icon in the header is the only entry point. Logged out, it opens a login panel. Logged in, it shows an authenticated state, and clicking it goes to the dashboard.

No dashboard navigation is ever visible to a public user. The Task Force should experience the dashboard as behind the public site, not beside it.

7.2 The hub

Large icon targets, centred, calm. This screen exists so that an occasional contributor knows immediately where to go.

┌──────────────────────────────────────────────────────┐
│  [GRID]                                  [user icon] │
│                                                      │
│   Good afternoon, Dr Okonkwo                         │
│                                                      │
│   ┌──────────────┐  ┌──────────────┐  ┌────────────┐ │
│   │      ⊕       │  │      ✓  ③    │  │     ⊞      │ │
│   │              │  │              │  │            │ │
│   │  Add data    │  │   Review     │  │   Browse   │ │
│   │              │  │ submissions  │  │  database  │ │
│   │  Analyte or  │  │              │  │            │ │
│   │  interval,   │  │  3 waiting   │  │  186       │ │
│   │  form or CSV │  │  for you     │  │  analytes  │ │
│   └──────────────┘  └──────────────┘  └────────────┘ │
│                                                      │
│   ┌──────────────┐  ┌──────────────┐                 │
│   │      ⟲       │  │      ⚇       │                 │
│   │              │  │              │                 │
│   │   History    │  │   Members    │                 │
│   │              │  │              │                 │
│   │  Recent      │  │  Invite and  │                 │
│   │  changes     │  │  manage      │                 │
│   └──────────────┘  └──────────────┘                 │
│                                                      │
│   Your recent activity                               │
│   · Submitted 12 ALP intervals        2 days ago  ▸  │
│   · Ferritin analyte approved         5 days ago  ▸  │
└──────────────────────────────────────────────────────┘

Rules:

  • Tiles are role-filtered. A contributor does not see Review submissions or Members — no disabled tiles, no permission errors. The interface shows only what the person can do.
  • The badge is the notification system. A count on Review submissions is what brings a maintainer back. It must be accurate and must clear immediately on action.
  • Recent activity below gives returning users a direct route back to work in progress, which is the most common return case.

8. Dashboard — contribution

8.1 Browsers

Analyte and interval browsers share one virtualised table component: search, sortable columns, row click to detail. Filters mirror the public side, so a member learns one filter vocabulary.

Each row exposes: edit, view history, and (maintainers) status.

8.2 Submission forms

Forms carry the data dictionary. This is a design responsibility, not a documentation one: the form must communicate what is required, what format, and why, at the point of entry.

Per field: label, unit or format hint, inline validation, and an ⓘ affordance giving the dictionary definition. A contributor should never need a separate specification document open beside the form.

Structure:

  • Grouped into sections — identification, population, method, values, source — with the section rather than the page as the unit of progress.
  • Publication metadata is fetched, not typed. The contributor enters a PMID or DOI; title, authors, journal and year are retrieved and shown for confirmation. This is a large reduction in effort and a large reduction in inconsistency.
  • Draft autosave, with visible state. Task Force members are busy and will be interrupted.
  • On submit, a plain-language confirmation of what happens next: a branch is created, a maintainer is notified, and the contributor can track status. Git vocabulary does not appear.

8.3 The Git layer is invisible

Branch, commit, merge request, diff, conflict — none of these words appear in the contributor interface. The user-facing vocabulary is:

Underlying Shown
Branch created Submitted for review
Merge request Submission
Merged Approved and published
Diff What changed
Conflict This entry changed since you started
Commit author Submitted by

Maintainers reviewing changes see What changed, side by side, with changed fields highlighted and unchanged fields dimmed.

8.4 CSV import wizard

Four steps, with a visible stepper.

  1. Upload — drag or select. Template download is prominent before upload, because the best import is one that started from the template.
  2. Map columns — auto-mapped where names permit, with confidence indicated. Every mapping is overridable by dropdown. Unmapped required fields are called out at the top, not discovered later.
  3. Preview and validate — a table of the parsed result with per-row status. Rows failing validation are editable in place. Import proceeds with valid rows only, and the invalid remainder is downloadable for correction.
  4. Confirm — a summary: "142 intervals across 6 analytes will be submitted for review."

The column-mapping component is shared with the public multi-partition paste (§5.2). Build once.

8.5 Approval queue

A list of pending submissions: contributor, analyte, entry count, date, and status. Filterable by contributor and analyte, sortable by age.

Opening a submission gives the side-by-side change view. Actions: approve, reject with a reason (mandatory — the reason is the contributor's feedback loop), or accept selected fields only.

Field-level acceptance presents each changed field with its own accept/reject control and a running summary of what will be applied. For bulk CSV submissions, batch controls sit above with the option to descend into per-entry review.

Outdated submissions surface as an inline warning naming what changed underneath, who changed it and when, with both versions shown side by side.

8.6 Revision history

Per entry, a chronological list: who, when, what changed, and the approving maintainer. Any two revisions are comparable side by side. Reachable from every browser row and from the approval queue.

Entries are retired by status change rather than deletion, so that a superseded or retracted interval — and the reason — remains visible to users rather than silently disappearing.

8.7 Member management

Administrator only. A list with role badges; invite by email with role assignment; remove access. Git identity is established in the background and never surfaced.


9. Component behaviour

Component Specification
Toggle Slides between states; label states the current condition, not the action.
Chip Enters with a short fade-and-scale, exits by collapsing width. Always removable by its own ⊗.
Deactivatable row Dims, then translates to the excluded group over ~300 ms. Never disappears.
Collapsible section Height transition; the disclosure marker rotates. Summary line always states what is inside, including counts.
Tooltip ~400 ms delay on hover, immediate on focus. Positioned to avoid covering the element it describes. Keyboard reachable.
Graph guide Follows the pointer along the relevant axis; snaps to meaningful positions (partition boundaries, the user's limits).
Loading Skeletons matching final layout, never spinners for content. The frame does not jump when data arrives.
Stepper Numbered, current step emphasised, completed steps navigable back.

Tooltips on graph elements carry: the identity of the element (publication or count), the exact values, the demographic, and — where relevant — the metadata explaining a divergence. They are the mechanism by which the graphs stay uncluttered while remaining complete.


10. Accessibility

Not a late pass. Load-bearing here, because the audience skews older and the content is graphical.

  • Every graph has a table equivalent — Level 4 is not an extra, it is the accessible route to Levels 2 and 3.
  • Colour never carries meaning alone. Coverage uses height as well as intensity; agreement and divergence carry text labels.
  • Full keyboard operation, including graph element traversal.
  • Visible focus states throughout.
  • Live regions announce result changes when publications are excluded or filters change.
  • prefers-reduced-motion respected, with outcomes unchanged.
  • Target size adequate for the toggle-heavy publication list.

11. What must never happen

A checklist for reviewing mockups.

  1. A synthetic averaged interval presented alongside or instead of published intervals.
  2. A smooth curve interpolated between age partitions.
  3. Translucent overlapping bands used to imply density.
  4. The words normal, abnormal, pass, fail, healthy in any result.
  5. A result that does not state how many publications it rests on.
  6. An interval displayed with no route back to its publication.
  7. A converted value shown as if originally reported.
  8. A unit conversion performed without a defined path.
  9. Age partitions rendered as equal-width categories.
  10. A publication's finer sub-partitions collapsed into one comparable band.
  11. Git vocabulary in a contributor-facing screen.
  12. A result screen without the disclaimer.

12. Open decisions

Marked for resolution before or during Task Force mockup review.

For the Task Force:

  1. Primary evaluation user — a laboratory validating its own interval, or a researcher checking measurements? This document assumes the former is primary and the latter supported. It affects default input path and result wording.
  2. Aggregation — is any aggregate representation wanted at all, or is counted coverage sufficient as the summary? (§2.2, §6.3)
  3. Partition matching defaults — should partially-overlapping publications be included by default? (§6.6)
  4. Evidence grading — if a scoring scheme is defined, should it filter, sort, or set an inclusion threshold? (§6.9)
  5. Weighting — should coverage ever weight by sample size, and if so by what method? (§6.3)
  6. Translations — which languages, and does display language follow the user or stay canonical? (§4.2)

For us:

  1. Path C placement — public, authenticated, or client-side only, given that it accepts measured values. (§5.3)
  2. Level 3 default window — full age range, or the user's own range with an expand control? (§6.5)
  3. Field structure — extent of alignment with HL7 FHIR ObservationDefinition, to keep later API and LIS connectivity cheap.