Design system · 2026

Votee Design System

A governed component library — tokens, primitives and a 466-mark asset set — that keeps six products speaking one language.

Design systemsUIFront-end

41
UI primitives on one token contract
466
Marks in the asset library
51
Story files documenting component states
16
Segments the third-party set is curated into

01Project overview

Role
Design system owner — tokens, components, governance
Timeline
2026 — current
Surface
Web Portal · Agent
Consumers
Votee tech team

I own the system end to end — token spec, component library, asset set, and the cadence that keeps them in step.

Where it ships

Self-hosting design system.

A lighter second output — design.md

One portable file — colour roles, type scale, spacing, etc.

Storybook
Votee Dev
Web
Mobile
Agent UI

02Target user

Two audiences, opposite asks. Neither is a designer.

  • Votee tech teammates

    Engineering

    They use Claude Code to build PoCs, documentation sites, and production UI.

    Already working in

    • Claude Code
    • MCP
    • Votee Agent Skills
  • Votee biz teammates

    Commercial

    They need quick access to the right assets for decks and docs. As the buffer time to prepare sales material are tight.

    Already working in

    • Google Slides
    • Manus
    • Google Docs

03Situation

Votee AI Studio is vibe-coded — no one draws it. Inside one app, the same button ships at five sizes with six different animation timings.

One product · one button component

Nobody drew it. Nothing agrees.

8 declared sizes · 5 in use · 331 instances

xsSave
unused
smSave
×158
defaultSave
×92 implicit
lgSave
×2
icon-xs
unused
icon-sm
×9
icon
×60
icon-lg
icon · unused

sm absorbs half the traffic and default is never typed once · icon and icon-lg are byte-identical · 10 more decide their size at runtime

6 transition durations · 10× spread

100ms
×1
150ms
×2
200ms
×23
300ms
×9
500ms
×1
1000ms
×1

duration-200 ×23 — closest to a default, never declared as one

After

3 semantic sizes · 1 motion token

SaveSaveSave

One token contract · every surface picks up the update automatically

04Systematic taxonomy

Shipped app layer

--radius 0.65rem

globals.css

shadcn “Orange” theme — OKLCH, a functional ramp, --chart-1…5.

Google Sans Flex / Code

  • In-product UI

01

Primitive value

#ff7f32

oklch(0.7 0.19 48)

02

Semantic role

primary

Named for what it means, not what it looks like.

03

Component token

Button.bg
primary
Button.fg
on-primary

04

Platform output

  • Web — React / Tailwind
  • Flutter — Material 3
  • React Native

Governing rule

In-product code reaches for bg-primary and text-muted-foreground — never a raw hex value.

One rule does most of the work

Product code asks for the primary action colour. It never asks for orange.

Anatomy of a token name

Shipped brand token — opt-in layer

brand-tealforeground
  1. brand- namespace: the opt-in brand layer — it never replaces --background
  2. teal role: the second brand hue, used beside orange
  3. foreground pairing: the ink guaranteed to pass contrast on that surface

Produces

Enterprisetier="enterprise"

DS classes: border-brand-teal/30 bg-brand-teal/10 text-foreground

Tailwind + shadcn/ui — shipped app layer

sidebar-accentforeground
  1. sidebar- scope: which surface owns it — scoped tokens re-theme independently
  2. accent role: the semantic slot — accent, primary, muted, destructive
  3. foreground pairing: the ink for that slot, not a separate colour decision

Produces

Reports
Dashboard
bg-sidebar-accent text-sidebar-accent-foreground

Why the pairing is named, not chosen

Each token ships with its contrast ratio measured against white and black and graded AA/AAA. A surface and its -foreground are published as a pair, so a passing combination is the one already in reach.

Same anatomy, shadcn/Tailwind grammar

--muted-foreground

mutedforeground

role + relationship

Primary label

Supporting / secondary

--chart-1

chart1

family + index

1
2
3
4
5

--accent-soft-border

accentsoftborder

role + modifier + element

Callout

Border slot — not fill or text.

The brand token table in Storybook, listing each token with its hex, RGBA and measured WCAG contrast ratio against white and black
Every brand token published with its measured contrast against white and black, graded AA/AAA. The pairing rule is enforced by the table, not by memory.

05Managed versioning

Two surfaces, kept in step by a short loop.

The MCP server and the component library have to describe the same thing. When MCP falls behind, agents confidently build against a shape that no longer exists.

Two surfaces, one shape

MCP server

What agents query

Components, tokens, logos.

Component library

What ships

The React primitives, live in Storybook.

kept in sync

The sync loop

  1. 1

    Component changes

    A primitive or pattern is updated.

  2. 2

    Announced in channel

    Posted where the whole team sees it.

  3. 3

    Reviewed at weekly

    Team agrees what MCP should expose.

  4. 4

    MCP updated

    Server catches up to the new shape.

  5. 5

    Agents build right

    Generated screens match what actually ships.

