This document reflects the current UI baseline in the codebase and was re-verified on March 24, 2026.
Use this as the visual source of truth for product-facing design decisions. For token definitions (primitive, semantic, component), see design-tokens.md. The bridge layer lives in src/app/globals.css.
Visual Direction
Pharos ships as a dark-first financial dashboard:
- Dense data presentation
- Conservative card-and-table surfaces
- Small, meaningful color accents (risk, status, category)
- Heavy use of monospace for numeric trust and scanability
The default theme class on load is dark, with a user toggle for light mode.
Light mode keeps the same hierarchy as dark mode, but status/accent text is calibrated one step darker to preserve readability on pale surfaces (typical pattern: text-*-700 dark:text-*-400).
Typography carve-out
Newsreader serif is reserved for the Daily Digest editorial surfaces: the /digest/** route and the homepage DailyDigest preview card. The detail-page AiSummary component uses Georgia serif (font-serif) for its AI-authored narrative paragraph — this is a second intentional carve-out. Every other dashboard panel on Pharos — including the homepage Market Snapshot, Core Monitoring band, Research Surfaces band, and all stablecoin-detail cards — uses Geist Sans at all weights. Do not introduce new serif usage outside these two carve-outs; a Vitest invariant in src/lib/__tests__/design-invariants.test.ts currently guards component-level drift under src/components/**, while route-level files still require manual review.
Masthead tagline
The SiteHeader tagline reads Chart your route through the stablecoin market — live peg, safety, liquidity, and dependency signals on every tracked coin. It is exposed from md upward (not lg+-only as before), with line-clamp-2 at md–lg widths and line-clamp-none at lg+. Mobile (<md) keeps the compact wordmark + stat-pill card.
Hero signals rail (stablecoin detail)
On lg+, the detail hero's right column surfaces a four-pill HeroSignalsRail (Safety / Peg / Liquidity / DEWS) that quick-jumps to #report-card and #liquidity. It replaces the duplicated SafetyGradeHero block that used to sit opposite the Safety Score card. Mobile (<lg) continues to render SafetyGradeHero because the Safety Score card is far down scroll on narrow screens.
Breadcrumbs
src/components/breadcrumb.tsx is the shared visual breadcrumb primitive for bespoke deep-route surfaces such as stablecoin detail. Most feature and taxonomy routes use FeaturePageShell, which renders its own Dashboard / current page breadcrumb and emits BreadcrumbJsonLd from either the default breadcrumbName/path pair or an explicit breadcrumbItems override for N-level routes. For new standard feature pages, prefer FeaturePageShell; for bespoke deep routes, use Breadcrumb directly or consolidate the two renderers first.
Global App Shell
Root + Fonts
- Body classes:
geist_*font variables +antialiased - Sans font:
Geist - Mono font:
Geist Mono - Default corner radius token:
--radius: .5rem - Body background adds two subtle radial glow layers via
--page-glow-topand--page-glow-bottom
Layout Structure
Public pages use this shell:
<header className="md:hidden sticky top-[3px] z-50 border-b border-border/80 bg-background" style={{ boxShadow: "var(--elevation-rest)" }} />
<div className="flex min-h-screen">
<aside className="hidden md:flex flex-col fixed top-[3px] left-0 h-[calc(100vh-3px)] border-r border-border/70 bg-card shadow-[0_0_0_1px_oklch(1_0_0_/0.03),0_20px_35px_oklch(0_0_0_/0.2)] z-40 transition-all duration-200" />
<div className="hidden md:block shrink-0 transition-all duration-200 w-[var(--sidebar-width-expanded)]" />
<div className="flex-1 flex flex-col min-w-0">
<main id="main-content" className="pharos-mobile-utility-safe flex-1 container mx-auto px-4 py-6 md:py-7 lg:px-6">
{/* route content */}
</main>
<footer className="border-t border-border/70 py-8 sm:py-10" />
</div>
</div>
Chrome Patterns
- Desktop sidebar widths:
--sidebar-width-expandedand--sidebar-width-collapsed - Mobile header height:
h-14 - Mobile utility dock: fixed bottom-right dock on
<640pxwith shared feedback + scroll-to-top placement; the dock stays hidden until the first scroll so it does not cover top-fold content - Main content and footer reserve bottom safe space via
pharos-mobile-utility-safe+--mobile-utility-safe-offset - Main container padding:
- Mobile:
px-4 - Vertical rhythm:
py-6(md:py-7) - Desktop (
lg):px-6
- Mobile:
- Footer now prioritizes a short list of core routes, keeps category browsing secondary, drops duplicate tagline text beside socials, and lets intro/legal copy breathe across wider lines.
- The footer intro block is not width-capped inside its header row; on larger screens it expands to fill the available column beside the social icons.
Page Shell Variants
Standard Analytics Pages
Most routes use:
- Wrapper:
space-y-6 - Title block:
space-y-2.5 - Breadcrumb:
flex items-center gap-1.5 text-xs text-muted-foreground sm:text-sm - Title row outer layer:
flex max-w-full flex-wrap items-start justify-between gap-x-3 gap-y-3 - Title row inner text/action layer:
flex max-w-4xl flex-wrap items-center gap-x-3 gap-y-2
Longform Pages
- Privacy:
mx-auto w-full space-y-6 max-w-2xl - Methodology:
mx-auto w-full max-w-[76rem] space-y-8 - Digest archive:
mx-auto max-w-4xl - Digest detail shell:
mx-auto max-w-4xl, with editorial body copy constrained tomax-w-[68ch]
Start Here (Special)
The /start/ orientation route keeps the shared breadcrumb/title shell, then shifts into a broader planning-board layout:
Behavioral contract: Start Page
- Wrapper:
mx-auto max-w-6xl space-y-8 - Hero shell: large rounded plotting-board surface with editorial onboarding copy on the left and a route board on the right
- Route board: uniform goal cards in
sm:grid-cols-2 xl:grid-cols-3 - Mobile top fold compresses the hero copy so the first route card stays visible above the fold
- Desktop hero keeps the route board on the right while the left column stacks headline copy and
HeroEscapeHatch - Desktop support content under the headline now stays in a clean vertical stack: CTA row first, then experienced-user note, signal brief, and route-planner note
- Follow-up sections use glossary cards, flattened feature-atlas groups, and shortcut cards instead of one long prose stream
Home Dashboard (Special)
Home keeps a single sr-only page h1 for semantics and uses a non-heading top fold composed of:
Behavioral contract: Homepage
- Tablet + desktop masthead strip:
pharos-card-shell hidden md:flex ... px-5 py-5 - Start Here onboarding callout: large CTA card appears only during a browser's first homepage session and retires once
/start/has been opened, so repeat visitors drop straight into live data - Snapshot shell: PSI-dominant first card + four supporting desktop KPI panels; mobile and tablet collapse to a 2x2 compact tile grid that includes net mint/burn flow
- Snapshot PSI lead card always renders the three compact delta pills (
24h,7d,30d) beside the score/band lockup - Digest preview: broadsheet split with a mono masthead, hairline
Executive Summarylabel, newspaper-styleNewsreadertitle on the left, and the lead paragraph plus CTA rail on the right at desktop
Stablecoin Detail (Special)
Active detail pages keep one server-rendered semantic h1 for crawlers and assistive tech, while the visible identity lives in the client hero:
- Server
h1:sr-only - Client HeroCard mobile
h2:text-2xl font-black tracking-tighter - Client HeroCard desktop
h2:text-3xl font-black tracking-tighter - Section and block titles across the detail route use
text-lg font-semibold tracking-tight - Detail metadata badges that qualify a section title (for example liquidity source coverage) sit inline with the title instead of dropping onto a separate row
- The
Contract Addressesblock shows a one-row, six-item preview on mobile with aShow alltoggle;sm+continues to show the full icon grid - A sticky pill-based
LongformScrollspyNavunder the hero; mobile keeps a swipe cue andmin-h-11tap targets, desktop tightens the pills back down for denser scanning - A single
Explore Nexthub at the end of the page, replacing the older stack of repeated research/compare/related link grids with one consolidated crawlable route cluster
This is intentionally denser than standard feature pages.
Digest Article (Special)
Digest entries use a distinctive "intelligence briefing" editorial aesthetic that deliberately departs from the standard Geist-based UI:
h1: Newsreader display face viadigestDisplay.classNamewithtext-[clamp(2.2rem,5vw,3.5rem)] font-semibold leading-[0.92] tracking-[-0.04em]- Executive summary card ahead of body copy
- Editorial prose constrained to
max-w-[68ch] - Homepage digest preview switches to a split desktop layout so the title block and italic executive-summary paragraph can use the full container width; dedicated digest pages keep the
max-w-[68ch]editorial measure.
Editorial Typography System
The digest feature employs a dual-font hierarchy that evokes newspaper headlines over wire-service dispatches:
| Element | Font | Rationale |
|---|---|---|
| Headlines | font-serif + route-local Newsreader usage where needed | Editorial authority — magazine headline gravitas |
| Body copy | Courier New italic | Raw urgency — telegrams, terminals, raw intel |
| Metadata | Courier New upright | Systematic precision — timestamps, edition numbers |
This pairing creates a "broadsheet newspaper" aesthetic that signals both authority and real-time urgency. It is one of two intentional non-Geist text treatments in Pharos, alongside the stablecoin-detail AiSummary Georgia serif paragraph.
Implementation: Import styles from @/lib/digest:
EDITORIAL_BODY_STYLE— Courier italic for proseEDITORIAL_META_STYLE— Courier upright for labels
Cemetery (Special)
The Stablecoin Cemetery (/cemetery/) employs a unique memorial aesthetic that is intentionally divergent from standard Pharos UI patterns:
- Tombstone visualizations: Custom SVG-based tombstones with varying shapes (arch, hammer, cross), sizes (by peak market cap), and weathering effects (by age)
- Theme-aware memorial palette: Uses bespoke stone/zinc accents and cause colors for the memorial atmosphere, while tombstone SVGs and cards still adapt through semantic CSS variables and light/dark Tailwind classes
- Cause-of-death color system: Algorithmic failure (red), counterparty failure (amber), liquidity drain (orange), regulatory (blue), abandoned (zinc)
- Interactive memorial: "Press F to pay respects" with persistent flower accumulation
This is a one-off artistic treatment — the patterns are not intended for reuse on other pages. The bespoke memorial colors and components serve the specific narrative of memorializing failed stablecoins.
Typography
Heading Scale
| Role | Live class pattern |
|---|---|
| Standard page title | min-w-0 text-3xl sm:text-4xl font-extrabold tracking-tight leading-[1.05] |
| Shared page title utility | pharos-page-title |
| Digest article title | Newsreader via digestDisplay.className, text-[clamp(2.2rem,5vw,3.5rem)], font-semibold, leading-[0.92], tracking-[-0.04em] |
| Homepage digest hero | Newsreader, font-semibold, text-[clamp(2.8rem,6vw,5rem)], leading-[0.88], tracking-[-0.045em] |
| Home logotype label | text-[1.06rem] font-mono font-semibold uppercase tracking-[0.16em] |
| Primary section heading | leading-none font-semibold |
| Secondary section heading | text-lg font-semibold or text-lg font-semibold tracking-tight |
| Table/section kicker | text-[12px] sm:text-[11px] font-semibold uppercase tracking-[0.12em] text-muted-foreground |
| Subsection heading | text-foreground font-medium |
Body + Supporting Text
| Role | Live class pattern |
|---|---|
| Standard body copy | text-sm text-muted-foreground |
| Shared lead copy | pharos-lead |
| Small metadata | text-xs text-muted-foreground |
| Shared metadata | pharos-meta |
| Card micro-labels | text-xs uppercase tracking-wide |
| Footer legal group | flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-muted-foreground |
Numeric Language
Numbers are consistently mono/tabular where precision matters:
font-monotabular-nums
Spacing and Layout Rhythm
Common Vertical Rhythm
- Section rhythm:
space-y-6 - Header block rhythm:
space-y-2.5 - Longform rhythm:
space-y-8 - Card prose rhythm:
space-y-6 text-sm text-muted-foreground leading-relaxed
Common Grids
- KPI grid (dense analytics):
grid grid-cols-2 gap-3 sm:gap-5 lg:grid-cols-5 - Home feature grid:
grid grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-5 - Home snapshot desktop partition:
hidden lg:grid grid-cols-[minmax(0,1.1fr)_repeat(4,minmax(0,0.92fr))] divide-x divide-border/30
Chip/Pill Layout
- Chips are frequently wrapped in
flex flex-wrap gap-2 - Category links and peg links prioritize
rounded-fullmicro-surfaces.
Onboarding / Access Surfaces
First-run compare, portfolio, and gated status states now share a structured onboarding surface:
- Large rounded shell with dark gradient backdrop
pharos-kickereyebrow + one decisive title- 3 step explainer cards
- CTA row using rounded-full buttons
- Preview panel shell on the right at desktop, stacked on mobile
- Optional footnote/support panel at the bottom of the text column
The dedicated /start/ route extends the same language into a full-page onboarding pattern:
- large hero shell with route-selection cards instead of a single preview panel
- compact fact blocks embedded in the copy column
- glossary cards beneath the hero
- flattened feature-atlas groups plus shortcut cards for optional progressive discovery
Shared Utility Classes
Live production now leans on a broader shared utility layer for finish-level consistency:
pharos-kickerpharos-focus-ringpharos-card-shellpharos-interactive-cardpharos-page-titlepharos-leadpharos-section-titlepharos-metapharos-control-pill/pharos-control-pill-activepharos-toggle-pillpharos-chart-stagepharos-chart-legend-chippharos-table-shellpharos-table-toolbarpharos-table-sticky-primary/pharos-table-sticky-metricpharos-panel-headerpharos-subtle-bandpharos-empty-note
Current high-use areas:
- Homepage snapshot and explore cards
- Peg filter pills
- CTA links with custom focus treatment
- chart legends, chart stages, and comparison controls
- stablecoin/comparison table wrappers and toolbars
Contextual Explainability
Computed metrics now use a shared contextual-methodology pattern instead of relying only on page-level intros or /methodology as a separate destination.
Current pattern:
- compact help trigger attached directly to the metric label
- desktop behavior: rich tooltip with short definition + methodology links
- mobile behavior: bottom sheet with the same content
- score-card footers may add
View methodologyandVersion historyactions when the surface is interpretation-heavy
Use this on:
- composite scores (
Safety Score,Liquidity Score,PYS,DEWS) - baseline-relative or Pharos-native signals (
Pressure Shift vs 30D,Bank Run Gauge) - opaque sub-dimensions where the label alone is insufficient (
Resilience,Dependency Risk)
Do not use this on every metric indiscriminately. The trigger is reserved for values where local interpretation meaningfully improves user decision-making.
Cards
Base Card Primitive
Default card composition in production:
data-slot="card"bg-card text-card-foreground flex flex-col gap-4 rounded-xl border py-4 shadow-sm- card surfaces now inherit a subtle shell gradient plus top-left highlight through the global token bridge instead of relying on flat fills alone
pharos-card-shellis the promoted authored-surface wrapper for major route modules, tables, and feature cards
Card Header + Title
- Header:
@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-4 - Tight variants add
pb-1,pb-1.5, orpb-2 - Titles: mostly
leading-none font-semibold - Shared route and chart surfaces increasingly use
pharos-panel-headerfor a restrained header band instead of ad-hoc muted strips
Accent Border Palette (Live)
border-l-[3px] is actively used with:
border-l-cyan-500border-l-amber-500border-l-violet-500border-l-sky-500border-l-zinc-500border-l-rose-500border-l-orange-500border-l-emerald-500border-l-teal-500border-l-red-500border-l-blue-500
Navigation active state uses border-l-frost-blue.
Interactive Card Pattern
pharos-interactive-card is the richer hover-lift utility used on the about-page feature grid. Homepage callouts currently stay on lighter pharos-card-shell variants without the extra interactive-card class.
className =
"pharos-card-shell pharos-focus-ring pharos-interactive-card group flex flex-col gap-2 border-l-[3px] bg-gradient-to-b from-background/40 to-transparent p-4";
Logo Containers
Tracked token logos now render inside a shared neutral container:
rounded-full border border-border/60 bg-background/80- subtle inset highlight
- image shrunk slightly inside the wrapper so transparent/low-quality upstream assets do not collapse into the page background
Badges and Chips
Feature Status Badges
- Mature: emerald badge (
bg-emerald-500/15 ... border-emerald-500/30) - Beta: amber badge (
bg-amber-500/15 ... border-amber-500/30); bothbetaand theexperimentalalias render theBetalabel - Testing in Prod: orange badge (
bg-orange-500/15 ... border-orange-500/30) - Status text should follow light/dark pairing (
text-*-700 dark:text-*-400) instead of fixedtext-*-300/400tones. - Badge copy is now terse (
Mature,Beta,Testing in Prod) instead of repeating “Feature Status”.
Version Badge
Secondary version pill:
bg-background/35 text-muted-foreground border-border/60
Micro Chips
Common chip form:
inline-flex items-center rounded-full border bg-background px-2.5 py-1 text-xs font-medium hover:bg-accent transition-colors
Control Pills
The preferred finish-level control language is now the shared pill system:
- base:
pharos-control-pill - selected:
pharos-control-pill pharos-control-pill-active - used on time-range controls, density toggles, lens pills, and lightweight route context summaries
- pills should feel dense and precise, not marketing-chip playful
Tables
Base Table Styling
- Table:
w-full caption-bottom text-sm - Major table surfaces should prefer
pharos-table-shellover a plain rounded border wrapper - Toolbars should prefer
pharos-table-toolbarwith a brief explanatory line rather than a bare row of buttons - Row:
hover:bg-muted/40 data-[state=selected]:bg-muted border-b transition-colors - Table rows now also take a subtle left-edge accent and a small horizontal nudge on hover; risk rows preserve their own semantic border color on hover
Header Variants
- Standard header:
[&_tr]:border-bwith a theme-aware header band and token-driven shadow - Sticky directory header (peg pages): sticky top header with restrained blur and
--table-header-shadow - Stablecoin detail history tables (depeg + mint/burn) use a rounded bordered shell (
rounded-xl border overflow-hidden) with the muted header treatment and a footer row pairing mono range copy with outlinePrevious/Nextcontrols
Mobile Directory Table Handling
- Toolbar becomes a vertical stack on mobile instead of a cramped inline row
ColumnsandExport CSVkeep large tap targets on mobile; density and range controls also stay pill-based instead of collapsing into tiny tabs- Density controls now include a true
Listmode for ticker-first scanning; in that mode the stablecoin table suppresses the expanded coin name and keeps only the ticker lockup - Table keeps a deliberate horizontal-scroll affordance via helper copy and
min-w-[820px] - Bottom spacing is preserved so the mobile utility dock never sits on the last visible rows
Sortable Head Pattern
Sortable heads consistently include:
cursor-pointerhover:bg-muted/50 transition-colors
Numeric columns remain right-aligned (text-right) and collapse progressively by breakpoint (hidden sm:table-cell, hidden md:table-cell, etc.).
Clickable Rows
Interactive rows use:
group cursor-pointerfocus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none
Charts
Live Chart Container Pattern
- Height:
h-[250px] sm:h-[350px] - Recharts container keeps
min-width: 0; min-height: 0 - Chart-heavy home modules now reserve height through matching skeletons or client-ready mount guards before
ResponsiveContainerrenders - Premium chart framing now uses
pharos-chart-stage: a dedicated bordered stage inside the card, rather than letting charts float directly on the card background - Legends should prefer compact chips (
pharos-chart-legend-chip) when the chart needs persistent series context outside the tooltip
Axis + Grid (Observed)
From production rendered charts:
- Tick text:
font-size: 12,font-family: var(--font-mono, monospace),fill: var(--color-muted-foreground) - X axis keeps extra breathing room through
tickMargin={10} - Y axis defaults to
width={64}andtickMargin={8}for cleaner number alignment - Grid lines:
stroke="var(--color-border)",strokeDasharray="2 6", verticals off by default
Area Chart Styling (Observed)
- Areas use gradient fills (e.g.
fill="url(#psiScoreGradient)") - Stroke widths are typically
1.5or2 - Tooltips should use the shared elevated card treatment (
PharosChartTooltip) with uppercase label treatment and mono values
Loading Fallbacks
Common chart skeletons:
rounded-lg bg-muted/30 animate-pulse relative overflow-hidden h-[250px] sm:h-[350px] w-fullbg-accent animate-pulse h-[250px] sm:h-[350px] w-full rounded-xl- Blacklist hero chart uses
h-[220px] sm:h-[280px] - Yield scatter plot uses
h-[240px] sm:h-[340px]inside a bordered chart stage
Interaction and State Patterns
Navigation Active vs Inactive
- Active sidebar item:
border-l-[3px] border-l-frost-blue bg-muted/60 text-foreground shadow-sm
- Inactive sidebar item:
border-l-[3px] border-l-transparent text-muted-foreground hover:border-l-border/80 hover:bg-muted/45 hover:text-foreground
Focus Treatment
Two dominant focus patterns:
focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-nonefocus-visible:ring-[3px] focus-visible:ring-ring/50
Loading States
- Skeletons are the default loading surface (
data-slot="skeleton"+animate-pulse) - Page-level loader currently appears as:
flex min-h-[40vh] items-center justify-centerh-10 w-10 rounded-full bg-frost-blue/30 animate-pharos-pulse
Live/Event Indicator
Depeg live indicator uses animated ping:
animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75
Data Availability Banner
When data streams are missing:
rounded-md border px-4 py-2.5 text-sm border-border/60 bg-muted/40 text-muted-foreground
The current pattern is a titled trust banner with dataset-specific copy, for example:
Waiting for initial dataAffected: report cards.Last successful update: Mar 24, 1:01 PM GMT+1.
Responsive Behavior
Breakpoint Behavior in Production
sm:- Compacts/expands table columns
- Converts details/nav patterns
md:- Sidebar becomes active (
md:flex) - Mobile header hides (
md:hidden)
- Sidebar becomes active (
lg:- Main horizontal padding increases (
lg:px-6) - Larger grid splits and extra table columns
- Main horizontal padding increases (
xl:- Additional dense table columns
- Home KPI grid keeps the wide five-panel snapshot module intact
Mobile-Specific UX
- Category browse collapses into
details(sm:hidden) --table-header-top: 56pxis set on mobile for sticky header offset alignment- Bottom utility controls are consolidated into one dock on mobile instead of separate floating widgets
Accessibility Baseline
Live app-wide patterns:
- Skip link present on every page:
sr-only focus:not-sr-only ... - Breadcrumb navigation on content routes
- Focus-visible rings on sidebar links, buttons, table rows, and chips
- Keyboard-ready clickable rows on interactive tables
- Color is reinforced with structure and iconography for key status states
Maintenance Rule
If a deployed class pattern changes in production, update this document immediately after release. This file is intended to describe what users currently see, not aspirational or historical styles.