feat(ui): cohesive frontend polish — design system + per-screen refinement

Distill the UI into a single, recognizable voice: a precision
instrument for one operator on one machine. Bloomberg-coded chrome,
warm-paper detail surfaces, mono-heavy numerics, with one editorial
serif accent for moments of weight.

Design system
- Three-font stack: Geist Sans (UI), Geist Mono (data), Instrument
  Serif (editorial display). No Inter, no system fonts.
- Two surfaces: dark chrome (--background, --accent, --signal) and
  warm paper detail surfaces (--surface, --surface-ink*).
- Inbox has its own Ticker Tape terminal palette (--tt-*).
- Shared component classes: .eyebrow, .mono, .display, .surface,
  .surface-2, .row-hover, .nav-active, .kbd, .editorial, .hairline.
- --m-* token aliases for the legacy drawer components so test-
  asserted class strings keep resolving to the same hue family.

Drawers (Claim + Remit)
- Editorial display face for totals (paid/adjustment amounts).
- Color-coded money tiles: green-tinted paid card, amber-tinted
  adjustment card when non-zero, muted otherwise.
- Tabs get accent-blue underline + CSS-driven active state.
- Validation banners with proper background opacity + ring-around-
  dot success badge.
- StateHistoryTimeline: dashed border, ring around dots, ↳ prefix
  for remit ids.
- DiagnosesList, PartiesGrid, MatchedRemitCard, CasAdjustmentsPanel:
  refined typography, dashed dividers, italic descriptions, mono
  amounts, font-semibold totals, hover row tints.

Inbox Ticker Tape
- Custom RowCheckbox with sr-only input + amber accent.
- Alternating row striping, hover tints, accent rail with inset
  shadow on selection.
- Refined sparkline with glow at high values.
- BulkBar: bottom-floating bar with amber count chip, larger shadow.
- CandidateBreakdown: animated progress bars with amber gradient.
- InboxHeader: Instrument Serif headline, mono day/date stamp,
  pulsing amber status dot.

Dialogs & search
- NewClaimDialog: editorial title, mono NPI/CPT/amount fields.
- SearchBar: refined input row with mono, footer with pulsing
  loading indicator.
- KeyboardCheatsheet: monogram icon chip, hover row states, refined
  eyebrow header.

Primitives
- StatusBadge / ClaimStateBadge: per-state dot indicators.
- SelectItem: data-[highlighted]:outline tokens for keyboard a11y.
- Table primitives: refined header treatment, hover/focus states.

Tests + build
- 354/354 tests passing across 59 files.
- Vite build clean (53.84 kB CSS / 560.86 kB JS).
- Eyebrow assertions updated to match the consolidated .eyebrow
  class (intact visual contract, abstracted class string).
- Badge variant tokens updated to the polished bg-muted/80 /
  /0.14 opacity scale.
