Skip to content

Helm Design System

Helm is the operating platform for a fire-protection contractor’s back office: jobs, liens and collections, vendors, material and training on one Tower foundation. The system is warm paper and navy ink in light, near-black and gold in dark: calm, dense, record-first. Every value lives in one token store; nothing on a screen is spelled by hand.

Page mockups: browser, tablet and phone mockups of whole screens, built with this system, are on their own page: Page mockups.

Page map: every page, tab, panel and modal in Helm, in menu order, with links to each one’s mockup and issues: Page map.

Content fundamentals

  • Say what the record is, in the business’s words. “Lien Register”, “Vendor Holds”, “Schedule of Values”, “Confirm Paid”. Not “Items”, not “Manage”.
  • Sentence case everywhere except text-overline / text-overline-sm eyebrows, which upper-case themselves. Never type capitals by hand.
  • Second person, plain verbs. Buttons name the action: “Start filing”, “Log contact”, “Mark filed”. No “Submit”, no “Click here”.
  • Numbers are the content. Amounts, counts and day-ages carry text-numeric and right-align in tables; an exceptional figure (an overdue total) takes text-body-emphasis plus a status colour.
  • Empty is a state, not a blank. Every list ships an EmptyState with what is missing and the one action that fixes it: “No projects yet. Create your first project to get started.”
  • No emoji, no exclamation marks, no mascots. The brand speaks as a competent colleague.

