Skip to content

Design System

Ghost’s design system follows one core principle: containers get out of the way; data speaks. Every visual decision is about reducing noise and making the information stand out — cards are nearly invisible, borders are barely there, and the only things that catch your eye are the actual data: HTTP methods, status codes, timing numbers, and content. The design avoids decoration for its own sake — no glass effects, no heavy shadows, no gradients on containers. Premium means restraint and precision, not more visual weight.

The entire design system is defined in a single CSS file using Tailwind v4’s @theme directive (no tailwind.config.ts). Every color, spacing value, font, and animation is a CSS custom property, making themes as simple as overriding these variables.

Ghost uses two fonts, both bundled as woff2 files (no CDN, no external requests):

RoleFontWeightsFormatUsage
UISpace Grotesk300–700 (variable)woff2, 2 files (Latin + Latin Extended)All interface text — labels, buttons, headings, body copy. Chosen for its clean geometric shapes with just enough personality to feel premium without being distracting.
CodeJetBrains Mono400–500 (variable)woff2, 2 files (Latin + Latin Extended)All code display — request/response bodies, header values, addon scripts, agent tool outputs. Chosen for its clear character distinction (l vs 1 vs I) and comfortable reading at small sizes.

Both use font-display: swap — the browser shows fallback system fonts immediately and swaps in the custom fonts once they load, so there’s no invisible text during page load.

CSS variables:

  • --font-sans: 'Space Grotesk', system-ui, sans-serif
  • --font-mono: 'JetBrains Mono', monospace

A 12-step neutral scale with a subtle blue-purple undertone (not pure gray). Used for all surfaces, text, and borders. In dark mode, lower numbers are darker; in light mode, the scale inverts completely.

TokenDark ValueLight ValueTypical Usage
ghost-950#09090b#fafafbApp background (deepest surface)
ghost-900#111118#f1f1f5Card / elevated surface background
ghost-850#16161f#e8e8efPopover / dropdown background
ghost-800#1c1c28#dddde8Input backgrounds, borders, panel separators
ghost-700#2a2a3a#b8b8ccBorders, dividers, scrollbar thumb
ghost-600#6a6a88#8585a3Placeholder text, disabled states
ghost-500#8585a3#6a6a88Muted / secondary text
ghost-400#a0a0b8#555570Labels, secondary text
ghost-300#c0c0d0#3a3a50Body text
ghost-200#d8d8e4#252535Emphasized text, hover states
ghost-100#ededf2#16161fHigh-emphasis text, foreground
ghost-50#fafafb#09090bMaximum contrast text

Four accent colors derived from the Ghost logo gradient (cyan → blue → purple → pink). Each has three variants: standard, dim (for backgrounds and muted states), and bright (for hover/active states).

ColorDarkDark DimDark BrightLightLight DimLight Bright
Cyan#22d3ee#06b6d4#67e8f9#0891b2#0e7490#06b6d4
Blue#0ea5e9#0284c7#38bdf8#0284c7#0369a1#0ea5e9
Purple#8b5cf6#7c3aed#a78bfa#7c3aed#6d28d9#8b5cf6
Pink#ec4899#db2777#f472b6#db2777#be185d#ec4899

Light theme colors are shifted deeper for better contrast on white backgrounds.

TokenDarkLightUsage
success#22c55e#16a34aPositive states, 2xx status codes
warning#eab308#ca8a04Caution states, 4xx status codes
error#ef4444#dc2626Error states, 5xx status codes
info#0ea5e9#0284c7Information, 3xx status codes

Each HTTP method has a dedicated color, used consistently across the traffic list, inspector, composer, and comparison views:

MethodDark ColorLight Color
GET#22c55e (green)#16a34a
POST#0ea5e9 (blue)#0284c7
PUT#eab308 (yellow)#ca8a04
PATCH#8b5cf6 (purple)#7c3aed
DELETE#ef4444 (red)#dc2626
OPTIONS#6b7280 (gray)#4b5563
HEAD#6b7280 (gray)#4b5563

Method badges use 10% opacity backgrounds with full-color text (e.g., bg-method-get/15 text-method-get), so they’re visible but don’t dominate the row.

RangeText ClassBadge Class
1xx (Informational)text-ghost-400bg-ghost-600/10 text-ghost-400
2xx (Success)text-successbg-success/10 text-success
3xx (Redirect)text-infobg-info/10 text-info
4xx (Client Error)text-warningbg-warning/10 text-warning
5xx (Server Error)text-errorbg-error/10 text-error

The signature Ghost gradient appears throughout the app — in the logo, accent borders, status bar accent line, and hover effects.

Dark theme:

