Overview

Imbi is a DevOps Service Management Platform for managing large environments containing many services and applications. It provides a centralized service catalog with metadata management, dependency tracking, ownership hierarchy, and AI-powered features. The UI is a utility-forward, high-density tool: warm paper surfaces, a single amber brand voice, muted semantic chips, and type chosen for reading long lists of service names all day.

Stack: React 18 · TypeScript · Vite · Tailwind · shadcn/ui primitives (Radix) · React Query · React Router v7. Type: Inter / JetBrains Mono. Brand: WorkerBee amber.

Tokens are defined once in src/index.css as CSS custom properties and exposed through Tailwind utility classes via tailwind.config.js. Dark mode is class-based (.dark on <html>). A pre-React bootstrap in index.html applies the saved theme before first paint to avoid a flash.

Principles

Four rules that set the line between cleaner and busier. When in doubt, remove.

  1. One brand voice, many muted helpers. Amber is the only saturated color. Status and environment chips sit on quiet tinted backgrounds so a card reads as one object, not five.
  2. Density over decoration. 142 services want tight rows and small type, not generous gradients. Pad where type breathes; compress where the eye scans.
  3. Warm grays, not cold. Ink and paper are hue-shifted toward amber (~40° hue). Never pure black on pure white.
  4. Tokens before exceptions. If you reach for a raw hex in a component, it belongs in the token file. Hardcoded colors are the #1 source of theme drift — the tokens migration in commit 4cc2e78 exists to enforce this.

Color

Surface ramp is warm paper in light, warm ink in dark. Both share the same amber accent so the brand does not shift.

Surfaces & ink

TokenLightDarkUse
--bg-app#f5f4f0#141413page canvas
--bg-surface#ffffff#1e1e1ccards, panels, nav
--bg-sunken#eeedea#2a2a27toolbars, inputs-in-cards
--fg-primary#1a1a18#e8e7e3titles, body
--fg-secondary#5f5e5a#a0a09adescriptions, labels
--fg-tertiary#888780#6b6b65meta, captions
--border-subtlergba(0,0,0,.15)rgba(255,255,255,.15)cards, dividers
--border-defaultrgba(0,0,0,.3)rgba(255,255,255,.25)buttons, inputs
--border-strongrgba(0,0,0,.4)rgba(255,255,255,.35)hover / active

Brand — Amber

Single ramp from --amber-50 to --amber-800. The primary action is --action-bg which flips between #b8860b (light) and #fbce39 (dark) to match the header logo in each theme.

amber-100
#faeeda · chip bg
amber-400
#ef9f27 · focus ring
amber-500
#b8860b · action bg
amber-700
#7c5900 · amber ink

Semantic palette

Six roles. Each exposes a bg, fg, and dot. Backgrounds read at ≥7:1 against their fg; dots are used for filter pills and chip leaders. Defined in src/index.css and mapped to Tailwind utilities as bg-{role} / text-{role} / border-{role}.

RolePurposebg (light)fg (light)Tailwind
Successconfirmed state, passing checks#eaf3de#27500abg-success / text-success
Warningneeds feedback, pending#faeeda#7c5900bg-warning / text-warning
Dangererror, failed, blocked#fcebeb#a32d2dbg-danger / text-danger
Infoneutral context, related link#e6f1fb#185fa5bg-info / text-info
Accentimplementing, follow-up#eeedfe#3c3489bg-accent / text-accent
Neutralqueued, default#f1efe8#444441bg-tertiary / text-secondary

Example: environments

Environments are user-configurable (see Label colors) — Testing / Staging / Production are instances of the Label system with default seed colors, not a parallel semantic role.

Testing Staging Production
22px tall · 5px radius · 11.5px/500 · rendered by EnvironmentBadge (src/components/ui/environment-badge.tsx).

Label colors

Environments, blueprints, teams — anywhere the product lets a user tag an object with a color — share a single 8-swatch palette. Saturation is capped at ~60 and lightness at ~58 so every swatch sits on the same tonal plane as the amber brand and warm paper surface.

The palette

NameHexSeeded useChip preview

Chip derivation

Chips compute their own bg and fg from the stored hex. Users never pick fg — the system guarantees contrast.

// Light mode (matches ColorPicker preview + EnvironmentBadge style)
bg = rgba(r, g, b, 0.20)            // 20% alpha of swatch
fg = darken(hex, 0.35)              // multiply channels by 0.65

// Dark mode
fg = lighten(hex, 0.30)             // shift toward white

