PSI Design System

Unified visual language for all Progressive Surface internal web apps. Covers design tokens (184, generated from a single source), 70+ UI components, layout patterns, and example app screens. Light-first, shop-floor optimized, published as an npm package.


Overview

PropertyValue
RepoC:\git\psi-design-system
Package@progressivesurface/design-system on GHE Packages (npm.progressivesurface.ghe.com) — released by pushing a v* tag
Token sourcetokens/tokens.json (W3C DTCG) → npm run build generates ps-tokens.css, tokens.json, tokens.ts, tokens.figma.json
Core fileassets/ps.css — component styles; @imports the generated ps-tokens.css + ps-fonts.css
Iconsassets/icons.js — call via <i data-icon="name">
Fontsassets/fonts/*.woff2 — self-hosted variable fonts, no CDN
Version1.4.1 · 2026-08-27
React appsconsume it via @progressivesurface/ui (psi-platform), which depends on the package and re-ships the CSS — no vendoring anywhere
CI gatesnpm run check (token drift, version, WCAG contrast, CSS rules, docs) + axe-core a11y over the doc pages, on Linux and Windows

Changelog

VersionDateNotes
1.4.12026-08-27Container scoping actually works. data-theme / data-density were documented as usable on <html> or any container, but derived tokens resolve var()/calc() at the element they’re declared on — nested dark containers rendered invisible text and density containers stayed 1×. The generator now re-declares derived tokens in the container-scoped blocks, and [data-theme] is a paint boundary (sets base color/background-color).
1.4.02026-08-25Dark mode + accessibility + token pipeline. tokens/tokens.json becomes the single source of truth with a role-token layer (--ps-on-primary, --ps-outline, state layers) so components never touch primitives; full dark re-theming; solid 2px focus rings; ~30 new components (drawer, bottom nav, rail, FAB, sheets, stepper, sliders, save bar…); responsive .ps-shell app chrome; cascade layers (ps.tokens/base/components/utilities); variable fonts; published as @progressivesurface/design-system; contrast + a11y CI gates.
1.3.12026-06-02Self-hosted fonts. Inter + JetBrains Mono vendored as .woff2 in assets/fonts/ and loaded via assets/ps-fonts.css (imported by ps.css) — no Google Fonts CDN, so the system works behind private endpoints with no public egress. Deploy the whole assets/ folder.
1.3.02026-06-01Gap-closing release. Contextual density (--ps-scale + data-density modes) replaces the v1.2 global html { font-size: 150% } — dense desktop by default, data-density="floor" opts into 1.5x for gloved/distance use, and it no longer inflates consumer rem-based layouts. Success gets its own green (#16A34A), distinct from the brand/primary green, so “Complete” no longer looks like a button. Dark mode + accessibility: tokenized semantic -fg/-border (badges/alerts/toasts now adapt in dark mode), visible focus on every interactive element, offset focus ring (no more green-on-green), real role="switch" toggle, skip link. 14 new components (menu, tooltip, popover, spinner, empty state, banner, chip, avatar, key/value list, timeline, segmented control, date picker, combobox, pagination) plus an upgraded data table (sort, row select, sticky first column, zebra, loading/empty/error states, pagination footer). Data-viz palette (--ps-chart-1..8). Added tokens.json export.
1.2.02026-05-12Global 1.5x scale baseline (superseded by 1.3 contextual density)
1.1.02026-04-29Accessibility pass: 44px tap targets, reduced motion, auto dark mode via prefers-color-scheme, WCAG AA contrast fix, ARIA roles, skeleton loader, strengthened toast borders
1.0.02026-04-23Initial release

Why it exists

PSI had inconsistent UIs across apps — Process Services MES used one style, PSI Explorer another, new tools varied. This system unifies them around a modern SaaS aesthetic grounded in PSI’s actual brand (Pantone 348 green #027A54, near-black ink #1A1D1F).


Design tokens

Brand colors

TokenValueUse
--ps-green-500#027A54Primary action, brand
--ps-green-600#026647Hover state
--ps-ink#1A1D1FPrimary text
--ps-ink-3#606770Muted / captions (passes WCAG AA on --ps-bg)
--ps-bg#F6F7F5Page background
--ps-surface#FFFFFFCards, panels
--ps-border#E4E6E2Default borders

Semantic colors

RoleTokenHex
Success--ps-success#16A34A (v1.3 — distinct from brand green)
Warning--ps-warning#B7791F
Danger--ps-danger#C0392B
Info--ps-info#2563A8

Each role carries four tokens: base · -bg (tint) · -fg (text on tint) · -border. The -fg/-border tokens are overridden in dark mode, so badges, alerts, toasts, and banners stay legible in both themes — always use them rather than hard-coding hex. A categorical chart palette (--ps-chart-1--ps-chart-8) is derived from the brand + category accents.

Typography

  • UI text: Inter → var(--ps-font-sans)
  • Data/IDs/part numbers: JetBrains Mono → var(--ps-font-mono)
  • Scale at 1× (each value is × --ps-scale): 12/13/14/15/17/20/24/30/38/48px (--ps-text-xs through --ps-text-5xl)
  • Self-hosted (v1.3.1): fonts are vendored as .woff2 and loaded via ps-fonts.css — no Google Fonts CDN, so the system renders correctly behind private endpoints with no public egress.

Spacing

4px base grid, each step × --ps-scale: --ps-space-1 (4px) through --ps-space-20 (80px).

Density (v1.3)

One token — --ps-scale — drives type, spacing, control height, and chrome. Default is 1 (dense desktop). Opt into larger sizing per context with data-density on <html> or any container:

AttributeScaleUse
data-density="compact"0.92×ultra-dense data desks
(default)desktop — engineers, office, finance
data-density="comfortable"1.2×tablets (auto on pointer: coarse)
data-density="floor"1.5×gloves + distance viewing (operators, technicians)

This replaced v1.2’s html { font-size: 150% }, which forced large sizing on everyone and inflated consumers’ rem-based layouts. The root font size is now untouched — rem stays predictable.

Radii

  • Controls/inputs: --ps-radius (6px)
  • Cards: --ps-radius-lg (10px)
  • Modals: --ps-radius-xl (14px)

Components (46 total)

CategoryComponents
LayoutTopbar, Sidebar, Page scaffold, Breadcrumbs, Container
ActionsButton (5 variants, 3 sizes), Icon-only button, Segmented control
FormsInput, Select, Textarea, Input group, Checkbox, Toggle (role="switch"), Combobox (typeahead), Date picker
DataTable (sort / select / sticky column / zebra / states), Pagination, KPI card, Progress bar, Key/value list, Timeline, Chart palette + sparkbars
FeedbackBadge (5 states), Status pill (live dot), Alert, Toast (3 variants), Banner, Tooltip, Empty / error state
OverlaysModal, Menu / dropdown, Popover, Spinner
ContainersCard, Launcher tile (category-accented)
NavigationTabs
UtilitiesAvatar (+ group), Chip, Skip link, Monospace, Muted, Eyebrow label, Stack, Row

Quick usage

<!-- Import -->
<link rel="stylesheet" href="assets/ps.css" />
<script src="assets/icons.js" defer></script>
 
<!-- Primary button -->
<button class="ps-btn ps-btn--primary">Save job</button>
 
<!-- Badge -->
<span class="ps-badge ps-badge--success">Complete</span>
 
<!-- Monospace ID (part numbers, job IDs) -->
<span class="ps-mono">J-84720-11</span>
 
<!-- Shop-floor density on a technician/operator screen -->
<html data-density="floor"> … </html>

Full class reference: see AGENTS.md in the repo, or browse components.html.


Prototype pages

FileContents
index.htmlSystem overview, principles, changelog
foundations.htmlColor palette, type specimens, spacing, density, motion
components.htmlAll 46 components with states and variants
patterns.htmlPage shell, list-detail, wizard, empty/error, mobile layouts
examples.htmlLauncher, job list, job detail, technician mobile

Design principles

  1. 44px minimum tap targets — shop floor, gloves, distance; targets grow with data-density="floor"
  2. Density is contextual — one --ps-scale token; dense desktop by default, larger on tablet/floor (not a global override)
  3. Monospace for IDs — part numbers, job IDs, timestamps always use ps-mono
  4. Consistent chrome — same topbar, sidebar, table, badge palette across every app
  5. Copy voice — Direct: “Mark complete” not “Submit”; “48 orders waiting” not “Pending: 48”

Dark mode

Add data-theme="dark" to <html> (or rely on prefers-color-scheme). All --ps-* tokens auto-adapt — including the semantic -fg/-border tokens, so status colors on badges, alerts, toasts, and banners stay legible. No extra classes needed.


For AI agents

Read AGENTS.md in the repo root — it has a full token table, every component class name with HTML examples, the density model, and the design principles. This is the fastest path to building on-brand UIs without opening a browser.

The canonical source of truth for colors and spacing is assets/ps.css; assets/tokens.json is a generated reference export. When implementing in React or any other framework, import or mirror the CSS custom properties — don’t hardcode hex values.