# 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 `` 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:` 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. ```