Portfolio
Case study · 2021-2024

A design system,
treated as a product.

Rebuilding the foundation of a multi-platform workplace product ecosystem. From fragmented Material Design overrides into a tokenized, governed, and adopted system serving web, mobile, kiosks and meeting room displays.

01The scaling problem

Deskbird grew faster than the UI it was built on.

The product had been built quickly on top of Google Material Design, which made sense in the early days. As the team grew, a designer began adapting and overriding Material components without a clear system or governance model.

What started as “we’ll standardize later” became structural debt: duplicated patterns, drifting spacing, components that looked similar but behaved differently, and a frontend codebase increasingly hard to evolve.

When I joined, my first responsibility was to rebuild the foundation, not as a library of components but as the operational backbone of a fast-growing multi-platform product.

7
Button variants across the codebase
4
Different modal implementations
12+
Spacing values used inconsistently
0
Source of truth for tokens
0
Governance or intake process
~38%
Components reused across pages
Before · Inventory audit · Q1
Hot desk ✕Hot deskhot_desk

A 3-day audit across the web app, admin and mobile screens surfaced dozens of near-duplicate primitives. None of them were “wrong” in isolation. Together they made the product feel inconsistent and made every new screen a small negotiation.

02Approach

The system was built as a product, with its own users and roadmap.

The mistake I’ve seen most often in design systems is over-engineering, building for the system instead of for the people using it. Deskbird needed a practical foundation, not the biggest component library possible.

Users

Designers and frontend engineers. Their workflows defined every API and naming decision.

Product

Backlog, releases, intake, and changelogs. Treated with the same rigor as any feature team.

Outcome

Predictable shipping velocity, lower onboarding cost, consistent multi-platform UX.

03Research & discovery

Most of the answers were already inside the team.

Before designing a single component, I spent weeks watching how the team actually worked. The clearest signals came from screen-shares, not surveys.

Figma screen-shares
Asked designers to walk me through a real flow. Watched where they hesitated, copy-pasted, or rebuilt from scratch.
Frontend syncs
Bi-weekly with frontend leads. What hurts to maintain. What needs flexibility. What should be hard-locked.
Inventory audit
Catalogued every primitive in production. Mapped overrides back to a small set of intended patterns.
Support tickets
Read 6 months of customer-facing inconsistencies. The end-user felt the debt too.
Synthesis board · 41 notes
Same button, 3 names
Devs ship custom CSS to fix spacing
Modals built from scratch every time
No way to mark legacy
Designers fear breaking flows
Tokens would unlock dark mode
PrimeNG vs custom unclear
Storybook unmaintained
Kiosk needs bigger hit areas
04Design principles

Four constraints that shaped every decision.

01
Right-sized

Build what the product actually needs. Resist coverage as a vanity metric.

02
Predictable

Components behave the same across platforms. Surprise is a bug.

03
Documented in place

Specs live where the work happens, inside Figma, next to the component.

04
Tokenized end-to-end

Color, spacing, type, radius, elevation. No raw values in production.

05Token architecture

Everything became a token. That changed how the team shipped.

When Figma rolled out variables, we restructured the foundation around a 3-tier token model: primitive → semantic → component. The output is a single JSON source that feeds Figma, web, iOS and Android, written once, propagated automatically.

Color · Semantic
TokenValueUsageSample
color.surface.default#FFFFFFPrimary surface
color.surface.muted#FAFAF7Page background
color.surface.inverse#0E0F13Dark surfaces
color.action.primary#5B5BF6Primary actions
color.action.danger#E04F5FDestructive actions
color.feedback.success#0FA37FSuccess states
color.feedback.warning#E0A52AWarning states
color.text.primary#0E0F13Primary text
color.text.muted#5A5F6BSecondary text
Spacing scale · 4px base
space.00
space.14
space.28
space.312
space.416
space.524
space.632
space.748
space.864
Radius & elevation
radius.sm
radius.md
radius.lg
radius.xl
shadow.sm
shadow.md
shadow.lg
Typography scale
text.displayDisplay · 44 / 1.05
text.h1Heading 1 · 30 / 1.15
text.h2Heading 2 · 22 / 1.25
text.bodyBody · 14 / 1.55
text.captionCaption · 12 / 1.45
06Component system

