Files
Rpa_Skills/rpa-dashboard-ai/templates/reference/web/DESIGN.md
T

27 KiB
Raw Blame History

name, description, colors, typography, rounded, spacing, components
name description colors typography rounded spacing components
Superbill Extraction and Automation Tool Extract billing fields from a scan, verify them against the page, post to eClinicalWorks.
ground rail surface surface-2 line line-soft ink ink-2 muted dark-ground dark-rail dark-surface dark-surface-2 dark-line dark-line-soft dark-ink dark-ink-2 dark-muted accent-1 accent-2 clean verify review absent shield confidence-green confidence-green-border confidence-amber confidence-amber-border confidence-red confidence-red-border confidence-unscored confidence-unscored-border
#F4F3F7 #FFFFFF #FFFFFF #F7F6FA #E2DFE9 #ECEAF1 #18151F #3D3850 #66607A #0A0912 #100E1A #151220 #1B1727 #2A2438 #221D31 #F3F1F8 #C9C4D8 #8D87A0 #FF3049 #F26B21 #51A68F #E39B34 #DE5243 #6B6580 #21395A #B6E2B6 #9AC79A #F4E2B6 #D9C79A #F4B6B6 #D99A9A #FFFFFF #D0D0D0
display-page display-figure display-spend display-appbar display-runbar display-drop display-panel display-group button button-sm body body-doc body-table nav meta caption label eyebrow badge signin-title signin-brand signin-body signin-label
fontFamily fontSize fontWeight lineHeight
'Sora' 22px 700 1.2
fontFamily fontSize fontWeight lineHeight
'Sora' 21px 700 1.1
fontFamily fontSize fontWeight lineHeight
'Sora' 24px 700 1.1
fontFamily fontSize fontWeight lineHeight
'Sora' 17px 700 1.15
fontFamily fontSize fontWeight lineHeight
'Sora' 17px 700 1.2
fontFamily fontSize fontWeight lineHeight
'Sora' 16px 700 1.2
fontFamily fontSize fontWeight lineHeight
'Sora' 14px 600 1.25
fontFamily fontSize fontWeight lineHeight
'Sora' 11.5px 600 1.25
fontFamily fontSize fontWeight lineHeight
'Sora' 13px 600 1.2
fontFamily fontSize fontWeight lineHeight
'Sora' 12px 600 1.2
fontFamily fontSize fontWeight lineHeight
'IBM Plex Sans' 14px 400 1.5
fontFamily fontSize fontWeight lineHeight
'IBM Plex Sans' 13.5px 600 1.3
fontFamily fontSize fontWeight lineHeight
'IBM Plex Sans' 13px 400 1.4
fontFamily fontSize fontWeight lineHeight
'IBM Plex Sans' 13px 500 1.3
fontFamily fontSize fontWeight lineHeight
'IBM Plex Sans' 12.5px 400 1.4
fontFamily fontSize fontWeight lineHeight
'IBM Plex Sans' 11.5px 400 1.4
fontFamily fontSize fontWeight lineHeight
'IBM Plex Sans' 12px 600 1.3
fontFamily fontSize fontWeight lineHeight textTransform letterSpacing
'IBM Plex Sans' 10.5px 600 1.3 uppercase 0.1em
fontFamily fontSize fontWeight lineHeight
'IBM Plex Sans' 11px 600 1.2
fontFamily fontSize fontWeight lineHeight
'Sora' clamp(22px, 3.2vw, 26px) 700 1.2
fontFamily fontSize fontWeight lineHeight
'Sora' 16px 700 1.2
fontFamily fontSize fontWeight lineHeight
'IBM Plex Sans' 14.5px 400 1.55
fontFamily fontSize fontWeight lineHeight textTransform letterSpacing
'IBM Plex Sans' 12.5px 600 1.3 uppercase 0.06em
card field control input icon chip swatch full signin-card signin-control
14px 11px 9px 8px 7px 10px 3px 9999px 20px 10px
xs sm md lg xl xxl
4px 8px 12px 16px 18px 22px
app-bar nav-item-active button-primary button-quiet card pill-clean pill-verify pill-review badge-super-admin field-row
background textColor padding
linear-gradient(100deg, {colors.accent-1}, {colors.accent-2}) #FFFFFF 12px 18px
borderLeft background
3px solid {colors.accent-1} linear-gradient(90deg, color-mix(in srgb, {colors.accent-1} 12%, transparent), transparent)
background textColor rounded padding
linear-gradient(115deg, {colors.accent-1}, {colors.accent-2}) #FFFFFF {rounded.control} 9px 16px
backgroundColor textColor border rounded
{colors.surface-2} {colors.ink} 1px solid {colors.line} {rounded.control}
backgroundColor border rounded padding
{colors.surface} 1px solid {colors.line} {rounded.card} 14px 16px
backgroundColor textColor rounded
color-mix(in srgb, {colors.clean} 22%, transparent) {colors.clean} {rounded.full}
backgroundColor textColor rounded
color-mix(in srgb, {colors.verify} 20%, transparent) {colors.verify} {rounded.full}
backgroundColor textColor rounded
color-mix(in srgb, {colors.review} 20%, transparent) {colors.review} {rounded.full}
background textColor rounded
linear-gradient(115deg, {colors.accent-1}, {colors.accent-2}) #FFFFFF {rounded.full}
backgroundColor border rounded gridTemplateColumns
{colors.surface} 1px solid {colors.line} {rounded.field} 78px minmax(0,1fr) 42px 70px

