SupraDashboard / docs /DESIGN.md
Tianyi-Billy-Ma
Deploy: simplify codebase (dead-code removal, behavior-preserving)
1727091
|
Raw
History Blame Contribute Delete
15.7 kB
# SupraDashboard — Design Rationale (v0)
> Companion to `SPEC.md`. This document is the UI/UX contract for `app.py`. It
> covers the aesthetic direction, layout, visual hierarchy, the rule-header color
> system, typography, the streaming/loading UX, and how each SPEC user-flow step
> maps to a concrete UI element.
---
## 0. Aesthetic direction (and why it is NOT the editorial default)
SupraDashboard is an **internal expert-review instrument**, not an editorial or
brand surface. The dominant content is a dense, color-coded scientific reasoning
trace that ~3 chemists will read for minutes at a time, many guests per sitting.
The design goal is *legibility under sustained reading* + *glanceable comparison*
+ *calm chrome that never competes with the data*.
So this is deliberately **not** the warm-cream / serif / terracotta editorial look.
It is a **"scientific instrument"** direction:
- **Palette — cool slate/ink neutrals with a single teal primary.**
- App background: `#F5F7FA` (very light cool slate, almost white but with a blue
undertone so the white reasoning card lifts off it).
- Surface / cards: `#FFFFFF` with a `#E3E8EF` hairline border and a soft shadow.
- Ink (body text): `#1F2933`; muted labels: `#5B6B7B`.
- Primary action / focus / combined-emphasis: **teal** `#0E7C86`
(hover `#0A5F67`). One accent, used sparingly, so it reads as "the system",
not decoration. Avoids the AI-slop purple-on-white gradient entirely.
- Semantic: ok `#1A7F37`, warn `#9A6700`, error `#B42318`.
- **The rule-header accents are the ONLY place saturated color appears in volume**,
and that is intentional: color = meaning (rule index), not chrome. Keeping the
rest of the UI desaturated is what lets the rule colors carry signal.
- **Typography.**
- UI chrome / labels: the system UI stack (`ui-sans-serif, -apple-system, …`) —
chrome should be invisible, not expressive. (System fonts are correct *here*:
this is an instrument, and we're spending the reader's font-attention budget on
the reasoning, not the frame.)
- **Reasoning body + summaries: a serif reading face** —
`"Source Serif 4", "Iowan Old Style", Charter, "Georgia", serif`, **16px /
line-height 1.7**. Prior reviewer feedback explicitly asked for a *bigger,
more readable* reasoning block; a serif at 16/1.7 is measurably easier for
long-form sustained reading than a 13px sans, and it visually marks "this is the
thing to read" vs. the sans chrome around it.
- **Numbers (logKa) + InChIKey: a monospace face** —
`"IBM Plex Mono", "SF Mono", ui-monospace, monospace`. Tabular numerals make the
three predictions vertically scannable and unambiguous.
If the user or brand later mandates the editorial aesthetic, that overrides this —
but for a data-dense expert review tool the instrument direction is the correct
default, per the domain mapping.
---
## 1. The ONE memorable thing
**A reading-grade, color-coded reasoning panel that feels like a lab notebook,
not a chat bubble.** Everything else (structure, prediction table, summaries,
rating) is sized and colored to orbit it. A reviewer's eye should land on the
reasoning first and stay there.
---
## 2. Layout (wireframe)
Single scrolling page, three vertical zones. Max content width ~1280px, centered.
```
┌──────────────────────────────────────────────────────────────────────────────┐
│ SupraDashboard CB[7] host–guest logKa reasoning review │ ← header band
│ ● proxy configured (model=gpt-5.5-codex) ● dataset loaded (142 guests) │ ← health strip (colored dots)
├──────────────────────────────────────────────────────────────────────────────┤
│ [ Guest molecule ▾ ............................. ] [ ▶ Run prediction ] │ ← control bar (sticky-ish)
├──────────────────────────────────────────────────────────────────────────────┤
│ RUN STATUS: ◷ Running trajectory 2/3 — chemistry-guided… │ ← streaming status line (hidden when idle)
├───────────────────────────────┬──────────────────────────────────────────────┤
│ LEFT RAIL (scale 2) │ HERO (scale 3) │
│ │ │
│ ┌─────────────────────────┐ │ REASONING — combined trajectory │
│ │ 2D structure (RDKit) │ │ ┌────────────────────────────────────────┐ │
│ │ [ molecule ] │ │ │ 16px serif, line-height 1.7 │ │
│ └─────────────────────────┘ │ │ │ │
│ │ │ 1. Hydrophobic cavity filling — … ←blue│ │
│ PREDICTED logKa │ │ 2. Portal ion–dipole contacts — … ←purpl│ │
│ ┌──────────┬───────┐ │ │ 3. Desolvation penalty — … ←green │ │
│ │ Combined │ 4.82 │◀ prim. │ │ 4. Preorganization — … ←pink │ │
│ │ Physics │ 4.10 │ │ │ … │ │
│ │ Chemistry│ 5.21 │ │ │ │ │
│ └──────────┴───────┘ │ └────────────────────────────────────────┘ │
│ │ │
│ PHYSICS ⟶ tldr card │ │
│ CHEMISTRY ⟶ tldr card │ │
├───────────────────────────────┴──────────────────────────────────────────────┤
│ EXPERT REVIEW (full-width band, visually set apart by a top divider) │
│ Does the reasoning make chemical sense? │
│ ( ) Absolutely right ( ) Mostly right ( ) Partially right (mixed) … │ ← horizontal radio
│ [ comment textarea, 4 rows ......................................... ] │
│ [ Submit review ] ✓ Saved review #37. Thank you! │
└──────────────────────────────────────────────────────────────────────────────┘
```
Responsive: below ~860px the left-rail / hero `Row` reflows to a single column
(Gradio columns stack), structure on top, reasoning below, table and tldr cards
full-width. The horizontal rating radio wraps. No horizontal scroll at 360px.
---
## 3. Visual hierarchy (largest signal → smallest)
1. **Reasoning panel** — biggest type (16px serif), white card, most screen real
estate (3/5 width), the color comes from here. This is the hero per FR6 + prior
feedback.
2. **Predicted-logKa comparison** — mono numerals; **Combined** row is bolded, gets
a teal left-accent bar and a slightly larger number; physics/chemistry are
secondary (muted). Glanceable in <1s (priority 2).
3. **2D structure** — anchored top-left, fixed card; orienting, not dominant.
4. **Physics / chemistry tldr cards** — small labeled cards, one line each, muted
border + a tiny mode tag. Supportive context (priority 3).
5. **Expert review block** — deliberately set apart by a full-width divider and its
own band so it reads as a distinct *action*, but it sits **below** the reading
zone so it never competes with reasoning (priority 5).
6. **Chrome** (header, health strip, labels) — smallest, desaturated, system font.
---
## 4. Rule-header color system
The combined `<think>` contains numbered binding-driver rules of the form
`N. Title — explanation` or `N. Title: explanation`. We bold the `N. Title` run and
color it by **rule index mod palette** so consecutive rules are visually separable
and a reviewer can say "rule 3 is wrong" and find it instantly.
Palette (saturated, accessible on white, distinct hues — refined from the existing
`RULE_COLORS`; kept on the same blue→purple→green→pink→amber→cyan wheel so rule N
always maps to the same hue across guests):
| idx | hue | hex | WCAG AA on #FFF |
|-----|--------|-----------|-----------------|
| 1 | blue | `#0969DA` | ✓ (4.6:1) |
| 2 | purple | `#8250DF` | ✓ (4.5:1) |
| 3 | green | `#1A7F37` | ✓ (4.7:1) |
| 4 | pink | `#BF3989` | ✓ (4.5:1) |
| 5 | amber | `#9A6700` | ✓ (4.8:1) |
| 6 | cyan | `#1B7C83` | ✓ (4.6:1) |
Rendering rule (refined `_color_reason`):
- Only the `N. Title` span is colored + `font-weight:700`; the explanation stays ink
`#1F2933` so the *header* pops but the prose stays calm and readable.
- Each rule also gets a **faint left border** in its hue (`border-left:3px` + small
left padding) so the rule blocks are scannable as bands even in peripheral vision.
- Non-rule lines (intro/transition prose) render as plain serif body, indented to
align with rule text.
- The whole block is wrapped in `white-space:pre-wrap; font-size:16px;
line-height:1.7; font-family:<serif>` so the SPEC's "≈15–16px / line-height ~1.6"
ask is met (we go 16/1.7, slightly more generous, which prior feedback favored).
---
## 5. Streaming / loading UX (generator-driven)
`predict()` is an **async generator** that yields after each milestone, so the
expert sees structure instantly and results stream in (NFR1, decision-critical SDK
facts). The `.click()` uses `show_progress="full"` so Gradio also paints its native
queue/progress spinner.
Yield timeline (each yield repaints only what changed; everything else is
`gr.update()`):
| # | When | What becomes visible | Status line text |
|---|------|----------------------|------------------|
| 0 | immediately | 2D structure; table skeleton with all `…`; reasoning placeholder "running…"; tldr cards show a pulsing skeleton | `◷ Rendering structure & dispatching trajectories…` |
| 1 | after **combined** returns | combined logKa fills + **full reasoning panel renders** (hero first — the reviewer can start reading while the rest runs) | `◷ Running trajectory 2/3 — physics-guided…` |
| 2 | after **physics** returns | physics logKa + physics tldr card | `◷ Running trajectory 3/3 — chemistry-guided…` |
| 3 | after **chemistry** returns | chemistry logKa + chemistry tldr card | `● Done — 3/3 trajectories. Review below.` |
Design choices:
- **Combined first**, not last. The hero (reasoning) is the slowest-value-per-second
thing to read, so we surface it at yield 1 and let physics/chemistry trickle in.
(Implementer may parallelize the three calls behind the generator; the yield
contract is independent of execution order as long as combined is awaited first.)
- **Status line** is a dedicated `gr.Markdown` that is empty/hidden when idle and
shows a `◷` running glyph (amber) while in-flight, flipping to a green `●` Done.
- **Per-call failure is isolated** (NFR1): if a trajectory raises, that cell shows
`error` in red and the status line notes which trajectory failed, but
already-rendered panels are kept. The generator catches per-mode and continues.
States:
- **Empty (pre-run):** structure card shows a faint flask glyph + "Pick a guest and
Run prediction"; table shows `—` rows; reasoning panel shows a dashed-border
placeholder "The combined reasoning trace will appear here."
- **Error (config):** the health strip dot is red and the reasoning/structure area
shows a single error card with the readable message (proxy/dataset not configured)
instead of a broken run. Never crashes (NFR5).
---
## 6. Component choices (Gradio 5.x)
| UI element | Gradio component | Notes |
|---|---|---|
| Header + health strip | `gr.HTML` | colored status dots, custom markup |
| Guest picker | `gr.Dropdown(value=None)` | blank default (Gradio 5 would else pick first) |
| Run button | `gr.Button(variant="primary")` | teal primary |
| Run-status line | `gr.Markdown` | streamed by the generator |
| Structure | `gr.Image(height=300, show_label=False)` | PIL from RDKit |
| Prediction table | `gr.HTML` | custom card-table so we can emphasize Combined + use mono numerals (Markdown table can't carry the styling) |
| tldr cards | `gr.HTML` x2 | labeled mode cards |
| Reasoning hero | `gr.HTML` | color-coded serif render |
| Rating | `gr.Radio(choices=RATINGS)` | horizontal; secondary placement |
| Comment | `gr.Textbox(lines=4)` | |
| Submit | `gr.Button` | non-primary (secondary action) |
| Feedback ack | `gr.Markdown` | shows saved row id |
| Admin export | `gr.Tab` "Admin" → `gr.DataFrame` + `gr.DownloadButton` | FR12; gated by same `auth=` |
Styling delivered via a `gr.themes.Soft(...)`-based theme (teal primary, slate
neutrals) **plus** a `css=` string for the reasoning serif, the table emphasis, the
status glyphs, and the card borders. Queue enabled:
`demo.queue(default_concurrency_limit=1).launch(...)`.
---
## 7. SPEC user-flow → UI mapping
| SPEC step (§4) | UI realization |
|---|---|
| 1. Pass `APP_AUTH` wall | `launch(auth=_auth())` — unchanged |
| 2. Health line + dropdown | header health strip (two colored dots) + `guest_dd` |
| 3. Pick guest + Run | control bar; `run_btn.click → predict` generator |
| 4. Loading state | run-status line + `show_progress="full"`; structure shows at yield 0 |
| 5. Render structure / table / tldr / reasoning | left rail (structure, table, tldr cards) + hero reasoning panel, filled across yields 0–3 |
| 6. Read, rate, comment | reasoning hero → expert-review band (radio + textbox) |
| 7. Submit + confirm row id | `submit_btn.click → submit_feedback`; ack Markdown shows `#id` |
| 8. Next guest | dropdown stays; new Run resets panels via yield 0 |
FR coverage: FR2 structure (graceful empty), FR5 tag parsing (`—` on miss), FR6
table + color-coded rules, FR7 no-rating warning, FR8 persisted row + id, FR11
health strip, FR12 admin export tab.
---
## 8. Accessibility
- Rule colors all pass WCAG AA (≥4.5:1) on white; color is **redundant** with the
bold weight + left-border band + the literal `N.` number, so color-blind reviewers
still parse rule boundaries (color is never the sole signal).
- Radio + textbox carry visible `label`s; focus ring uses the teal primary at 3:1+.
- Status glyphs (`◷`, `●`) are paired with text, never icon-only.
- Reasoning panel is selectable text (real HTML), not an image, so it's
screen-reader and copy-paste friendly.
```