06 / 06 · PTI

An enterprise design system, built so engineering could ship without a designer in every meeting.

Enterprise SaaS Design system UX audit Design tokens
Client
PTI
Planning software · enterprise field service
Role
Lead UX Designer
Audit → design system
Customers
KPN · VAT · VAL
Eneco · Ziggo · others
Output
Tokenised system + Figma library
30+ components, 3-layer architecture

One system. 30+ components. Three token layers.

Built from scratch on a primitive → global → component token architecture, so engineering can ship new screens without reinventing buttons, colours, or radii.

Brand 500 Turquoise 500 Yellow 500 Success 500 Warning 500

Components · live preview

Brand 500 · #931F80
Primary Secondary Success Error
Brand Success Info Pending Draft
Auto-route enabled
01 · Context

An enterprise SaaS with great engineers, no designers, and visible seams between every app.

PTI ships planning software for field service crews at some of the largest utility and telecom companies in the Netherlands - KPN, VAT, VAL, Eneco, Ziggo. Their software tells field engineers where to be, what to do, and in what order. Mission-critical stuff.

They hired me for a UX audit. The audit surfaced one root cause behind nearly every complaint: zero design consistency. Different button styles per screen, different headings, different colours, different spacings. Multiple applications stitched together where users could literally see the seams.

The root of the root: PTI had brilliant engineers but no in-house UX. When a feature needed a screen, a dev built the screen. Free reign on buttons, type, spacing. Multiply that across years and four applications and the visual chaos was inevitable.

No research budget. The biggest lever was consistency itself - so we built a design system.

02 · The audit

Before vs after - what the system changed.

The first deliverable was a written audit. The second was the design system that addressed it.

BeforeAudit findings

  • Three different button shapes across four applications. Hover/disabled states inconsistent.
  • Colours sampled from screenshots, not from a palette. Brand purple varied per page.
  • Heading hierarchy invented by each developer. H1s with smaller font-size than nearby H2s.
  • No spacing system. 11px next to 15px next to 24px next to 7px on the same screen.
  • Modal patterns differed per app. Close button top-right OR bottom OR neither.
  • Engineers asked to build screens cold, with no spec and no reference.

AfterWhat we shipped

  • One button family. Primary / secondary / tertiary × 4 intents. Every state mapped.
  • Primitive → global → component colour tokens. Brand 500 is one hex, referenced everywhere.
  • 9-step type scale, Montserrat for display, Inter for text. Engineers pick from the scale.
  • 11-token spacing system. Devs pick by intent name, not pixel value.
  • One modal pattern. One toast pattern. One sidesheet pattern. Each specced top-to-bottom.
  • Figma library that maps 1:1 to the component library engineering is now building in code.
03 · Architecture

Primitives → globals → components. Three layers, no shortcuts.

Every design decision flows top-down. Change a primitive, the whole system updates. Change a component, only that component updates.

Layer 01 · Primitives

The raw values.

Hex codes, pixel scales, font sizes. The only place a hard-coded value exists in the system.

--colors-brand-500: #931F80;
--colors-brand-800: #3B0C33;
--numerics-4-16px: 16px;
Layer 02 · Globals

The semantic layer.

Names that describe intent. "brand", "error", "surface-secondary" instead of raw hex.

--base-brand: var(--colors-brand-500);
--base-brand-hovered: var(--colors-brand-800);
--spacing-xl: var(--numerics-4-16px);
Layer 03 · Components

The named tokens per part.

Every component has its own scoped tokens. Reference globals, never primitives.

--button-primary-bg: var(--base-brand);
--button-primary-bg-hover: var(--base-brand-hovered);
--button-padding: var(--spacing-xl);
04 · Components

30+ components, every one variantised and state-complete.

Every component lives in Figma with all variant axes mapped. Engineers pick the variant by name and it lands in code the same way.

Buttons

3 styles × 4 intents · 6 states each
Brand · primary / secondary / tertiary
Primary Secondary Tertiary Disabled
Success · Error
Approve Approve Delete Delete

Badges

5 intents
Brand Success Info Pending Draft Error
Tag · with icon
● Open ● Done ● Pending

Chips

Removable
KPN × Amsterdam × Today × Critical × + Add filter

Inputs

Default · active · error · success
DefaultOrder number
ActiveORD-2026-04-512
ErrorInvalid order ID
SuccessORD-2026-04-512 ✓

Checkbox · Radio · Toggle

All states
Checkbox
Unchecked Checked Indeterminate
Radio · Toggle
Off Selected Off On

Codefield

Monospace + line numbers
1{
2  "orderId": "ORD-2026-04-512",
3  "engineer": "M. de Vries",
4  "status": "in-progress"
5}

Toast & Banner

5 intents · 4px left rail
Order updatedThe work order was successfully reassigned to M. de Vries.
Synced4 work orders synced with KPN's planning system.
DNS pendingEngineer route not yet validated. Check back in 2 minutes.
Sync failedCould not reach the Ziggo endpoint. Retrying.

Navigation

Tabs · Breadcrumbs · Pagination
Tabs
Overview Schedule Engineers Reports
Breadcrumbs
Planning/ KPN/ Amsterdam Centrum/ Order ORD-2026-04-512
Pagination
1 2 3 12

Modal · Tooltip

Overlays
Reassign work order

Move ORD-2026-04-512 from M. de Vries to another engineer. The customer will get a notification.

Cancel Reassign
Click to reassign Last synced 2 min ago

Page rows · Sidesheet

Data + overlays
Overview
Schedule
Engineers · M. de Vries
Order details×
ORD-2026-04-512 · Amsterdam Centrum
Engineer: M. de Vries · ETA 14:30

Datepicker

April 2026
‹ April 2026
M
T
W
T
F
S
S
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

File selector

Upload zone
Drop files here, or click to browse.pdf, .csv, .xlsx · up to 50 MB

Table

5 variants · row hover · status column
OrderEngineer · AddressStatus
512M. de Vries · Spoorstraat 21, AmsterdamDone
513M. de Vries · Kerkstraat 8, AmsterdamPending
514S. van Dijk · Stationsplein 1, UtrechtOpen
515R. Bakker · Damstraat 14, RotterdamBlocked
05 · Impact

What this unlocked.

A design system isn't a deliverable. It's a multiplier.

30+
Components in Figma + code, in lockstep.
3
Token layers - primitives, globals, components.
8
Colour ramps × 11 stops + alpha variants.
0
Hex codes hard-coded anywhere except primitives.
06 · Reflection

What I'd tell you as a client.

We almost picked Material. I'm glad we didn't.

The first instinct was to fork an existing system - Material, shadcn, Untitled UI. We almost did it. But PTI's product needs were specific - field-service workflows, map-heavy screens, dense data tables - and the trade-off between "fast start" and "easy to tweak" was real. We went custom.

What I'm proud of: the three-layer architecture survived every "but what if we want X" question. Engineering can rebrand the entire system by changing the primitives layer. The Figma library matches the code library 1:1. That alignment is the win.

What I'd do differently: get an engineer in the room from day one of token naming. We did the second pass together and the names got 30% better. Should have been the first pass.

Back to start · Case 01 of 06

DUO.