Design System: Superbill Review

Overview

Creative North Star: "The Triage Desk"

Superbill Review reads like an emergency triage desk, not a spreadsheet. Every extracted field arrives already sorted by how urgently it needs attention — green passes through untouched, amber gets a spot-check, red stops for a full look — so a reviewer's eye lands exactly where judgment is actually required and nowhere else. The interface itself stays out of the way: a dark, quiet "console" surface with almost no decoration, so the one signal that is allowed to be loud — the confidence band on a field — is never competing with anything else for attention.

The system is calm, precise, and unhurried by design, because the job it supports is not: a reviewer working through dozens of patients per document needs an interface that does not add its own visual noise on top of already noisy handwritten source material. Confirmed rejection: no bright accent palette, no illustrative flourishes, no "friendly SaaS" warmth — this is an operations tool for people doing careful, repetitive verification work, and it should feel like a well-run night shift, not a marketing site.

Key Characteristics:

  • One loud signal only — the green/amber/red confidence band — everything else recedes
  • Flat by default; depth appears only as a response to state, not as decoration
  • Dense, scannable typography tuned for many short fields in a row, not prose
  • Every screen answers "what needs me right now" before anything else

Colors

Two appearances — Light and Dark, and only those two — declared once as CSS variables in web/src/index.css and registered there as Tailwind utilities via @theme inline. A component names a token (bg-surface, text-muted, border-line); it never names a hex. If a colour decision is not in that file, it is not a colour decision this product makes.

@theme inline rather than plain @theme is load-bearing: inline emits each utility as var(--surface) at the use site instead of snapshotting the value, which is what lets .dark reassign a token and repaint every utility built on it. Plain @theme would freeze the light value into the stylesheet.

Surfaces and ink

token light dark what it is
ground #F4F3F7 #0A0912 the page behind everything
rail #FFFFFF #100E1A the sidebar, and the review screen's own strips
surface #FFFFFF #151220 cards, panels, tables
surface-2 #F7F6FA #1B1727 inset things: inputs, chips, wells
line #E2DFE9 #2A2438 the one border colour
line-soft #ECEAF1 #221D31 the divider between rows of a list
ink #18151F #F3F1F8 anything being read
ink-2 #3D3850 #C9C4D8 text that supports it
muted #66607A #8D87A0 labels and captions, found rather than read

The ink ramp is three steps on purpose. Anything reaching for a fourth is usually reaching for the wrong one of the three.