Colour

  • Ground and ink. Pages sit on background; raised surfaces use card, then helm-s2 and helm-s3 for nested wells. Text is foreground; secondary text is helm-text-dim; placeholders and meta are muted-foreground.
  • Brand pair. primary is navy (#233153) in light and gold (#d5a944) in dark: primary buttons, selected state, focus ring. accent is terracotta (#b95b46) in light and green (#4b8b5f) in dark: hover fills, the accent marker, the active sub-nav underline.
  • Chrome. The top bar and footer are sidebar navy with sidebar-foreground text; sidebar-foreground-dim for inactive labels.
  • Status is meaning, not decoration. Use the StatusBadge tones: helm-green success, helm-orange warning, helm-red danger, helm-blue info, helm-purple special. A status surface is its hue at 12% (*-dim) with a 35% border (*-border). The word always travels with the colour.
  • Aging. Receivables age current → helm-green, tier 1 helm-yellow, tier 2 helm-orange, tier 3 helm-red, tier 4 helm-red-deep.
  • Messages. Contextual strips (process, operational, onboarding, other) use their own color-message-* surface, border and deep same-hue text.
  • Charts take chart-1 … chart-5 in order, through ChartContainer.
  • Contrast. foreground holds 15:1 on background in both themes. Three source pairs miss 4.5:1 and are kept exact: muted-foreground on background in light (3.6:1), accent-foreground on accent (3.6 / 4.0:1) and destructive-foreground on destructive (3.8:1). Keep those to 14px+ semibold labels or non-essential meta.

Type

  • Outfit for all UI and headings (--font-family-ui, --font-family-heading); JetBrains Mono for codes, IDs and aligned figures (text-mono). Both ship with the system as variable font files covering weights 400 to 700 (SIL Open Font License).
  • Use a role, never a size. Page title text-title; section and dialog titles text-heading; body text-body; dense UI text-body-sm; control labels text-label; eyebrows text-overline; meta text-caption.
  • Emphasis is a ladder. At base, sm and caption sizes step medium → strong → emphasis (500 / 600 / 700): text-body-medium for a row’s identifying cell, text-body-strong for a block’s lead line, text-body-emphasis only for a figure that must read as exceptional.
  • Modifiers compose. text-numeric (tabular figures) and text-selected (weight only) sit on top of a size role.
  • A raw Tailwind size beside a role wins over it: remove every utility a role covers.

Space, shape and depth

  • Spacing steps space-xs 4 · space-sm 8 · space-md 16 · space-lg 24 · space-xl 32 · space-2xl 48, plus space-3xs (5px) for icon-to-label gaps. Page padding is page-padding-y 28px by page-padding-x 32px.
  • Radius is one base, radius (10px), with offsets: radius-sm 6px for chips and checkboxes, radius-lg 12px for cards, radius-xl 16px for dialogs, radius-full for pills and avatars. radius-none only where square is the meaning.
  • Borders over shadows. Surfaces separate with a 1px border hairline (border-width-thin). Shadows are for things that float: shadow-sm resting controls, shadow-lg menus and popovers, shadow-xl dialogs. Dark mode deepens every shadow.
  • Layering follows z-*: dropdown 100 < sticky 200 < overlay 300 < modal 400 < toast 500 < tooltip 600.

Layout

  • Every signed-in screen is AppShell → Page, which takes one typed PageContract and renders PageHeader + ContentLayout + two TogglePanels. Do not assemble those by hand.
  • The top bar is top-bar-height (66px); the module sub-nav is secondary-nav-height (40px). Navigation is the MENU button at every width (ED-161-1628); the vertical rail (sidebar-width 228px) is switched off, not deleted.
  • Left/Right Panels open to panel-width (280px), never more than panel-max-width-share (30%) each, and collapse to a panel-rail-width (32px) rail whose only signal is colour-coded dots. Fill them with PanelCard.
  • Below breakpoint-tablet-max (1023px) panels stack above and below the page.

Motion

Motion tokens are CSS variables in the bundle (this format has no motion family): --duration-fast 120ms, --duration-base 200ms (the panel and nav glide), --duration-slow 300ms, --duration-bounce 600ms (still in the store; panels no longer use it), --duration-dwell 1000ms (how long a confirmed state holds before it exits), easing --easing-ease cubic-bezier(0.4, 0, 0.2, 1). Panels and the nav glide at --duration-base; nothing snaps and nothing bounces. prefers-reduced-motion collapses every CSS transition to near-instant.

Iconography

  • Lucide, at icon-size-sm (16px) in controls and icon-size-md (24px) in empty and stat states, stroke inherited from the text colour.
  • Icons are role tokens. Each role maps a slot to a Lucide name (nav.home → House, lien.filing → Landmark, action.mark-filed → Stamp). Render with RoleIcon role="…" or ActionIconButton; never import a Lucide icon at a call site. A new slot gets a new role with a description of where it sits. The Icon roles page shows every assignment live, and the Icons group holds each role as an SVG with its slot.

Marks

  • Helm is a ship’s wheel beside a heavy geometric wordmark. The shell uses the transparent horizontal lockup directly on either surface; the collapsed rail uses the wheel alone. See the Logos group for each file’s ground.
  • LiensEasy is the one-module product shell for lien-collections: a white wordmark with a green (#8fd360) mark, so it sits only on sidebar navy or darker.
  • The mark colours (the wheel’s gold and teal) are artwork, not tokens; never sample them into UI.

Printed documents

The four legal instruments (statutory notice, filing affidavit, lien waiver, lien release) print from the print family in PDF points: US Letter, 1in margins, Helvetica at 14 / 13 / 11 / 10 / 9pt, 15pt leading, pure black print-ink. They never borrow UI colour or type.

Not synced

  • Motion (duration-*, easing-*) has no family in this format; the values ship as CSS variables in components/bundle.css.
  • Icon roles are not a token family in this format; they ship as the Icon roles page, the Icons asset group, and in the bundle as Helm.ICON_ROLES and Helm.ICON_ROLE_DESCRIPTIONS.
  • theme-name, a deploy diagnostic string, is omitted.
  • Uncarded exports: GuideSpotlight (a full-viewport tour overlay anchored to live page elements) and the hooks and helpers (useShellNav, useAnchorBox, useIsNarrowLayout, cn, cx, C) are in the bundle without cards.
  • Route: built. components/bundle.js is packages/design-system/src compiled with Vite and Tailwind v4, with React 19 inside it (React 19 ships no UMD build). Each preview is that component’s Storybook stories compiled against the bundle; ActionIconButton, DataSourceBadge, GuideNote and PanelCard have no stories and their previews are hand-written.