Back to work

Building OTKit: From Drift to Discipline

Built and scaled OpenTable's design system: shared tokens, component patterns, and contribution workflows across web and native platforms.

Design SystemsTokensMulti-platform
Company
OpenTable
Year
2022 – 2024
Role
Lead Product Designer, Design Systems - Product Owner
Timeline
~1 year focused engagement
OTKit Design System hero: overview of the design system River built at OpenTable spanning iOS, Android, and web
Less QA time
25%
Recovered
≈$98K/yr
Diner bookings
+2.19%
Teams
6 product teams · iOS, Android, Web
Shipped
Tokens, type + icon systems, a brand refresh

OTKit existed on paper but lacked cohesion, governance, and trust. The mandate: turn a drifting system into an operational platform teams actually relied on. It served three users at once.

  • The diner books a table in seconds, once a month. Consumer-grade craft, fast by default.
  • The front-of-house host runs the floor from an iPad, eight hours a shift. Density, zero tolerance for error.
  • Design and engineering — six teams consuming tokens, components, docs. The system succeeds when their work gets faster.

Three sources of truth, ≈$390K a year

Drift was expensive and invisible. Three sources of truth — Storybook, Figma libraries, inline docs — were reconciled by hand, separately, per team, producing visual inconsistency across products and late-stage QA bugs tied directly to token and component mismatch.

Screenshots of multiple Storybooks and documentation source-of-truth websites before consolidation
Design system drift across OpenTable products before consolidation.

We put the cost of drift at ≈$390K a year in visual QA time, across design and engineering.

Costed from a 37-person survey: ~5 engineering hours a week across 14 people (≈$273K), ~3 design hours across 10 (≈$117K), at $75/hour. Naming it in dollars is what made the work fundable.

The survey also found where it hurt most. 100% of iOS designers were creating one-off components often or occasionally, against 60% on web — unsurprising given a mature web library (Buffet) and nothing comparable for native. And 75% of Restaurant web designers spent 4–8 rounds per release clarifying styling with engineering, where 100% of Diner web designers needed 0–3.

I think iOS lacks the rich component system that web has (Buffet), and that poses some challenges for us.

Restaurant iOS engineer

Interviews surfaced the same thing from both sides: unclear documentation, scattered sources of truth, no shared component language. Much of the style guide lived in designers' heads.


Foundations first, without stopping delivery

Six teams were shipping in parallel, on legacy patterns embedded in production, across three platforms. Nothing could pause. So the order was tokens before components, accessible colour and typography first, and documentation treated as a system artifact rather than an afterthought — the layer teams could adopt inside an active codebase without a rewrite.

Colour: twenty-two states, eleven tokens

I audited every colour cross-platform, deprecated the redundant ones, and rebuilt the system around accessible semantic tokens with mixed light/dark theming for existing UI. That alone fixed actions that read as disabled and closed platform-parity gaps.

Figma Variables panel showing OTKit foreground color tokens: semantic roles (default, alt, disabled, action, success, info, warning, danger) mapped across Light and Dark themes with primitive references
Primitive → Semantic → Component. The three-tier contract that made OTKit's theming scale without forking.

22 reservation statuses needed colour, plus data viz, plus white-label theming. Hyper-specific tokens read precisely but couldn't scale across two products, so statuses bind to the semantic accent ramps instead: eleven tokens carry all twenty-two states. Shipped on time, zero new colours.

Blumenthal, party of 47:30 PM · Table 12 · Window

Reservation status: Booked

Live component, rebuilt from the OTKit source. Every state maps to an existing semantic token.

The floor plan is the denser half of the same screen. Labels sit *on* the colour, so each tile pairs its fill with its own `foreground/on-*` token to stay legible.

Floor plan · table status

Every label uses its background's foreground/on-* token — all 21 clear WCAG AA.

Select a tile to inspect its background token, label token, and live contrast ratio.

Each tile's label is its background's foreground/on-* token. The lighter drinks fills pair with foreground-default and course 4 uses a darker accent-teal, so every label clears WCAG AA — no new colors. Every glyph is the real OTKit vector.

The floor-plan counterpart to the reservation button. Select a tile to inspect its tokens and live contrast ratio.

Type: thirty-nine fonts, one scale

The old scale was one-size-fits-all: not mobile-optimized, encouraging incorrect font usage in native apps, and ballooned to 39 fonts in the codebase. The replacement was contextual — size, weight and family defined per platform context, plus Apple Dynamic Type, 44px minimum touch targets, and the same tokens as web. A/B testing showed a +2.19% increase in diner bookings, about +600 weekly net bookers, on pages using dynamic type.

Animation of dynamic text sizing implemented on iOS
Contextual typography scale showing size, weight, and family defined per platform context
The contextual type scale reduced 39 font variants to a purposeful, platform-aware system.

Icons: one grid, every glyph