The mechanism

Decide + reconcile

Weekly sync

Walks through what changed and what MCP should expose.

Announce + unblock

Team channel

Where a change lands the moment it ships, in the open.

How it stays in step

Changes are announced in the team channel as they land, then reconciled at the weekly. Short loop, no surprises.

A Storybook story with the spacing overlay enabled and the accessibility and interaction test panels open
The same story doubles as the spec and the test: spacing overlay on the 8px grid, with accessibility and interaction checks running against the rendered component.

06The mark library

Icons are another useful component for business teammates.

The full third-party set is curated into 16 segments across the design system. This section shows 5 of them — 86 marks in total.

  • AI Models· 11 marks
  • AI Agents· 19 marks
  • AI Protocol and Framework· 3 marks
  • Data & Infrastructure· 24 marks
  • Task-Oriented Platform· 29 marks
The third-party logo story in Storybook, showing the AI Models and AI Agents groups with per-mark copy and download actions
The library as teammates meet it — grouped, counted, and every mark one click from SVG, PNG or a copied snippet.

07AG-UI

As the agent works, the screen updates live — not all at once when it's done.

AG-UI is the connection between the agent and the screen. It sends each small change as it happens, so the reply builds in front of you.

AG-UI

What’s running

The Agent ↔ User streaming protocol — typed SSE events that carry what the agent is doing straight to the screen as it happens. It is the system’s primary transport.

Packages
@ag-ui/core · @ag-ui/client
Stream endpoint
POST /v1/threads/{id}/agui-stream
State model
The backend keeps a canonical CognitiveState and ships minimal RFC-6902 JSON Patches as STATE_DELTA events — the client mirrors server state with no per-event reducers.

This is the one the whole system actually runs on.

Which model should we ship for Cantonese?
Here’s how the three candidates compare on the Q2 Cantonese benchmark — context window, accuracy, and latency all at once. [genui:comparison-table]

JSON spec (what the agent emits)

{
  "root": "card",
  "elements": {
    "card": {
      "type": "Card",
      "props": { "title": "Model comparison", "maxWidth": "full" },
      "children": ["table"]
    },
    "table": {
      "type": "Table",
      "props": {
        "columns": ["Model", "Context", "Cantonese acc.", "Latency"],
        "rows": [
          ["Atlas",     "1M",   "+12%", "120ms"],
          ["Beever-LR", "256k",  "+8%",  "90ms"],
          ["Baseline",  "128k",   "—",  "140ms"]
        ],
        "caption": "Q2 evaluation set"
      }
    }
  }
}

Rendered result

Model comparison

Q2 evaluation set
ModelContextCantonese acc.Latency
Atlas1M+12%120ms
Beever-LR256k+8%90ms
Baseline128k140ms
  1. Server

    Keeps the one true record

    Every message, tool call and step the agent takes updates a single record on the server — the one source of truth.

  2. Streaming

    Sends only what changed

    Instead of resending everything, the server streams just the small update, each time something happens.

  3. App

    Applies each update live

    The app merges every update into its own copy automatically, with no extra code per case.

  4. Screen

    Simply draws what it's given

    That live copy is what you see — message bubbles, tool activity, reasoning steps.

08Impact & results

Icon library

466 marks total

304
162
In-house, soft-3D
304 marks · 6 products
Third-party brand
162 marks · 16 segments
  • UI primitives
    41
  • Storybook stories
    51

51 stories against 41 primitives — every component ships with more than one documented state.

Platform reach

2 token layers → 3 targets

  • Brand spec
  • Shipped app tokens
  • Web — React / Tailwind
  • Flutter — Material 3
  • React Native

466 marks, 41 primitives documented across 51 stories, one token spec reaching three platforms — the same system, drawn once.

  • Component states are documented rather than remembered.
  • Marks come from the library, not from a per-deck hunt.
  • Agent-generated front ends land inside the system by default.
  • What I'd change: adoption is visible only by reading code. It should report itself.

09AI-native execution

Authored as source, not as a file to copy — tokens, stories and the mark pipeline live in the repo.

  • Claude CodeWhere the token spec, components and stories are written — and the MCP server that exposes them.

  • StorybookThe published contract: one story per state, a docs page per component.

  • PlaywrightInteraction tests behind the stories, plus a render check on every mark.

  • FigmaWhere type scale, spacing and mark candidates are judged by eye first.

  • MermaidArchitecture and taxonomy diagrams themed through one shared variables block.

Teammate involved

Agents did the typing. I wore both hats here — set the direction, made the calls, reviewed what shipped.

UX/UI DesignerUX/UI Designer
AI EngineerAI Engineer