~60 components, each defined by behavior rather than appearance.

Every component had a contract: states, density, accessibility, responsive behavior, and what it intentionally did not do. Below, a handful of the most-used primitives, recreated.

Button
Primary action surface
component
Checkbox
States & focus ring
component
  • Unselected
  • Selected
  • Focus
  • Disabled
Inline message
In-flow feedback
component
Booking will be confirmed within 24 hours.
Desk reserved for Tuesday, 10:00 AM.
This area is at 80% capacity for that slot.
That desk is no longer available.
Field & dropdown
Form primitives
component
Office
Berlin · Mitte
Floor
Select a floor
3 floors available
Time slot
10:00 – 13:00
Slot overlaps an existing booking
Chip
Removable · dropdown · selectable
component
Hot desk ✕Available ✕Half-day ✕Restricted ✕Floor ▾Amenities ▾Sort ▾
Area card
Composite · most-used in booking
component
Engineering
Opens 7:00 PM
ABCD+1
13 desks available from 10:00 AM
Marketing
Half-day 1:00 PM
ABCD+1
6 desks available from 10:00 AM
Modal
Confirmation pattern
component
Do you want to reset your password?
×

We’ll send a recovery link to rui@deskbird.com. The current session will stay active until you sign back in.

07Documentation

We moved the docs into Figma. Adoption changed overnight.

Storybook served the engineering side, but designers rarely opened it and the content drifted from the live components. Once specs lived inside the component file, “is this allowed?” questions almost disappeared.

Components  ›  Inputs  ›  Checkbox
v2.4stable
Checkbox
Anatomy
  1. 1   Box   · 18 × 18 · radius.sm
  2. 2   Indicator   · inset 2 · stroke 2
  3. 3   Label   · text.body · space.2 gap
  4. 4   Focus ring   · 3px · action.primary @ 35%
Usage
Do. Use for binary or multi-select inside a list where each option is independent.
Don’t. Use a checkbox when only one option can be selected. Use radio instead.
Note. Indeterminate is reserved for parent rows in a hierarchy.
Variants
ExampleRole
DefaultUnselected
SelectedSelected
FocusKeyboard focus
DisabledDisabled
Component properties
NameTypeDefault
selectedbooleanfalse
disabledbooleanfalse
indeterminatebooleanfalse
labelstring
onChangefunction
68%

Drop in design-review questions after migrating docs into Figma

2 days

Average onboarding to ship a new screen, down from ~2 weeks

1 source

Specs, examples and properties living next to the component

08Engineering collaboration

The hardest decision wasn’t what to build. It was what to standardize.

I ran a bi-weekly sync with frontend leads. Not to review pixels, but to negotiate the line between hard-locked and intentionally flexible. The matrix below was the working contract.

SurfaceLevelRationale
Color, type, spacing, radiusLockedTokenized at source. No raw values shipped.
Button, Input, Modal, ToastLockedSingle implementation. Behavior is the contract.
Empty states, illustrationsTokenizedShared library, copy per surface.
Page layout & gridTokenizedDensity, gutters and breakpoints are fixed.
Marketing pagesFlexibleBrand-led. Uses tokens but composes freely.
Admin data tablesFlexibleDensity and columns owned by feature teams.
09PrimeNG mapping

A vendor library landed mid-roadmap. We treated it as a constraint, not a setback.

Frontend moved to PrimeNG. Rebuilding every component on top of it would have burned the team. Instead, we defined a three-layer model so adoption could continue without forcing technical taste into a corner.

Layer 1
Native PrimeNG

Used as-is when the default behavior matches our spec. Faster delivery, lower surprise.

Layer 2
Themed PrimeNG

Restyled via tokens to match our system. Behavior inherited, surface owned.