Shape-based grids gave one optical size and equal optical area across keyshapes, with the drawing rules set explicitly: 1.5px stroke replacing the deprecated 2.0, 2dp corners outside and square inside, unified keyshapes for indicators, centralized repositories, and SF Symbol parity for accessibility.

Animation of shape-based icon creation grid with keyshapes

The rules had reasons. The 2dp outer corner echoes the OpenTable logo; detail is a function of size, answering a five-second recognition test, which is why 24px earns tick marks that 16px cannot afford. The result was a searchable, themeable SVG system shared across native and web.

Screenshot of searchable icon library on documentation site

"Black is the new red"

OpenTable competes in an industry drenched in red and orange. Partnering with Brand, we translated a premium direction into tokenized theming rather than one-off visual treatments: two new families (Nantes for editorial display, Haffer for product UI, Brandon retired), a refreshed palette, and visual distinction for Iconic restaurants.

Every text style already pointed at one family token, so the swap was a re-binding, not a redraw — two variable fonts replaced six static cuts. The one judgement call was the CTA: 570, not the 600 the old scale implied, tuned to Haffer's optical weight.

OTKit type specimen: Nantes set as editorial display reading 'Find your table for any occasion', above Haffer's weight axis showing 430 body, 530 label, 570 title and 600 CTA, with a slider marking 570 as the shipped CTA weight between 400 and 620, and a product-UI row of Haffer buttons
Nantes for editorial display, Haffer for product UI.

The name was the argument. Red meant error, and it was also our primary action: one hue doing two contradictory jobs. Black solved both, giving Iconic its elegance and freeing red to mean danger again. Every black-button state was checked against AA, not just the default.

Checking states instead of defaults is what turns up real problems. `background/action-hover` resolves to the lighter step of the brand ramp, so a white label on a hovered primary button measures 3.24:1 on Restaurant teal and 3.57:1 on Diner red. AA asks for 4.5:1 and makes no exception for hover. Only disabled controls are exempt. The demos on this page render the real token rather than a safer substitute, so that state fails here too, and this site's contrast gate reports it as an accepted failure rather than quietly passing.


One component, three brands

Pick a brand — only four variables move

Two MICHELIN Stars

Lazy Bear

American · Mission, San Francisco · $$$$

6:457:308:15

Reserve for 7:30 PM

Brand collection · the only diff

  • bg-action#2D333F
  • bg-action-hover#575C66
  • bg-action-pressed#141A26
  • bg-alt#F2EFE6

6 variables re-point per brand. The other 278 are shared.

The button never names a brand — it asks for these; the collection decides what they resolve to.

The same card under all three OTKit brand collections. The markup, the components, and the other 278 tokens never change.

Mass serves the core audience: editorial in feel, two-column, booking surfaced immediately. Iconic is the premium tier: full-bleed photography, dark wine-toned time slots, sticky booking. Same tokens, same components, different values — which is why the brand shift landed without teams rewriting anything.

Iconic Theme: premium experience with full-bleed imagery, icon badges, and dark-wine timeslots

See it live: browse the Icons collection of premium restaurants, or open an individual Iconic restaurant page (Rich Table, San Francisco) to see the theme in production.


Adoption came through advocacy, not enforcement

Two people can't scale to six teams — a designer–engineer pair per platform could. Each pair championed the system locally and carried friction back; contribution ran one path every time: propose, review, merge. That friction fed a quarterly roadmap to the director, keeping the cost of drift visible to the people funding it.

Photo from OTKit design ambassadorship program
Design ambassadors in product teams acted as system advocates and real-world feedback channels.

Slack buried system updates in noise, so every release shipped an email instead: what changed, why, a demo, how to migrate. Every other team started one after this. Trust came from follow-through — issues answered fast, breaking changes migrated for teams — and that turned skeptics into advocates.

OTKit release-notes email: a Foundation v1.0.1 announcement headed 'Variables are now in Figma', with links to a theming guide and recorded training sessions, and an embedded 38-second demo of theme modes switching live
One release, explained. A direct line to the system's users.

I wanted to mention how helpful it has been having River support me on the design systems front. His depth of knowledge has been invaluable for our team.

Jordon · Restaurant Design Ambassador

What it returned

  • 50% fewer Figma libraries
  • 27% lift in clarity around Figma component sources, in the follow-up survey
  • One design language across 6 teams, and a full brand refresh absorbed without a rewrite

The ≈$98K a year is that 25% QA reduction, priced against the $390K drift was costing. What actually changed was the mindset: the system became something teams relied on rather than worked around. That's the difference between a library and a platform.


What I'd do differently

We started before Figma shipped Variables, managing tokens through naming conventions and manual syncing; we migrated fast when Variables arrived, but the gap cost early momentum. With teams across North America, Europe and Asia, context didn't travel — the newsletter became the most effective channel and I'd have started it much earlier. And system work kept getting pulled into feature support; ambassadors helped but their hours were a real cost against product delivery. Formal sessions worked at first, but what drove adoption was loosening them into jams. I'd draw all three lines earlier.