Skip to main content
Pharos
PHAROSlive stablecoin signals

Design Language

Live UI patterns, typography, spacing, and responsive rules.

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 mdlg 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.

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-top and --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-expanded and --sidebar-width-collapsed
  • Mobile header height: h-14
  • Mobile utility dock: fixed bottom-right dock on <640px with 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
  • 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 to max-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 Summary label, newspaper-style Newsreader title 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 Addresses block shows a one-row, six-item preview on mobile with a Show all toggle; sm+ continues to show the full icon grid
  • A sticky pill-based LongformScrollspyNav under the hero; mobile keeps a swipe cue and min-h-11 tap targets, desktop tightens the pills back down for denser scanning
  • A single Explore Next hub 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 via digestDisplay.className with text-[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:

ElementFontRationale
Headlinesfont-serif + route-local Newsreader usage where neededEditorial authority — magazine headline gravitas
Body copyCourier New italicRaw urgency — telegrams, terminals, raw intel
MetadataCourier New uprightSystematic 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 prose
  • EDITORIAL_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

RoleLive class pattern
Standard page titlemin-w-0 text-3xl sm:text-4xl font-extrabold tracking-tight leading-[1.05]
Shared page title utilitypharos-page-title
Digest article titleNewsreader via digestDisplay.className, text-[clamp(2.2rem,5vw,3.5rem)], font-semibold, leading-[0.92], tracking-[-0.04em]
Homepage digest heroNewsreader, font-semibold, text-[clamp(2.8rem,6vw,5rem)], leading-[0.88], tracking-[-0.045em]
Home logotype labeltext-[1.06rem] font-mono font-semibold uppercase tracking-[0.16em]
Primary section headingleading-none font-semibold
Secondary section headingtext-lg font-semibold or text-lg font-semibold tracking-tight
Table/section kickertext-[12px] sm:text-[11px] font-semibold uppercase tracking-[0.12em] text-muted-foreground
Subsection headingtext-foreground font-medium

Body + Supporting Text

RoleLive class pattern
Standard body copytext-sm text-muted-foreground
Shared lead copypharos-lead
Small metadatatext-xs text-muted-foreground
Shared metadatapharos-meta
Card micro-labelstext-xs uppercase tracking-wide
Footer legal groupflex 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-mono
  • tabular-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-full micro-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-kicker eyebrow + 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-kicker
  • pharos-focus-ring
  • pharos-card-shell
  • pharos-interactive-card
  • pharos-page-title
  • pharos-lead
  • pharos-section-title
  • pharos-meta
  • pharos-control-pill / pharos-control-pill-active
  • pharos-toggle-pill
  • pharos-chart-stage
  • pharos-chart-legend-chip
  • pharos-table-shell
  • pharos-table-toolbar
  • pharos-table-sticky-primary / pharos-table-sticky-metric
  • pharos-panel-header
  • pharos-subtle-band
  • pharos-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 methodology and Version history actions 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-shell is 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, or pb-2
  • Titles: mostly leading-none font-semibold
  • Shared route and chart surfaces increasingly use pharos-panel-header for a restrained header band instead of ad-hoc muted strips

Accent Border Palette (Live)

border-l-[3px] is actively used with:

  • border-l-cyan-500
  • border-l-amber-500
  • border-l-violet-500
  • border-l-sky-500
  • border-l-zinc-500
  • border-l-rose-500
  • border-l-orange-500
  • border-l-emerald-500
  • border-l-teal-500
  • border-l-red-500
  • border-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); both beta and the experimental alias render the Beta label
  • 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 fixed text-*-300/400 tones.
  • 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-shell over a plain rounded border wrapper
  • Toolbars should prefer pharos-table-toolbar with 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-b with 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 outline Previous / Next controls

Mobile Directory Table Handling

  • Toolbar becomes a vertical stack on mobile instead of a cramped inline row
  • Columns and Export CSV keep 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 List mode 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-pointer
  • hover: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-pointer
  • focus-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 ResponsiveContainer renders
  • 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} and tickMargin={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.5 or 2
  • 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-full
  • bg-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

  • 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-none
  • focus-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-center
    • h-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 data
  • Affected: 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)
  • lg:
    • Main horizontal padding increases (lg:px-6)
    • Larger grid splits and extra table columns
  • 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: 56px is 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.