Brand ramp — the only saturated thing on a page

  • Accent 1 (#FF3049) → Accent 2 (#F26B21), identical in both appearances, and the same two hues the sign-in screen uses — so signing in is a continuation rather than a change of product.
  • linear-gradient(115deg, …) on controls, 100deg on the wide header band.
  • It appears on exactly six things: the header bar, primary buttons, the active nav row's 3px bar and 12% wash, the super-admin badge, the review progress fill, and a batch id. Nothing else gets the gradient.

Grading — semantic, never decorative

  • Clean (#51A68F): legibility ≥0.95 — clean print, passes straight through.
  • Verify (#E39B34): 0.90–0.95 — sampled rather than checked one by one.
  • Review (#DE5243): <0.90 — handwritten or hard to read, always reviewed.
  • Absent (#6B6580): no legibility signal at all. Grey because nothing was read, so there is no clarity to grade — distinct from "read badly".

These appear on status pills, the 3px grade rule down a field input, legend swatches, donut segments and quality meters. Never on a button, never on a heading. The moment green also means "primary action", neither reading survives. BAND_VAR in web/src/bands.ts is the one map from a band to its token.

The workbook fills are not interface colours

confidence-green/amber/red (#B6E2B6 / #F4E2B6 / #F4B6B6) and their borders are still in the token list and still used — but only for the highlight chips drawn over the scanned page. They mirror what patient_extractor_v2.py fills spreadsheet cells with, so a reviewer moving between the sheet and this screen sees one language. Interface tokens would break that, and these pastels would be invisible on a white scan besides.

Named Rules

The One Loud Colour Rule. The brand ramp is the only saturated thing on a page, and grading is the only colour carrying meaning. A third loud hue would compete with the one signal the product exists to surface.

Grading Never Becomes Chrome. A grading colour on a button or a heading destroys both readings at once. The only adjacent case is a destructive action (Remove on the People screen), which uses review as text — not as a fill.

One Border, One Radius. Every card, panel, table container, dropzone and empty state is 1px solid line at 14px. Hierarchy comes from spacing, not from stacking shadows: a page of cards at three elevations reads as a pile, and every screen in this product is a list of peers.

There Is No Third Appearance. An eye mode — the light palette under a warm sepia filter — was removed. It was a third surface to keep in sync forever, it tinted the scanned page a reviewer is trying to read accurately, and both the OS and the monitor already offer the same warmth system-wide.

Typography

Display Font: Sora 600/700 — page titles, panel headings, figures, the app-bar title, buttons, batch ids, group headers in the rail.

Body Font: IBM Plex Sans 400/500/600 — everything read as prose: body copy, nav labels, table cells, captions, form labels, metadata.

Both load from Google Fonts with display=swap, and every rule that names one carries the full system stack (ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Arial, sans-serif) behind it.

This changed. The interface used to run entirely on the system stack — a density argument for 11–13px lists: at label size, Inter beside Segoe UI is a distinction nobody reading a field list notices, and it cost a network dependency on an internal deployment. That argument was sound in isolation and wrong in aggregate. Once the sign-in screen was rebuilt on Sora/IBM Plex, signing in read as leaving one product and arriving at another. Two families across the whole product is the cheaper inconsistency.

Why these two. They are the sign-in screen's pairing, which is the approved mockup's pairing, carried inward rather than re-chosen. Sora has a real 600 and 700 so weights are drawn rather than synthesised, and its figures hold up at 21–24px where this product puts its money. IBM Plex Sans was drawn for interfaces and stays legible at the 10.5–13px the tables and rails live at.

The rule that survives. A blocked CDN must cost voice and nothing else. No layout figure may depend on either font's metrics without a re-measurement noted beside it, and adding a third family needs a reason written down here first.

Numbers. Anything that sits in a column — money, counts, timestamps, confidence figures, percentages — carries tabular-nums (the tnum class). Figures that shift width as they update are the single most common reason a dashboard looks unstable.

Sign-in scale — a documented exception

The sign-in screen shares the two families but not the size or radius scales. It carries the mockup's own geometry so implementation and mockup stay comparable: radius 20px (card) and 10px (fields, button), type clamp(22px, 3.2vw, 26px) headline, 16px brand name, 14.5px lede/fields/button, 12.5px uppercase labels.

This is a deliberate island, not drift. It is the only surface that is neither a list nor a board, it is seen once per session, and it never renders beside a component built from the scales below — so there is nothing for it to be inconsistent with. A value from that list appearing on any other surface is a bug.

Hierarchy

  • Figure Lead (700 weight, 26px, 1.15 line-height): the one dominant quantity on a reporting surface — total spend on the ROI board. At most one per screen; a second one means neither is the answer.
  • Figure (700 weight, 20px, 1.2 line-height): a supporting quantity that still has to be read before its own label — the settled-field percentage, each side of the by-hand comparison.
  • Heading (700 weight, 16px, 1.25 line-height): patient name in the review header, document title, the brand wordmark.
  • Control (600 weight, 13px, 1.3 line-height): navigation that has to be found before it is read — the Extraction/Automation tabs, a batch card's name, a primary button on the batch boards.
  • Body (400 weight, 12px, 1.4 line-height): field values, document metadata, most UI copy.
  • Label (500 weight, 11px, 1.3 line-height): field names, filter pills, band captions, timestamps.

Named Rules

The Flat Hierarchy Problem — partially addressed. The shipped UI used only 11px, 12px, and 16px, roughly 1.45:1 from smallest to heading. A hierarchy that flat asks color and weight to do all the work vision normally gets from size. The Control step (13px/600) was added for the batch-board and tab-bar chrome, which sits above the data-dense lists and needs to be found without competing with a heading. The ramp is now 11/12/13/16/20/26. 16px stays the ceiling inside any data-dense list, reserved for the one heading that anchors the view; 20px and 26px appear only on surfaces that are not lists — today that is the ROI board, which carries both. The sign-in screen no longer counts toward this ramp: it runs on the signin-* steps instead, for the reasons in Typography > "Sign-in scale".

Sign-in screen

The one screen with nothing else on it, so the brand sweep carries the whole left panel rather than a bar across the top — the only place in the product where the palette is decorative rather than functional. Below lg that panel collapses entirely and the form takes the full width: a half-screen of decoration would push the fields under the fold on a laptop. Type runs 26px headline / 20px title / 13px controls / 12px supporting copy.

Display — removed. There used to be a 44–72px step (clamp(2.75rem, 4.6vw, 4.5rem)) for a product wordmark set across the waist of a viewport-tall monogram. Both are gone: the letter spanned the screen with the card sitting on its spine, so the parts that showed around the card read as a ring rather than as a letter. Nothing now exceeds the 26px ceiling, and the sign-in screen's own type is the signin-* steps in the token block above.

The rule divides the lockup. The name sits above the brand rule and AUTOMATION below it, so the rule that cuts the S is also the line of the wordmark — one gesture doing both jobs. The whole block is therefore rotated onto the rule's own −8° axis: set horizontal, the rule climbs ~53px across the width of the name and clips a letter at one end or the other, and the dodge (parking the wordmark in the clear space above) reads as a wordmark avoiding a line. Parallel, the gap stays constant at every viewport width. The tilt is structural, not stylistic — it is the only reason the geometry holds without pinning a gap to a text width the font resolves at runtime.

Implementation note: the lockup's container is h-0 at top-1/2, so it shares the rule's rotation pivot and its own axis is the rule. The two lines are then offset by fixed distances (0.5rem / 0.85rem) rather than by anything computed per breakpoint. The offsets are deliberately unequal because cap height is not box height — uppercase leaves ~8px of empty descender space below the 72px line and only ~2px above the 20px one, so equal offsets read as unequal air.

It is sized like a headline but weighted like a background: white at 88% stepping to 52% across a gradient raked to match the light behind it, so the card's 26px pure-white headline is still the loudest type on the screen. That the sign-in headline leads is unchanged — the wordmark is the room, not the sentence. A wordmark at full white here reverses that and the screen becomes a poster rather than a sign-in.

Gated at xl, not lg. Between the two the card's left edge lands roughly where the wordmark ends; below xl the S carries the screen alone and the card's own header names the product.

The reveal covers the whole hero, not just the letter. The cursor torch lights the monogram and the wordmark together, as one field: the letter goes brand red, the name goes red-into-orange on the same 150° rake as its resting white gradient. It is one mask on one unrotated, full-size layer that carries both marks — which is the only arrangement that works, since a mask on the wordmark's own container would rotate with it and the torch would slide off the cursor. Everything in the hero is therefore rendered twice, once per layer.

Where the name overlaps the letter, a halo separates them. The name is placed to clear the monogram's painted edge where the screen is wide enough, but on squarer screens the letter grows until no channel remains between it and the card, and the name sits on the letter instead. A single tight dark halo behind the glyphs keeps the two from tangling. It stays tight on purpose: a wider one reaches the brand rule ~16px below and veils the very gesture the lockup is built on. The halo is a filter, never a text-shadow — these glyphs are a background clipped to text, and a text shadow on transparent text paints straight through the letterform.

Layout

The document list is a vertical stack of full-width rows on a light Paper background, each row a single click target. Opening a document replaces the list entirely with a two-pane, full-window split (draggable divider, ratio persisted in localStorage): the source PDF on the left inside a Midnight Console Deep canvas, the field-by-field review queue on the right on Midnight Console. Density is high by design — the review pane favors many short rows over few tall ones, since the job is scanning many fields fast, not reading long-form content.

Within the review pane, navigation is patient-by-patient rather than a long scroll: one patient's full field record fills the pane at a time, with Prev/Next controls, a jump dropdown, and left/right arrow-key navigation. The PDF pane follows the active patient's page automatically.

Elevation & Depth

Flat by default. Surfaces sit at the same visual plane at rest — no ambient drop shadows on cards, panels, or list rows. Depth appears only as a direct response to state: the currently-open patient card gets shadow-lg shadow-black/20 to read as "in focus," and the PDF page itself gets shadow-xl shadow-black/40 specifically to read as a physical page sitting on the dark canvas rather than blending into it. Nothing gets a shadow simply for decoration.

Named Rules

The Flat-at-Rest Rule. Depth is a state signal, not a texture. If a shadow is not communicating "this is the thing that's active/focused/physical right now," it does not belong.

Shapes

Corners are consistently soft but modest: rounded-md (6px) is the default for inputs, value chips, and buttons; rounded-lg (8px) for cards and the larger patient/document containers; rounded-full for pill-shaped filter buttons and status badges. No sharp corners, no aggressively rounded (>8px) surfaces — the form language stays understated rather than either clinical-sharp or playful-round.

Components

Every interactive element is restrained and functional: minimal ornament, a single clear affordance, and just enough state feedback to confirm what happened. Nothing asks to be noticed on its own — the confidence bands already carry that job.

Buttons

  • Shape: rounded-md (6px), except pill-shaped filters (rounded-full).
  • Primary (Confirm/OK/Approve): accent-confirm emerald background, dark text, used for "Verify," "OK," and "Approve for posting."
  • Secondary/Neutral: bg-slate-800 with text-slate-300, used for Undo, Retry, and non-destructive secondary actions.
  • Hover / Focus: background lightens one step (hover:bg-slate-700, hover:bg-emerald-400); no transform or shadow added on hover, consistent with the flat-at-rest rule.
  • Disabled: opacity-40 plus cursor-not-allowed, used specifically to make the Approve gate's "you can't do this yet" state legible.

Filter Pills

  • Style: pill (rounded-full), bg-slate-800/text-slate-300 at rest, inverted to bg-slate-200/text-slate-900 when active.
  • State: exactly one pill active at a time (Red / Amber / Missing code / Unscored / All).

Cards / Containers (Patient record)

  • Corner Style: rounded-lg (8px).
  • Background: bg-slate-900 (open) / bg-slate-900/60 (collapsed, in the list-style views).
  • Shadow Strategy: none at rest; shadow-lg shadow-black/20 only on the currently-open item.
  • Border: 1px, border-slate-800 at rest, border-slate-700 when open or hovered.
  • Internal Padding: px-3.5 py-3 header, px-2.5 py-2 per field row.

Inputs / Fields (correction input)

  • Style: rounded-md, border-slate-700, bg-slate-800, text-slate-100.
  • Focus: border shifts to border-slate-500; no glow/ring — consistent with the flat, restrained component philosophy.
  • Placeholder: text-slate-500, always "correct value" — never blank, so an empty input still communicates its purpose.

Confidence Band Chip (signature component)

The one component that is allowed to be visually loud. A field's extracted value renders inside a solid chip colored by its band (confidence-green / -amber / -red), dark text for contrast, with a 1px border one shade darker than the fill. On dark surfaces, unscored/blank never uses the literal white token — see the Literal-White Trap rule under Colors.

Navigation (document-open header)

  • Back control (← All documents, bordered, neutral) plus the current document's name and template, band legend right-aligned on wide viewports.
  • No persistent sidebar or tab bar — this is a single-task, single-document focus model, not a multi-section app shell.

Do's and Don'ts

Do:

  • Do treat the confidence band as the only color allowed to demand attention on a review screen.
  • Do render unscored/blank fields as a dashed muted chip on dark surfaces, never as literal white.
  • Do use shadow only as a state signal (open/active/physical), never as ambient decoration.
  • Do keep every button, pill, and input restrained — one clear affordance, minimal ornament.
  • Do animate transform/opacity for progress and state changes, not width/height — a width transition re-triggers layout on every frame, a transform: scaleX() does not.

Don't:

  • Don't introduce a second loud accent hue anywhere near the confidence bands — it competes with the one signal the product depends on.
  • Don't add ambient drop shadows to cards or panels at rest; depth is earned by state, not applied by default.
  • Don't rely on a single font family across every size and weight without also varying size deliberately — see the Flat Hierarchy Problem: the current 11/12/16px scale is undersized for a system this data-dense and should widen (e.g., toward a 11/13/16/20px, ~1.8:1 progression) the next time typography is revisited.