--gradient-ghost: linear-gradient(135deg, #22d3ee, #0ea5e9, #8b5cf6, #ec4899);
--gradient-ghost-subtle: linear-gradient(135deg, #06b6d4, #7c3aed);
--gradient-ghost-horizontal: linear-gradient(90deg, #22d3ee, #8b5cf6, #ec4899);

Light theme (deeper stops for contrast):

--gradient-ghost: linear-gradient(135deg, #0891b2, #0284c7, #7c3aed, #db2777);
--gradient-ghost-subtle: linear-gradient(135deg, #0e7490, #6d28d9);
--gradient-ghost-horizontal: linear-gradient(90deg, #0891b2, #7c3aed, #db2777);

Gradient utility classes:

  • .gradient-text — applies the horizontal gradient as text color via background-clip: text
  • .gradient-border-l — 2px left accent border via ::before pseudo-element
  • .gradient-border-b — 2px bottom accent border via ::after pseudo-element
  • .gradient-border-t — 2px top accent border via ::before pseudo-element (with overflow hidden)
  • .gradient-glow — box-shadow glow using cyan and purple at low opacity

21 restyled shadcn/ui components, all remapped to Ghost design tokens. Every component that comes from shadcn/ui has been restyled — the default shadcn appearance is not used anywhere.

VariantAppearance
defaultCyan background with dark text (primary action)
destructiveRed background (dangerous action)
outlineGhost-700 border, transparent background
secondaryGhost-800 background
ghostFully transparent, shows on hover
linkText-only, underlined
SizeHeight
default36px (h-9)
xs28px (h-7)
sm32px (h-8)
lg40px (h-10)
icon36×36px
icon-xs24×24px
icon-sm32×32px
icon-lg40×40px
VariantAppearance
defaultGhost-800 background with ghost-700 border
ghostTransparent background, no border, subtle hover

Sizes: default (h-9), sm (h-8), lg (h-10). Focus state: cyan border at 50% opacity with cyan ring at 20%.

Uses Radix primitives. Trigger: ghost-800 background, ghost-700 border, h-9. Content dropdown: ghost-850 background, ghost-700 border, max height 320px. Selected item indicator: cyan text color.

ComponentKey Styling Details
TextareaGhost-800 background, ghost-700 border, 80px minimum height, vertical resize enabled
Checkbox16×16px, rounded 4px corners, ghost-800 background. Checked: cyan background with dark check icon. Focus: 3px cyan ring.
SwitchTwo sizes: default (h-1.15rem, w-8) and sm (h-3.5, w-6). Checked: primary background.
BadgeRounded full (pill shape). Variants: default, secondary, destructive, outline, ghost, link.
CardGhost-900 background (dark) / white (light), ghost-700 border, rounded-xl, subtle shadow. Padding: 24px vertical, 24px horizontal content.
DialogOverlay: black at 60% opacity with backdrop blur. Content: ghost-900 background, ghost-700 border, rounded-xl, deep shadow. Max width: 32rem (sm). Close button optional.
DropdownMenuGhost-850 background, ghost-700 border, rounded-lg, deep shadow. Item height: 36px. Focus: ghost-800 background. Destructive items: error color with 10% error background on focus.
ContextMenuSame styling as DropdownMenu (shared design language for all menus).
MenubarTrigger text: 11px, ghost-400. Hover/active: ghost-800 background. Content: ghost-900, rounded-md.
TooltipInverted colors (foreground as background, background as foreground). Zero delay by default. Arrow matches foreground.
AlertDefault: card styling. Destructive: error text with card background.
Progress8px height bar, rounded full. Track: primary at 20%. Indicator: primary solid.
SeparatorBorder color. Horizontal: 1px tall, full width. Vertical: full height, 1px wide.
Label12px uppercase with wider letter spacing, ghost-400 color.
FormFieldWrapper with optional label, hint (ghost-500), and error (error color, 12px).
CollapsibleThin wrapper around Radix, no custom styling.
ScrollAreaScrollbar thumb: ghost-700, rounded full.
TagInputComplex host management input with search, bulk add, collapse/expand. Container: ghost-700 border, ghost-800 background. Tags: ghost-700 background, 11px text, individual remove buttons.

A custom Ghost component (not shadcn) that wraps Phosphor icons with built-in tooltips:

  • ghost variant: ghost-500 text, ghost-800 background on hover
  • subtle variant: ghost-400 text, ghost-850 background on hover
  • Default icon size: 14×14px
  • Tooltip: ghost-800 background, ghost-200 text, ghost-700 border, 12px text

Ghost supports two themes — dark and light — implemented via a data-theme attribute on the root HTML element. The theme is toggled via a sun/moon icon in the command bar or the command palette, and persists to localStorage.

The default theme. The deepest background (ghost-950) is #09090b — nearly pure black with a hint of blue. Cards (ghost-900) are #111118 — barely distinguishable from the background, making them feel like windows into the content rather than containers around it. Text ranges from ghost-600 (placeholder, 40% legible) to ghost-50 (maximum emphasis, nearly white).

The ghost logo uses mix-blend-mode: screen in dark mode, which makes it glow against dark backgrounds.

Activated by setting data-theme="light" on the document root. The light theme overrides every single color token in the system:

  • Grayscale inverts completelyghost-950 becomes the lightest color (#fafafb), ghost-50 becomes the darkest (#09090b)
  • Brand colors shift deeper — cyan goes from #22d3ee to #0891b2, purple from #8b5cf6 to #7c3aed, ensuring adequate contrast on white backgrounds
  • Semantic colors shift deeper — success from #22c55e to #16a34a, etc.
  • Method colors shift deeper — same pattern, all darkened for legibility
  • All three gradient definitions regenerate with deeper stops
  • shadcn semantic tokens remap — card background becomes white, popover becomes white, borders use light ghost values
  • Logo blend mode changes to multiply (the inverse of screen, works on light backgrounds)

Both themes are equal quality — Ghost is not “dark-first with a light afterthought.” The light theme has its own tuned color palette, not just inverted dark values.

13 keyframe animations, all designed to be subtle and functional rather than decorative:

AnimationDurationEasingPurpose
flow-flash1.2sease-outCyan glow on newly arrived flow rows, fades to transparent
skeleton-shimmer1.5sease-in-out, infiniteLoading skeleton pulse, opacity 6% → 12%
view-enter200msease-outView entry: fade in + slide up 4px
panel-reveal250mscubic-bezier(0.34, 1.56, 0.64, 1)Panel entry: fade in + slide up 8px, springy overshoot
sort-bounce200msease-outColumn sort arrow: 1px bounce up
ghost-breatheLogo ambient pulse: opacity 20% → 35%, scale 1 → 1.04
status-pulse2sease-in-out, infiniteStatus dot expanding ring
tap-pulse400msease-out, forwardsDevice tap indicator: scale 0.5 → 2.5, opacity 1 → 0
cursor-blink600msease-in-out, infiniteChat streaming cursor: opacity 30% → 100%
overlay-backdrop-in150msease-outModal backdrop fade in
overlay-panel-in200mscubic-bezier(0.34, 1.56, 0.64, 1)Modal panel scale 97% → 100%, springy
slide-over-in200msease-outSlide-over panel entrance from right
slide-over-out200msease-inSlide-over panel exit to right

The .stagger-enter class creates a cascading entrance effect for lists and grids. Up to 6 children are staggered with 50ms increments (0, 50, 100, 150, 200, 250ms delay), each using the view-enter animation at 250ms.

Sidebar navigation buttons (.sidebar-nav-btn) have a subtle scale interaction:

  • Hover: scale 1.05 (100ms ease)
  • Active (clicking): scale 0.95
  • Active state indicator: a 2px-tall gradient bar (::after pseudo-element) that scales from 0 to full width via a 150ms CSS transition

All animations respect the prefers-reduced-motion media query. When the user has enabled reduced motion in their operating system preferences, all animation durations are forced to 0.01ms (effectively instant) and limited to a single iteration.

The draggable dividers between resizable panels have gradient feedback:

  • Horizontal separator: 8px tall, ghost-800 background, row-resize cursor. On hover/active: gradient from cyan → purple → pink (90° horizontal)
  • Vertical separator: 8px wide, ghost-800 background, col-resize cursor. On hover/active: gradient from cyan → purple → pink (180° vertical)

All interactive elements use a visible cyan outline on keyboard focus:

:focus-visible {
outline: 2px solid var(--color-cyan);
outline-offset: 2px;
border-radius: 2px;
}

Mouse clicks do NOT show the outline (:focus:not(:focus-visible) removes it), so keyboard users get clear focus indicators without cluttering the UI for mouse users.

All button and [role="button"] elements automatically get cursor: pointer. Disabled elements and [aria-disabled="true"] get cursor: not-allowed.

Selected text uses a cyan highlight at 30% opacity via color-mix(), maintaining brand consistency even in text selection.

All panels, lists, menus, and interactive elements support full keyboard navigation. The command palette (Ctrl+K) provides keyboard-driven access to all major actions.

  • Tailwind CSS v4 via @tailwindcss/vite plugin (no tailwind.config.ts or postcss.config.ts)
  • All design tokens defined via CSS @theme directive in index.css
  • Animation library: tw-animate-css (imported alongside Tailwind)
  • Path alias: @./src
  • Dev server: port 5173, proxies /api to localhost:9090, /ws as WebSocket
  • Source maps: hidden (for Sentry, not served to browser)