Layer 3
Deskbird custom

Built from scratch when the contract or composition couldn’t be met otherwise.

The key: stopping the “rebuild everything” reflex and maintaining a clear contract for what lives in each layer. Every decision was reviewed and documented, so even when it had to move between layers, the transition was a known one.

10Multi-platform scaling

One system. Seven surfaces. Same primitives.

Once tokens were in place, expansion stopped being a redesign. Adding a new surface meant mapping tokens to the host platform, not rebuilding the system.

Web app
Booking & admin
Admin
Reporting & policies
iOS
On-the-go booking
Android
On-the-go booking
Tablet
Receptions, wall-mount
Kiosk
Check-in surfaces
Room display
Meeting-room TV
Slack / MS Teams
Quick booking
Flow
Figma variables
Source of truth
Style Dictionary
Build pipeline
Platform outputs
CSS · iOS · XML · Slack JSON
11Governance

Intake, contribution, deprecation. The boring infrastructure that made it last.

A system without governance becomes folklore. Three lightweight processes kept the bar high without slowing teams down.

Request
  • · Linked from Figma + Storybook
  • · Triage weekly · 30 minutes
  • · Decision: adopt · defer · custom
Contribute
  • · Async RFC + Figma PR
  • · Pairing slot if needed
  • · Versioned release & changelog
Deprecate
  • · Marked legacy in Figma + code
  • · Migration guide per change
  • · Sunset within 2 release cycles
12Adoption

What actually changed in the system.

I stopped tracking dashboards of percentages early on. The signals that mattered were structural: what teams stopped arguing about, what disappeared from PRs, what became the default path of least resistance.

Shared primitives across seven surfaces

Web app, admin, iOS, Android, kiosks, room displays and Slack/Teams all rendered to the same source of truth and the same core primitives.

Tokenized foundations replaced hardcoded values

Color, spacing, radius and typography stopped being inline constants. Replacing the theme or rebrand was a config change, ahead of a release.

Documentation moved out of Storybook into Figma

Less drift, less rework. Anatomy, behavior and engineering details lived alongside the component, where the actual work was done.

Governance embedded into weekly rituals

Intake reviews and contribution sessions kept a repeatable rhythm, never blocking but never letting the system run off into entropy.

UI drift across surfaces visibly reduced

Cross-platform reviews stopped flagging the same spacing, color and component inconsistencies. Visual cohesion became business as usual.

New screens stopped being net-new design work

Most product work became a reassembly of existing patterns. Cycle time shrank, quality bar went up.

Adoption curve · 2 functional, 1 composite reuse rate, production2021 → 2024
Q1 21Q2 22Q3 23Q4 24Q1 25
Composite reuse · Primitive reuse · Adoption rate across active production surfaces
13Outcomes

What changed for the people using it.

Designers stopped negotiating the basics

Conversations moved away from spacing and color towards flow, hierarchy and customer value. The system absorbed the foundations, freeing the team’s attention.

Engineering shipped without rework cycles

Token-backed components meant a feature branch never landed in the WIP limbo. Behavior became a config concern instead of multi-team rewrites.

New surfaces launched at the same quality bar

Kiosk and room displays launched in months instead of quarters. UX parity arrived naturally because the underlying primitives didn’t fork between surfaces, only composed differently.

Onboarding compressed

A new product designer could ship their first production-quality screen in their first week, because the system was the path of least resistance, not the exception.

14Reflection

The lessons that stayed with me.

“A design system isn’t a library of components. It’s a collection of decisions the team has agreed to stop re-debating.”

  • Start with intake and governance. Components come second. Without them, people stop trusting the system. Surface bias, choices have a cost.
  • Treat constraints, vendor libraries, platform differences, as invitations to define what belongs in the system and what doesn't.
  • Tokens earlier than feels necessary. The compounding return is enormous.
  • Documentation only works at the artifact. Anywhere else and it shifts. Adoption is the only metric that matters. Coverage without adoption is theatre.