Rules

  • Never present the raw hex picker as the first interaction — the 8 swatches cover 95% of cases and enforce the system.
  • The free-form hex input is an escape hatch. When a user types a custom hex, clamp saturation to ≤65 silently before saving.
  • Semantic colors (success, danger, info, warning) are not in this palette. Those are reserved for system messaging.
  • A label's color is stored as raw hex, not a palette index — so adjusting defaults later won't repaint existing data.
  • The picker is defined once in src/components/ui/color-picker.tsx and reused anywhere a color is captured.

Typography

Inter for UI, JetBrains Mono for IDs, hashes, and counts. The scale is short on purpose — every size below is defined in tailwind.config.js under fontSize.

UtilitySizeUse
text-h124px / 500Page title
text-h220px / 500Section title
text-card-title18px / 500Card heading
text-task16px / 500Task / inline heading
text-base15.5px / 400Body
text-sm15px / 400Secondary body
text-badge13.5px / 500Badge text
text-xs13.5px / 400Meta
text-chip13px / 400Chip label
text-overline13px / 500 · 0.5 trackingSection eyebrow

Spacing

Page and component spacing is centralized in tailwind.config.js under theme.extend.spacing and applied with named utilities instead of raw numbers.

TokenValueUse
nav-height48pxTop navigation bar
page-x / page-y20 / 28pxPage edge padding
card-x / card-y18 / 14pxList card padding
card-detail-x / -y22 / 18pxDetail card padding
tab-x / tab-y18 / 11pxTab button padding
tab-panel22pxTab content padding
form-gap16pxGap between form fields
meta-gap8pxMeta row inline gap
avatar-gap12pxAvatar + name gap
card-gap8pxChips inside a card

Radii & elevation

Soft but not squishy. Tailwind's rounded-{sm,md,lg,full} map to 4 / 8 / 12 / 9999 px respectively. sm for chips, md for controls, lg for cards and panels.

TokenValueUse
rounded-sm · --radius-sm4pxchips, badges
rounded-md · --radius-md8pxbuttons, inputs
rounded-lg · --radius-lg12pxcards, dialogs, panels
rounded-full9999pxpills, avatars, health

Elevation

TokenUse
--shadow-smcard hover
--shadow-mdpopovers, menus, dropdowns
--shadow-lgmodals, dialogs
--ringfocus ring (amber @ 25% alpha)

Buttons

Three variants, shadcn-based. Primary is amber; sm is the default across the product except in empty states and modals. Implementation lives in src/components/ui/button.tsx.

<Button variant="default" size="sm">New</Button>
// variants: default | secondary | ghost | outline | destructive | link
// sizes:    default | sm | lg | icon

Inputs

Single field style. Implementation in src/components/ui/input.tsx and src/components/ui/textarea.tsx. Focus uses the amber ring; error states swap the border to border-danger.

Chips & badges

One shape, six semantic colorways plus three named environment colorways. If you need a new state, extend the semantic set — no custom shapes.

Semantic

Queued Needs feedback Implementing In review Failed Related

Sizes

sm default · 22px lg

Health indicator

Tinted background + 1.5px ghost ring, not a heavy ring-4. Thresholds: ≥85 ok, 75–84 warn, <75 bad.

93
≥ 85 · healthy
78
75–84 · watch
68
< 75 · at risk

Cards

One card shell — 12px radius, 1px subtle border, card padding tokens. Implementation: src/components/ui/card.tsx (shadcn). Composable with CardHeader / CardContent / CardFooter.

API
Address Verification
API
86
Validates postal addresses in the US and internationally.
Conversions Testing Staging Production
Anatomy: 32px type icon · title + subtype · 34px health pill · 2-line description · team/envs meta row.

Tabs

Single-row tabs for object-level navigation (Overview, Configuration, Dependencies, …). Active state is a 2px amber underline, not a filled pill — filled pills are reserved for the top nav.

Never bold weight + underline on the same tab — one cue is enough. Count pills flip to amber on the active tab.

Version chip train

Deploy state across environments in one row. Chips are grouped inside a sunken surface, separated by right-arrows to imply promotion flow. Hashes use JetBrains Mono with tabular-nums so they don't jiggle on updates.

Testing 1962b02 Staging 1.0.11 Production 1.0.10

Key/value rows

Spine of every detail view. Label on the left in fg-tertiary, value right-aligned in primary ink. Rows separate with border-subtle, never alternating backgrounds. Implementation: src/components/EditableKeyValueCard.tsx and src/components/ui/key-value-editor.tsx.

TeamConversions
Slugaccount
Deployment TypeGitHub Actions
Lines Of Code9,353
SonarQubeSuccess

Segmented & filters

Segmented controls for mutually-exclusive views. Filter pills for additive narrowing. Never mix them in the same row.

Segmented

Filter pills

Color picker

