PointSav Design System — DESIGN.md export

Theme: pointsav-brand. Auto-generated from the live vault at https://design.pointsav.com.

Overview

What is the PointSav Design System

The PointSav Design System is a self-hosted, customer-owned substrate for SMBs who want to ship coherent, accessible, AI-readable interfaces without paying enterprise SaaS pricing or accepting hyperscaler brand voice.

The vendor showcase at design.pointsav.com is the canonical instance. Each SMB customer who forks the substrate runs their own instance at their own domain. Single codebase, single deployment shape, two contexts.

What it does

The substrate carries five elements per tenant, in a Git-tracked vault the customer owns:

  • Tokens in W3C Design Tokens Community Group format (DTCG 2025.10 stable spec) — primitive layer (color, type, spacing, motion, focus), semantic layer (interactive-primary, surface-elevated, ...), per-component scopes
  • Components as HTML+CSS+ARIA recipe files — framework- agnostic; the customer's chosen JS framework consumes the recipe, not the other way around
  • Themes — per-brand override layers that re-point semantic references at primitives
  • Research — AI-readable design-decision rationale, accessibility justifications, brand-voice rules
  • Exports — derived caches (Figma Variables JSON, Tailwind config, CSS variables, Style Dictionary builds)

The substrate engine reads the vault from disk and exposes:

  • A public showcase (this site) with the structure design-system practitioners recognise from their previous role
  • A live DTCG token bundle at /tokens.json
  • A shadcn-compatible registry at /r/registry.json (works with v0, Cursor, Claude Code, Windsurf out of the box)
  • A DESIGN.md export at /api/design-<theme>.md (Google's April 2026 spec — the substrate is among the first non-Google implementations)
  • A Model Context Protocol JSON-RPC server at POST /mcp for AI agents querying tokens, components, and research at decode time

Three structural inversions

The substrate inverts the hyperscaler design-system pattern on three structural axes:

  1. Customer ownership replaces hyperscaler hosting. The design system lives in the customer's Git repository, signed by the customer's key, replayable into any tool. Migration cost falls toward zero.

  2. Research as canonical replaces research as marketing. The why lives in the same vault as the what, in the same machine-readable tier, served through the same MCP endpoint. AI agents and human designers read the same files.

  3. Editor-agnosticism replaces editor lock-in. DTCG is the common denominator. FIGMA via Tokens Studio plugin, Penpot natively, Sketch via plugin, hand-authored JSON — any path produces vault content the substrate accepts.

Who uses the substrate

  • SMBs without an in-house design-system practitioner — the substrate gives them Carbon-shape muscle memory without an agency engagement.
  • Holding companies, franchise operators, white-label resellers — the multi-tenant theme model fans out a single substrate across many brands.
  • Regulated SMBs (financial services, healthcare, legal) — the customer-rooted attestation pattern means the design system is part of the customer's audit surface, not the vendor's SaaS controls.
  • AI-codegen-aware teams — every code generator (v0, Cursor, Claude Code, Windsurf) reads the substrate's MCP and registry endpoints at decode time. Generated UI matches the SMB's brand intent without re-deciding the same questions every session.

How it relates to other design systems

The substrate's delivery pattern — sidebar navigation, four canonical tabs per component (Usage / Style / Code / Accessibility), live preview, Git-linked source — is recognisable to anyone who has worked with a Carbon-influenced design system. The cognitive on-ramp is intentional.

The substrate's vocabulary — token names, component recipes, research files — is PointSav-original. No IBM trademarks, no Carbon-derived hex values, no IBM Plex font binding (Inter ships as the default; tenants substitute freely). The substrate is inspired by Carbon's information architecture, not built on Carbon's licensing surface.

What the substrate is NOT

  • Not a Storybook replacement. Storybook is a parallel renderer; the substrate owns its rendering.
  • Not a Figma / Penpot / Sketch competitor. Those are design editors; the substrate is the canonical store the editors interop with via DTCG.
  • Not a SaaS platform. It is self-hosted by design.
  • Not a JS-framework choice. Components are HTML+CSS+ARIA recipes; the customer's chosen framework consumes the recipe.
  • Not a container artefact. The substrate ships as native binaries deployed via systemd. No Docker, no Kubernetes, no OCI artefacts.

Where to next

  • Components — the component recipe library
  • Color — the color system
  • Typography — the type scales
  • GitHub — fork the substrate, file an issue, contribute a recipe

Colors

Color

The substrate's color system has three layers — primitive, semantic, and component. Each tenant's brand lives at the semantic layer; primitives are stable across tenants; components reference semantics, never primitives directly.

Three-layer model

   primitive    color.primary-60  →  #234ed8
        ↓
   semantic     interactive-primary  →  {color.primary-60}
        ↓
   component    button.background-default  →  {semantic.interactive-primary}

A tenant who wants their primary action to be teal instead of blue overrides only the semantic layer:

"interactive-primary": { "$value": "{color.brand-teal-60}" }

Components don't change. Primitives don't change. The override ripples through every consumer of interactive-primary.

Primitive families

The substrate ships five color families at the primitive layer. Names are generic — they describe role, not business meaning.

FamilyUse forSteps
NeutralBackgrounds, borders, ink, dividers10–100 (10 steps)
PrimaryThe tenant's most prominent interactive color10–90
PositiveSuccessful state, positive feedback10–70
CautionReversible problems, expiring states10–70
CriticalFailures, destructive actions, errors10–70

Numbers indicate lightness — 10 is lightest, 90/100 is darkest. A designer arriving from a Carbon-influenced system recognises the scale immediately; the muscle memory is intentional. Hex values are PointSav's, not Carbon's.

Semantic roles

The semantic layer maps roles onto primitives. PointSav-brand ships one canonical mapping; SMB customers fork it.

Ink (text)

TokenUse for
ink-primaryBody text, headings on default surface
ink-secondaryCaptions, helper text, supporting copy
ink-on-interactiveText on interactive backgrounds
ink-on-positive / -caution / -criticalText on support backgrounds
ink-disabledDisabled controls
ink-placeholderInput placeholder text

Surface (background)

TokenUse for
surface-baseDefault page background
surface-subtleBackgrounded panels, sidebars
surface-elevatedModals, popovers, layers above the page
surface-inverseHigh-emphasis inversions

Border

TokenUse for
border-subtleDefault border between sections, cards
border-strongEmphasised divider, input border
border-interactiveFocus / active state

Interactive (background)

TokenUse for
interactive-primary (+ hover, pressed, disabled)Primary buttons, primary links
interactive-secondary (+ hover, pressed)Secondary buttons
interactive-ghost (+ hover, pressed)Ghost buttons
interactive-critical (+ hover, pressed)Critical / destructive actions

Support (status feedback)

TokenUse for
support-positive (+ -bg)Successful state
support-caution (+ -bg)Reversible problem
support-critical (+ -bg)Failure
support-info (+ -bg)Neutral context

Themes

A tenant theme is a themes/<tenant>.json file that overrides the semantic layer. The substrate ships pointsav-brand.json; SMB customers fork.

A tenant can ship multiple themes:

  • <tenant>-light.json and <tenant>-dark.json for theme switching
  • <tenant>-seasonal-2026-q4.json for time-bounded campaigns
  • <tenant>-acquisition-x.json for sub-brand fan-outs

The future theme-composition endpoint (GET /api/themes/compose?base=...&override=...) lets multiple themes resolve into one DTCG bundle at request time — see Doctrine claim #38 leapfrog target L8.

WCAG contrast floor

The substrate's primitive choices guarantee WCAG 2.2 AAA contrast (7:1) for the canonical text-on-surface pairs:

  • ink-primary on surface-base: 14.7:1
  • ink-on-interactive on interactive-primary: 7.4:1
  • ink-secondary on surface-base: 8.9:1

A tenant theme that overrides primitives below the WCAG 2.2 AA floor (4.5:1 normal text, 3:1 large text) fails the audit endpoint (subsequent milestone). The substrate enforces the floor; the tenant chooses everything above it.

Typography

Typography

Two type scales — Utility and Display — split the typographic load between functional UI text and expressive surfaces.

The two scales

ScaleUse forSizes
UtilityUI text — body, labels, captions, table cells, button labels4 steps (12/14/16/16-bold)
DisplayExpressive type — sub-headings, section headings, page titles, hero4 steps (20/24/32/42)

The split is structural, not decorative. Utility text uses optimised letter-spacing for screen-readability at small sizes; Display text uses negative letter-spacing for a tighter, more confident heading rhythm. Mixing scales (Utility-3 used as a heading, Display-1 used for a button label) breaks the system's visual hierarchy.

Font stack

The substrate ships Inter as the canonical sans-serif, with a system-stack fallback chain:

'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif

Inter is a modern, open-source workhorse face (SIL OFL 1.1), optimised for UI rendering at small sizes. The system stack fallback ensures the substrate is fully functional even if the Inter binary is not loaded — degradation is graceful.

A tenant theme can substitute any font family at the primitive layer. Common substitutions:

  • IBM Plex Sans (open-source, Carbon's canonical face)
  • Source Sans 3 (Adobe, open-source)
  • Public Sans (USWDS, public domain)
  • A tenant's own brand face

Self-hosting the font binary is the customer's responsibility; the substrate references the family by name.

The scale

Utility

TokenSize / Line / WeightUse for
typography.utility-112 / 16 / 400Caption, label, table cell
typography.utility-214 / 20 / 400Secondary body, helper text
typography.utility-316 / 24 / 400Primary body floor
typography.utility-416 / 24 / 600UI heading, button label

Display

TokenSize / Line / WeightUse for
typography.display-120 / 28 / 500Sub-heading
typography.display-224 / 32 / 500Section heading
typography.display-332 / 40 / 400Page title
typography.display-442 / 50 / 300Hero / landing

Heading hierarchy

HTMLToken
<h1>display-3
<h2>display-2
<h3>display-1
<h4>utility-4
Body <p>utility-3
<small>, helper, captionutility-2

A heading-level skip (h1 → h3 with no h2 between) breaks accessibility — screen readers rely on the hierarchy to navigate. The substrate enforces this in subsequent-milestone audit work.

Brand voice

The substrate's voice rules live in the active theme's voice block. PointSav-brand:

  • Confident — no hedging, no apology
  • Direct — action verbs first, single-clause sentences where possible
  • Professional — Bloomberg article standard, no marketing register

Banned vocabulary applies to all body and heading copy.

Spacing

Spacing

A 13-step spacing scale on a 16px base. Numeric — space-1 through space-13 — gives one canonical answer per layout decision.

Scale

TokenValueCommon use
space-12pxHairline gutter (rare; usually borders only)
space-24pxInline element gap
space-38pxParagraph rhythm floor; icon-label gap
space-412pxForm field padding
space-516pxBody grid unit; default container padding
space-624pxCard padding; vertical rhythm between sections
space-732pxSection gutter
space-840pxWider section gap
space-948pxMajor section break
space-1064pxPage-level section
space-1180pxPage-level section (looser)
space-1296pxPage-level section (loosest)
space-13160pxHero / landing-page-only spacing

Composition

Compose larger spacing from the scale, never invent off-scale values:

  • 4px + 12px + 4px = space-2 + space-4 + space-2 for a labelled field
  • Card with 24px padding: space-6
  • Section break with title above and content below at 32px each: space-7 × 2

Off-scale values (5px, 14px, 22px) break the rhythm and accumulate as drift. The 13-step scale is dense enough to cover every layout need without resorting to off-scale.

Layout floor

The substrate uses a 16px (space-5) baseline grid. Body text and headings align to multiples of 16px in their line-height calculation; container padding aligns to multiples of 16px in the inline axis. This is structural — it ensures vertical rhythm remains consistent across surfaces.

Motion

Motion

Motion communicates causation. The substrate ships four easing curves and six duration steps. Combine them per interaction class.

Easing curves

TokenCurveUse for
motion.ease-utilitycubic-bezier(.2, 0, .4, 1)Productive interactions — buttons, inputs, tabs
motion.ease-displaycubic-bezier(.4, .14, .3, 1)Expressive interactions — toasts, modals, hero
motion.ease-entercubic-bezier(0, 0, .4, 1)Element appearing
motion.ease-exitcubic-bezier(.2, 0, 1, 1)Element disappearing

Productive curves are designed for short durations (≤200ms); expressive curves work at longer durations (≥320ms) where the extra weight is perceptible.

Duration steps

TokenValueUse for
duration.speed-170msImperceptible — color hover
duration.speed-2120msQuick — button press, focus ring
duration.speed-3200msSnappy — tab switch
duration.speed-4320msSmooth — toast appearance, drawer slide
duration.speed-5480msConsidered — modal entrance
duration.speed-6720msDeliberate — hero animation

Reduced motion

prefers-reduced-motion: reduce is honoured at all interaction layers. Component recipes ship with the media-query override included; consumers inherit. Do not bypass the override — motion-sensitive users have explicitly opted out.

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

Anti-patterns

  • Animations that block user input (modals fading in for 700ms while the focus is unfocusable). Modal entrance is speed-5 for the visual; focus moves immediately at duration 0.
  • Decorative motion that adds time to a productive task. Save expressive motion for moments that warrant the user's attention.
  • Off-scale durations (350ms, 500ms). The 6-step scale covers every interaction class.

Components

Badge (badge)

A small, inline status indicator. Carries a short label and a tone.

Recipe · Registry

Breadcrumb (breadcrumb)

Hierarchy trail to the current page. Useful when nested deeper than two levels.

Recipe · Registry

Button (button)

A trigger that initiates an action. Five variants — primary, secondary, ghost, critical, link — each with its own emphasis and use context.

Recipe · Registry

Checkbox (checkbox)

Boolean choice. Use when each option is independent of the others.

Recipe · Registry

Citation Authority Ribbon (citation-authority-ribbon)

A leading source-classification badge in a references list. Six source types — Academic (A), Regulator (R), Industry (I), Direct source (D), News (N), Web-informal (W) — each with a distinct colour and single-letter glyph. Colour is never the sole differentiator; each badge carries a letter glyph and an aria-label with the full class name. Designed for Wikipedia-style references sections.

Recipe · Registry

Freshness Ribbon (freshness-ribbon)

A per-section date badge that signals content review currency. Three semantic stops: fresh (≤90 days, green), stale (91–365 days, amber), archived (>365 days, muted). ISO date (YYYY-MM-DD) displayed in monospace — datestamp register. Attaches to

/

section headings via flex layout. On by default; toggle off globally via :root[data-freshness-display='off']. All three variants pass WCAG 2.2 AA contrast.

Recipe · Registry

Home Grid (home-grid)

A 3-column responsive category-browse grid for knowledge-wiki home pages. Shows all 9 operator-ratified categories with article count, top-3 child links, and a More → entry point. Collapses to 2-col at 960px, 1-col at 640px. Empty categories always rendered with empty-state copy.

Recipe · Registry

Text input (input-text)

Single-line text entry with label, helper text, and error state.

Recipe · Registry

Link (link)

A navigation primitive — moves the user to a destination without state change.

Recipe · Registry

Navigation bar (navigation-bar)

Page-level navigation header. Logo, primary nav, optional actions, optional account menu.

Recipe · Registry

Notification (notification)

Inline messaging — informational, positive, caution, critical. Toast variant subsequent milestone.

Recipe · Registry

Research Trail Footer (research-trail-footer)

A collapsible disclosure at article foot showing the research pipeline trail — Done, Suggested, and Open questions subsections. Collapsed by default (chrome posture). Uses native

/ — no JavaScript required. Placed after 'See also', before 'References'. Only rendered for articles with research_trail: true frontmatter and non-zero counts.

Recipe · Registry

Select (select)

Single-choice picker from a known list. Native