This commit is contained in:
Tyler
2026-06-20 22:27:01 -06:00
parent 02841d7e6e
commit fbe9940a3f
70 changed files with 2582 additions and 1663 deletions
+177 -141
View File
@@ -1,7 +1,16 @@
/* Inter Tight (UI sans) + JetBrains Mono (data/mono) for the claim-drawer
Modern palette. Loaded via Google Fonts CDN. Project default face (Geist
Mono) is preserved by fallback chains in --m-font-mono. */
@import url("https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap");
/* =====================================================================
Cyclone — design system
---------------------------------------------------------------------
A precision instrument for one operator on one machine. The visual
language borrows from Bloomberg terminals and Linear-grade
engineering tools: dark, hairline-strict, mono-heavy, with one
editorial serif accent for the moments that deserve weight.
---------------------------------------------------------------------
Type stack:
- Geist Sans · UI, body, controls
- Geist Mono · every number, every id, every code-like datum
- Instrument Serif · editorial display (hero numbers, headlines)
===================================================================== */
@tailwind base;
@tailwind components;
@@ -9,96 +18,98 @@
@layer base {
:root {
/* Refined Apple-HIG dark — true black with a whisper of blue */
--background: 240 6% 4%; /* #0a0a0c */
--foreground: 240 6% 96%; /* #f5f5f7 */
/* ---- Core surfaces ----
A graphite canvas with a whisper of blue. Slightly warm at the
midtones (--card) to keep the chrome from feeling sterile. */
--background: 222 18% 5%; /* #0b0c10 — near-black, cool */
--foreground: 220 14% 96%; /* #f4f5f7 — paper white */
--card: 240 5% 8%; /* #131316 */
--card-foreground: 240 6% 96%;
--card: 222 16% 8%; /* #11131a */
--card-foreground: 220 14% 96%;
--popover: 240 5% 9%;
--popover-foreground: 240 6% 96%;
--popover: 222 18% 9%; /* #13151c */
--popover-foreground: 220 14% 96%;
--muted: 240 4% 12%; /* #1e1e21 */
--muted-foreground: 240 4% 61%; /* #9a9a9f */
--muted: 222 12% 13%; /* #1f2229 */
--muted-foreground: 220 8% 64%; /* #9b9ea6 */
--secondary: 240 4% 12%;
--secondary-foreground: 240 6% 96%;
--secondary: 222 12% 13%;
--secondary-foreground: 220 14% 96%;
--accent: 217 100% 56%; /* #1a73ff — electric, more committed than stock */
/* ---- Accents ----
Electric blue (primary) for actions and emphasis. Amber (signal)
is reserved for "needs attention" states and the Inbox terminal
accents — never decorative. */
--accent: 212 100% 60%; /* #2e8aff — electric, modern */
--accent-foreground: 0 0% 100%;
--signal: 36 92% 56%; /* #f3a531 — warm amber */
--primary: 217 100% 56%;
--primary: 212 100% 60%;
--primary-foreground: 0 0% 100%;
--destructive: 1 100% 60%; /* #ff453a */
--destructive: 358 82% 60%; /* #ec4458 */
--destructive-foreground: 0 0% 100%;
--success: 142 71% 50%; /* #30d158 */
--success-foreground: 144 70% 10%;
--success: 152 64% 46%; /* #2cbd76 */
--success-foreground: 152 70% 8%;
--warning: 38 100% 56%; /* #ff9f0a */
--warning-foreground: 26 80% 14%;
--warning: 36 92% 56%; /* #f3a531 */
--warning-foreground: 26 80% 12%;
--border: 240 4% 14%; /* #232327 */
--input: 240 4% 14%;
--ring: 217 100% 56%;
--border: 222 10% 16%; /* #262932 */
--input: 222 10% 16%;
--ring: 212 100% 60%;
--sidebar: 240 6% 5%;
--sidebar-foreground: 240 6% 96%;
--sidebar-accent: 217 100% 56%;
--sidebar-border: 240 4% 12%;
/* ---- Sidebar ---- */
--sidebar: 222 20% 4%; /* darker than the page */
--sidebar-foreground: 220 14% 96%;
--sidebar-accent: 212 100% 60%;
--sidebar-border: 222 12% 12%;
--radius: 0.75rem; /* 12px */
/* ---- Document surface (drawer detail panels) ----
A faint warm paper. Keeps detail views distinct from the chrome
without going full light. Pairs with editorial serif headlines. */
--surface: 36 18% 96%; /* #f6f3ec */
--surface-ink: 30 12% 9%; /* #181614 */
--surface-ink-2: 30 6% 32%; /* #4f4b46 */
--surface-ink-3: 30 4% 52%; /* #85807a */
--surface-line: 30 14% 14%; /* #24221f */
--surface-line-soft: 30 14% 14% / 0.12;
/* ------------------------------------------------------------------
Modern palette (SP4 claim-drawer)
------------------------------------------------------------------
Light, document-feeling surface tokens for the drawer. Prefixed
`--m-` to avoid colliding with the project's existing tokens
(e.g. the existing `--accent: 217 100% 56%` electric blue, used
via Tailwind utilities everywhere). Same prefix family as the
`--sidebar-*` tokens above. The drawer's component CSS will
reference these via `var(--m-surface)` etc.
Coexists with the dark app shell — the drawer is rendered on a
light surface regardless of global theme.
------------------------------------------------------------------ */
--m-surface: #FAFAF7;
--m-ink-primary: #0A0A0A;
--m-ink-secondary: #6B6B6B;
--m-ink-tertiary: #A0A0A0;
--m-border-heavy: #1A1A1A;
--m-accent: #1D4ED8;
--m-accent-hover: #1E40AF;
--m-error: #DC2626;
--m-error-bg: #FEF2F2;
--m-success: #15803D;
--m-success-bg: #F0FDF4;
--m-warning: #B45309;
--m-warning-bg: #FFFBEB;
/* ---- Ticker-tape terminal (Inbox) ----
Bloomberg-coded: three accent rails on near-black, with the
paper-tone ink for legibility. */
--tt-bg: 220 22% 5%; /* #0a0d12 */
--tt-bg-elev: 220 18% 9%; /* #13171f */
--tt-ink: 36 18% 88%; /* #e2ddd0 */
--tt-ink-dim: 220 6% 58%; /* #8e9098 */
--tt-amber: 36 88% 56%; /* #efa836 */
--tt-oxblood: 354 50% 40%; /* #8a2d35 */
--tt-ink-blue: 212 38% 36%; /* #2e4d77 */
--tt-muted: 220 4% 38%; /* #5b5d62 */
/* ------------------------------------------------------------------
SP6 Inbox — Ticker Tape palette
------------------------------------------------------------------
Bloomberg-terminal vibe. Three accent rails (oxblood / amber /
ink-blue) on near-black surfaces. Prefixed `--tt-` to coexist
with the dark app shell tokens. The Inbox page is full-bleed
dark and reads as a separate working surface from the rest of
the app.
------------------------------------------------------------------ */
--tt-bg: #0a0e14;
--tt-bg-elev: #11161f;
--tt-ink: #e6e1d3;
--tt-ink-dim: #8a8b8e;
--tt-amber: #f4b942;
--tt-oxblood: #8b2c2c;
--tt-ink-blue: #2c4a6e;
--tt-muted: #4a4d54;
/* ---- Legacy aliases (--m-*)
Kept as thin shims so any older component still using the
"warm paper" surface family resolves to the same hue family.
New components should prefer --surface-* / --destructive / etc. */
--m-surface: hsl(var(--surface));
--m-ink-primary: hsl(var(--surface-ink));
--m-ink-secondary: hsl(var(--surface-ink-2));
--m-ink-tertiary: hsl(var(--surface-ink-3));
--m-border-heavy: hsl(var(--surface-line));
--m-font-mono: var(--font-mono);
--m-error: hsl(var(--destructive));
--m-error-bg: hsl(var(--destructive) / 0.08);
--m-success: hsl(var(--success));
--m-warning: hsl(var(--warning));
--m-accent: hsl(var(--accent));
/* Modern type stack — UI sans + data mono. Fallbacks preserve the
existing Geist Mono face for any element that hasn't opted in. */
--m-font-sans: "Inter Tight", ui-sans-serif, system-ui, sans-serif;
--m-font-mono: "JetBrains Mono", ui-monospace, "Geist Mono", monospace;
--radius: 0.625rem; /* 10px — slightly tighter */
/* ---- Type stack ---- */
--font-sans: "Geist", ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
--font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
--font-display: "Instrument Serif", "Iowan Old Style", "Apple Garamond", "Baskerville", "Times New Roman", serif;
}
* {
@@ -111,6 +122,7 @@
body {
@apply bg-background text-foreground;
font-family: var(--font-sans);
font-feature-settings: "ss01", "cv11", "calt";
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@@ -127,20 +139,27 @@
z-index: 0;
background: radial-gradient(
55rem 35rem at 100% -15%,
hsla(218, 100%, 58%, 0.085),
hsla(212, 100%, 60%, 0.07),
transparent 62%
);
}
/* Fine grain — the substrate of a precision instrument. */
/* Hairline grid — the substrate of a precision instrument.
Sits behind everything, gives the dark surfaces a sense of
scale and the work a sense of "field". */
body::after {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
opacity: 0.045;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
opacity: 0.5;
background-image:
linear-gradient(to right, hsl(222 10% 16% / 0.18) 1px, transparent 1px),
linear-gradient(to bottom, hsl(222 10% 16% / 0.18) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, black 30%, transparent 80%);
-webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, black 30%, transparent 80%);
}
/* Custom scrollbar — minimal, hairline, Apple-style */
@@ -164,20 +183,15 @@
/* Focus ring — accent, 2px, with offset.
Components override this with Tailwind focus-visible:ring-* utilities
for a ring+offset combo that matches the rest of the design system.
This base rule covers anything that doesn't opt in (links in
prose, native checkboxes, etc.). */
for a ring+offset combo that matches the rest of the design system. */
:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
border-radius: 6px;
}
/* Respect reduced-motion users globally. */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
@@ -193,11 +207,42 @@
font-feature-settings: "tnum", "ss01";
}
/* Display face — used for KPI numbers and data emphasis */
/* Display face — Instrument Serif. Used for the moments that need
editorial weight: hero numbers, page titles in document surfaces,
and the brand mark. */
.display {
font-family: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
font-family: var(--font-display);
font-weight: 400;
letter-spacing: -0.015em;
font-feature-settings: "ss01";
}
/* Data mono — Geist Mono with tabular-nums. The voice of every
id, code, dollar amount, and timestamp in the app. */
.mono {
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
letter-spacing: -0.04em;
letter-spacing: -0.01em;
}
/* Soft card surface — a 1px hairline + a 1px inner highlight to
give the surface a sense of depth without a heavy shadow. */
.surface {
background-color: hsl(var(--card));
border: 1px solid hsl(var(--border));
box-shadow:
inset 0 1px 0 0 hsl(0 0% 100% / 0.04),
0 1px 2px 0 hsl(0 0% 0% / 0.4);
}
/* Subtle elevated surface — used for cards that should lift a
little more than the default. */
.surface-2 {
background: linear-gradient(180deg, hsl(222 16% 9%) 0%, hsl(222 16% 7.5%) 100%);
border: 1px solid hsl(var(--border));
box-shadow:
inset 0 1px 0 0 hsl(0 0% 100% / 0.05),
0 1px 3px 0 hsl(0 0% 0% / 0.5);
}
/* Hairline — a 1px separator that reads as a divider, not a border */
@@ -205,16 +250,8 @@
border-color: hsl(var(--border) / 0.6);
}
/* Soft card surface with a 1px hairline + subtle inner highlight */
.surface {
background-color: hsl(var(--card));
border: 1px solid hsl(var(--border));
box-shadow:
inset 0 1px 0 0 hsl(0 0% 100% / 0.035),
0 1px 2px 0 hsl(0 0% 0% / 0.5);
}
/* Active nav indicator — a thin left-aligned accent line */
/* Active nav indicator — a thin left-aligned accent line with a
subtle glow that matches the instrument aesthetic. */
.nav-active {
background-color: hsl(var(--muted) / 0.6);
color: hsl(var(--foreground));
@@ -231,8 +268,7 @@
box-shadow: 0 0 12px hsl(var(--accent) / 0.5);
}
/* Skip link — invisible until focused. Sits above the scan element on
first paint so it is the first Tab stop in the app. */
/* Skip link — invisible until focused. */
.skip-link {
position: fixed;
top: 0.75rem;
@@ -264,7 +300,6 @@
0 0 0 4px hsl(var(--ring)),
0 8px 24px hsl(0 0% 0% / 0.45);
}
/* Reduced motion: skip the slide. */
@media (prefers-reduced-motion: reduce) {
.skip-link {
transition: none;
@@ -277,44 +312,66 @@
align-items: center;
gap: 0.15rem;
height: 1.375rem;
min-width: 1.375rem;
padding: 0 0.4rem;
border-radius: 0.375rem;
border: 1px solid hsl(var(--border));
background: hsl(var(--muted) / 0.5);
color: hsl(var(--muted-foreground));
font-family: "Geist Mono", ui-monospace, monospace;
font-family: var(--font-mono);
font-size: 10.5px;
font-weight: 500;
letter-spacing: 0.04em;
line-height: 1;
}
/* Section label — the all-caps eyebrow used everywhere */
.eyebrow {
font-size: 10.5px;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: hsl(var(--muted-foreground));
}
/* Editorial serif headline — for hero numbers and document
surfaces. A single line height that breathes. */
.editorial {
font-family: var(--font-display);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.05;
}
/* Row hover state — slightly more refined than the default
table hover. A subtle tint + a left accent strip. */
.row-hover {
transition: background-color 150ms ease;
}
.row-hover:hover {
background-color: hsl(var(--muted) / 0.4);
}
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
.text-pretty {
text-wrap: pretty;
}
.ring-inset-hairline {
box-shadow: inset 0 0 0 1px hsl(var(--border) / 0.6);
}
}
/* ----------------------------------------------------------------------
Print styles — a clean paper-record rendering of detail drawers.
When an operator hits Cmd-P / Ctrl-P with the ClaimDrawer (or the
future RemitDrawer) open, we strip the app chrome (sidebar, header,
close button, skeletons, animations, dark theme), collapse the
dialog to a static full-width block, and let its section headers +
tables flow onto the page without internal scrolling or backdrop
bleed. Result: a readable, ink-friendly paper trail of the claim.
---------------------------------------------------------------------- */
@media print {
/* Hide the app chrome — only the drawer content matters on paper */
aside, /* sidebar */
header, /* app header within Layout */
[data-print="hide"],
button[aria-label^="Close"] {
aside, header, [data-print="hide"], button[aria-label^="Close"] {
display: none !important;
}
/* Reset the dark theme for ink savings and readability */
:root {
--background: #ffffff;
--foreground: #000000;
@@ -325,14 +382,10 @@
--border: #cccccc;
--accent: #1a1a1a;
}
/* Reset the page surface */
body {
background: #ffffff !important;
color: #000000 !important;
}
/* Drawer becomes a static block at full width */
[role="dialog"] {
position: static !important;
inset: auto !important;
@@ -344,8 +397,6 @@
box-shadow: none !important;
border: none !important;
}
/* Strip backdrop + animations */
[data-state="open"][data-radix-dialog-overlay],
.animate-fade-in,
.animate-shimmer,
@@ -353,27 +404,12 @@
animation: none !important;
transition: none !important;
}
/* Show the panel contents without internal scrolling */
[role="dialog"] * {
overflow: visible !important;
}
/* Tables break gracefully */
table {
page-break-inside: avoid;
}
/* Avoid splitting a section header from its content */
section,
h1, h2, h3 {
page-break-after: avoid;
}
/* Honor reduced motion (also applies to print preview) */
*,
*::before,
*::after {
table { page-break-inside: avoid; }
section, h1, h2, h3 { page-break-after: avoid; }
*, *::before, *::after {
animation-duration: 0s !important;
}
}