Used anywhere a user labels an object with a color — Environments, Teams, Blueprints, Project Types, anything tagged. Renders the 8-swatch label palette as the primary path, custom hex as an escape hatch, and a live preview rendered at the three sizes the chip appears across the product. Implementation: src/components/ui/color-picker.tsx.

ClayEmberHoneyMoss DuskLilacRoseStone
or custom hex
Valid

API

<ColorPicker
  value={labelColor}                  // controlled hex, "" when unset
  onChange={setLabelColor}            // fires on swatch / hex / native picker
  objectType="environment"           // "environment" | "project type" | ...
  labelValue={name}                   // name used in preview chips
/>

Anatomy

  • Swatch grid · role="radiogroup", 8 radio buttons on one row. Primary path. Selected swatch shows dark border + white check.
  • Name row · shared vocabulary — users and docs refer to labels by name ("Moss"), not hex.
  • Divider · or custom hex marks the escape hatch as secondary.
  • Hex input · native color-picker pip on the left (OS eyedropper), editable hex, contrast status on the right.
  • Preview · rendered inside the picker using labelValue at three sizes (22px chip, 26px chip, dot+text).

Rules

  • Contrast status flips to "Low contrast" (warning) when chip bg/fg fails 3:1. Does not block save — the user is the tenant admin and may have reasons.
  • Keyboard: ArrowLeft/Right/Up/Down cycle focus, Space/Enter select, Tab jumps to the hex field.
  • tabindex is roving (only the selected swatch is in the tab order).
  • The label/name text in the preview mirrors the adjacent Name field in the form; the picker does not fabricate it.

Token reference

All surfaces, ink, and semantic colors are defined as CSS custom properties in src/index.css and mirrored into Tailwind utilities via tailwind.config.js.

/* Surfaces */
--color-background-primary      → bg-primary    · cards, panels
--color-background-secondary    → bg-secondary  · secondary surface
--color-background-tertiary     → bg-tertiary   · page canvas / sunken

/* Ink */
--color-text-primary            → text-primary   · titles, body
--color-text-secondary          → text-secondary · descriptions
--color-text-tertiary           → text-tertiary  · meta

/* Borders */
--color-border-tertiary         → border-tertiary · default divider
--color-border-secondary        → border-secondary
--color-border-primary          → border-primary · hover / active

/* Brand / action */
--color-action-bg               → bg-action   · primary CTA (#b8860b / #fbce39)
--color-action-bg-hover         → bg-action-hover
--color-action-fg               → text-action-foreground

/* Semantic triples — bg / fg / border */
--color-background-{info,success,warning,danger}
--color-text-{info,success,warning,danger}
--color-border-{info,success,warning,danger}
// Utilities: bg-info / text-info / border-info  (etc)
// Ring utilities: ring-info / ring-success / ...

/* Shadcn compatibility (HSL triplets) */
--background / --foreground
--primary / --primary-foreground
--secondary / --muted / --accent / --destructive / --card / --popover
--border / --input / --ring

Chart and status palettes (task status, link type, avatar fallback) are defined as hardcoded Tailwind color objects inside tailwind.config.js rather than CSS variables because they are not theme-reactive.

Codebase map

When implementing a feature that touches the design system, these are the files to read first.

PathWhat lives there
src/index.cssAll CSS custom properties; :root (light) + .dark overrides.
tailwind.config.jsUtility mappings (bg-*, text-*, border-*), typography scale, spacing tokens, status color objects.
index.htmlPre-React bootstrap that applies the saved theme class before first paint.
src/contexts/ThemeContext.tsxTheme provider + useTheme() hook. Uses useLayoutEffect to keep the class in sync.
src/components/ui/Shadcn-based primitives: button, input, card, dialog, tooltip, admin-table, environment-badge, color-picker, key-value-editor, icon-picker, …
src/components/ui/environment-badge.tsxRenders the Testing/Staging/Production chip from a stored label_color hex (20% alpha bg, 40% alpha border, raw hex fg).
src/components/ui/color-picker.tsxThe 8-swatch picker; takes value, onChange, objectType, labelValue.
src/components/admin/environments/EnvironmentForm.tsxCanonical consumer of ColorPicker (passes objectType="environment" and labelValue={name}).

When to reach for which token

  • Prefer semantic utilities. text-danger, bg-success, border-info — never text-red-500.
  • Prefer named spacing. px-page-x, py-card-y over raw Tailwind px-5 when the context is a known surface.
  • Prefer rounded-{sm,md,lg} over raw pixel values — the mapping is defined once.
  • Prefer the theme context. Read isDarkMode from useTheme() instead of inspecting document.documentElement.classList.
  • Never hardcode a hex inside a component. Add it to src/index.css (or tailwind.config.js for status/chart palettes) and expose a utility.