Radar's design system for AI coding agents. Read this before writing or modifying frontend code.
Source files: packages/k8s-ui/src/theme/variables.css (tokens), tailwind-theme.css (Tailwind mapping), components.css (shared classes), Badge.tsx (badge component + color definitions).
Cool, professional, data-dense. A barely-blue-tinted neutral palette — not generic gray, not flashy. Light mode defaults to cool off-white surfaces; dark mode to deep navy-black. The brand accent is a refined mid-blue (#4A7CC9 light / #6A9FE0 dark) — saturated enough to stand out, restrained enough for a tool you stare at all day. Information density is high (K8s dashboards), so the palette relies on subtle background layering and muted borders rather than heavy visual chrome.
Key characteristics:
- Light/dark via
light-dark()CSS function +.darkclass toggle - Cool blue-gray tint in backgrounds and borders (not pure gray)
- Single brand accent color — no secondary brand colors
- DM Sans Variable for UI, DM Mono for code/terminal
- Rounder than Tailwind defaults (6px base, 14px cards/modals)
- Status communicated via emerald/amber/red/sky semantic colors
| Token | CSS Variable | Light | Dark | Use for |
|---|---|---|---|---|
bg-theme-base |
--bg-base |
#F0F3F9 |
#0B0F1E |
Page background, header, toolbar chrome |
bg-theme-surface |
--bg-surface |
#F8FBFE |
#14192A |
Sidebars, cards, panels, drawers |
bg-theme-elevated |
--bg-elevated |
#E6ECF5 |
#1E2338 |
Inputs, count badges, dropdowns |
bg-theme-hover |
--bg-hover |
#DDE5F2 |
#282E48 |
Hover states on interactive elements |
bg-theme-active |
--bg-active |
#D3DCEC |
#1C2840 |
Active/pressed states |
| Token | CSS Variable | Light | Dark | Use for |
|---|---|---|---|---|
text-theme-text-primary |
--text-primary |
#1A1C20 |
#F5F6F8 |
Headings, primary content |
text-theme-text-secondary |
--text-secondary |
#555860 |
#D8DAE0 |
Body text, descriptions |
text-theme-text-tertiary |
--text-tertiary |
#6B7080 |
#B0B5C0 |
Placeholders, metadata, timestamps |
text-theme-text-disabled |
--text-disabled |
#A0A4AC |
#6A7080 |
Disabled states |
| Token | CSS Variable | Light | Dark | Use for |
|---|---|---|---|---|
border-theme-border |
--border-default |
#D8E0EE |
#222840 |
Standard borders (cards, inputs, dividers) |
border-theme-border-light |
--border-light |
#CDD6E8 |
#2C324A |
Slightly heavier borders |
border-theme-border-subtle |
--border-subtle |
rgba(74,100,180,0.07) |
rgba(255,255,255,0.04) |
Very subtle separators |
| Token | CSS Variable | Light | Dark | Use for |
|---|---|---|---|---|
bg-accent / text-accent |
--accent |
#4A7CC9 |
#6A9FE0 |
Primary actions, active states, links |
bg-accent-light |
--accent-light |
#3B66A8 |
#8BB5EA |
Hover on accent elements |
bg-accent-muted |
--accent-muted |
rgba(74,124,201,0.12) |
rgba(106,159,224,0.18) |
Subtle accent backgrounds |
text-accent-text |
--accent-text |
#3B66A8 |
#8BB5EA |
Accent-colored text |
Full Skyhook brand scale: --color-skyhook-50 (#EBF2FA) through --color-skyhook-900 (#1F365C), centered on --color-skyhook (#5088CC). Available as bg-skyhook-* / text-skyhook-* utilities.
| Color | Variable | Hex | Use for |
|---|---|---|---|
| Success | --color-success |
#22C55E |
Healthy, running, active, ready |
| Warning | --color-warning |
#F59E0B |
Degraded, pending, suspended |
| Error | --color-error |
#EF4444 |
Unhealthy, failed, crashloop |
| Info | --color-info |
#3B82F6 |
Completed, informational |
| Token | CSS Variable | Use for |
|---|---|---|
.selection |
--selection-bg |
Selected rows, items |
.selection-strong |
--selection-bg-strong |
Strongly selected items |
.selection-text |
--selection-text |
Text in selected items |
.selection-ring |
--selection-border |
Box-shadow ring on selected items |
| Token | CSS Variable | Use for |
|---|---|---|
shadow-theme-sm |
--shadow-sm |
Subtle elevation (buttons, small elements) |
shadow-theme-md |
--shadow-md |
Medium elevation (cards, dropdowns) |
shadow-theme-lg |
--shadow-lg |
High elevation (modals, popovers) |
shadow-drawer |
--shadow-drawer |
Slide-out drawers |
shadow-glow-brand-sm |
--glow-brand-sm |
Subtle brand glow |
shadow-glow-brand-md |
--glow-brand-md |
Medium brand glow |
Dark mode overrides shadows with heavier values + subtle white inset borders.
| Role | Family | Tailwind class |
|---|---|---|
| UI text | DM Sans Variable | font-sans (default) |
| Code, terminal, monospace | DM Mono | font-mono |
Standard Tailwind type scale. No custom sizes or tracking. Use Tailwind utilities (text-sm, text-base, text-lg, font-medium, etc.) as normal.
| Class | Use for |
|---|---|
.btn-brand |
Primary CTAs — brand-colored bg, white text, 10px radius |
.btn-brand-muted |
Secondary brand actions — dimmed brand bg, white text |
.btn-brand-toggle |
Toggle buttons — 50% brand bg, primary text |
Hover/disabled states are built into the classes. For non-brand buttons, use shadcn/ui <Button> variants.
Use the <Badge> component from @skyhook-io/k8s-ui — never hand-write badge
color strings (bg-<color>-NN/NN text-<color>-NN …). Those bypass the theme
(they're dark-tuned and wash out in light mode) and, worse, overload hues across
intents — a green bg-green-500/20 "HTTPS" chip becomes indistinguishable from a
green "success" chip, so retuning "success" silently recolors every HTTPS tag.
Pick the prop by what the badge MEANS, not by the color you want (tokens are named by intent so a future retune moves exactly the right ones):
| The badge represents… | Use | Example |
|---|---|---|
| a status / outcome | severity= (success/warning/alert/error/info/neutral) |
<Badge severity="error">Failed</Badge> |
| resource health | severity= via mapHealthToTone / getHealthBadgeColor |
health tone |
| a K8s/CRD resource kind | kind= |
<Badge kind="Middleware">Middleware</Badge> |
| a transport protocol/scheme | protocol= (http/https/tls/tcp/udp/grpc/h2) |
<Badge protocol="HTTPS">HTTPS</Badge> |
| a neutral attention/FYI marker (cross-namespace, wildcard, default, immutable) | tone="note" |
<Badge tone="note">cross-namespace</Badge> |
| local categorical distinction (rw/ro, spot/on-demand, control-plane/worker) — hue has no global meaning, just "tell siblings apart" | tone="accent1"/"accent2"/"accent3" |
<Badge tone="accent1">rw</Badge> |
| a structural data fragment (port, path, host, weight, name, count) | tone="structural" |
<Badge tone="structural" size="sm">:8080</Badge> |
| genuinely one-off (last resort) | colorClass= |
<Badge colorClass="…">Custom</Badge> |
<Badge severity="success">Running</Badge>
<Badge kind="Pod">my-pod</Badge>
<Badge protocol="TCP">TCP</Badge> {/* protocol ≠ severity: TCP is indigo, not orange/success */}
<Badge tone="note">wildcard</Badge>
<Badge tone="structural" size="sm">:443</Badge>If you reach for colorClass, first ask whether you're inventing an intent that
should be a named token here instead — if two places want "the same accent",
that's a token, not two literals. A ratchet test
(badge-no-handrolled.test.tsx) fails CI if a renderer adds a hand-rolled
chip; the existing baseline shrinks over time, never grows.
For programmatic color lookup (e.g., dynamic table cells):
getKindColorClass(kind)— returns Tailwind classes for a K8s resource kindgetSeverityBadge(severity)— returns classes for a severity levelgetResourceStatusColor(status)— maps K8s status strings ("Running", "Pending", "Failed") to badge classesgetHealthBadgeColor(health)— maps health states to badge classesgetHelmStatusColor(status)— maps Helm release statuses to badge classes
All from @skyhook-io/k8s-ui/utils/badge-colors.
Use CSS classes from components.css for status cells in table rows:
.status-healthy— emerald.status-degraded— amber.status-unhealthy— red.status-neutral— sky.status-unknown— gray (uses theme variables).status-violet,.status-purple,.status-orange,.status-cyan— special cases
| Class | Use for |
|---|---|
.card-inner |
Nested containers in drawers/renderers — base bg, subtle border, 6px radius, 8px padding |
.card-inner-lg |
Larger nested containers — 10px radius, 12px padding |
.dialog |
Modal/dialog containers — surface bg, default border, 14px radius, large shadow |
| Class | Use for |
|---|---|
.table-divide-subtle |
Ultra-subtle dividers between table rows (apply to parent) |
.border-b-subtle |
Bottom border on individual elements |
.border-r-subtle |
Right border |
.border-t-subtle |
Top border |
Standard Tailwind spacing scale. No custom base unit.
| Tailwind | Value | Use for |
|---|---|---|
rounded |
6px | Badges, inline elements |
rounded-md |
8px | Inputs, small containers |
rounded-lg |
10px | Buttons, dropdowns |
rounded-xl |
14px | Cards, modals, dialogs |
| Name | Width |
|---|---|
sm |
900px |
md |
1100px |
lg |
1280px |
xl |
1536px |
Wider than Tailwind defaults — Radar is a desktop-first tool.
| Level | Treatment | Use for |
|---|---|---|
| Base | bg-theme-base, no shadow |
Page background |
| Surface | bg-theme-surface, optional shadow-theme-sm |
Panels, sidebars, cards |
| Elevated | bg-theme-elevated, shadow-theme-md |
Dropdowns, popovers, inputs |
| Floating | bg-theme-surface, shadow-theme-lg |
Modals, command palette |
| Drawer | bg-theme-surface, shadow-drawer |
Slide-out resource drawers |
Dark mode adds heavier shadows with subtle white inset borders for edge definition.
- Use
bg-theme-surface,text-theme-text-secondary,border-theme-borderetc. for all surfaces, text, and borders - Use
<Badge severity="...">or<Badge kind="...">for all badge rendering - Use
.btn-brandfor primary action buttons - Use
.card-inner/.card-inner-lgfor nested containers - Use
.status-healthy/.status-degraded/ etc. for status cells in tables - Use
shadow-theme-sm/shadow-theme-md/shadow-theme-lgfor elevation - Use
font-sans(DM Sans) andfont-mono(DM Mono) — they're the defaults - Use
light-dark()in CSS when adding new custom properties that need theme awareness. Note:light-dark()doesn't work for comma-separated values (e.g., multi-layer shadows) — use a.dark {}override block instead, as done invariables.css
- Don't use
bg-white,bg-gray-*,bg-slate-*for backgrounds — usebg-theme-base/surface/elevated/hover - Don't use
text-gray-*,text-slate-*for text — usetext-theme-text-primary/secondary/tertiary - Don't use
border-gray-*,border-slate-*for borders — useborder-theme-border/border-light/border-subtle - Don't use
bg-blue-*/text-blue-*for brand/accent — usebg-accent,text-accent,.btn-brand - Don't use
shadow-sm/shadow-md/shadow-lg(raw Tailwind) for cards, panels, or dialogs — useshadow-theme-sm/md/lg. Raw Tailwind shadows are fine on tooltips and transient floating elements. - Don't hand-write badge color strings like
bg-emerald-100 text-emerald-700 ...— use<Badge>orbadge-colors.tshelpers - Don't use
dark:variants for colors that have theme tokens — the theme handles light/dark automatically - Don't use template literals to construct Tailwind class names — Tailwind can't scan dynamic strings
Exceptions: Raw Tailwind color scales (red-*, emerald-*, etc.) are acceptable in:
Badge.tsxandbadge-colors.ts— static color definitions that Tailwind scans- One-off visual effects (e.g., topology node glow, chart highlights) where no semantic token applies — use
dark:variants for these
Desktop-first tool. Most views assume 1100px+ viewport. Key patterns:
- Topology graph fills available space
- Resource tables truncate columns at narrower widths
- Drawers overlay from the right at fixed width
- Bottom dock (terminal/logs) collapses vertically
Page background: bg-theme-base
Panel/card bg: bg-theme-surface
Input/dropdown bg: bg-theme-elevated
Hover state: bg-theme-hover
Primary text: text-theme-text-primary
Secondary text: text-theme-text-secondary
Muted text: text-theme-text-tertiary
Standard border: border-theme-border
Subtle border: border-theme-border-subtle
Brand button: .btn-brand
Brand accent: bg-accent / text-accent
Card container: .card-inner / .card-inner-lg
Dialog/modal: .dialog
Elevation: shadow-theme-sm / shadow-theme-md / shadow-theme-lg
Status badge: <Badge severity="success|warning|error|info|neutral">
Kind badge: <Badge kind="Deployment|Pod|Service|...">
Table status cell: .status-healthy / .status-degraded / .status-unhealthy
// Card with header
<div className="bg-theme-surface border border-theme-border rounded-xl shadow-theme-sm">
<div className="border-b-subtle px-4 py-3">
<h3 className="text-theme-text-primary font-medium">Title</h3>
</div>
<div className="p-4 text-theme-text-secondary text-sm">Content</div>
</div>
// Primary action button
<button className="btn-brand px-4 py-2 text-sm font-medium">Deploy</button>
// Status in a table cell
<span className="badge status-healthy">Running</span>
// Nested detail section
<div className="card-inner">
<span className="text-theme-text-tertiary text-xs">Label</span>
<span className="text-theme-text-primary text-sm">Value</span>
</div>