Case Study
Quinine Design System
Tonic.ai
I built Quinine as Tonic's internal design system to replace weak early frontend patterns, UI drift, and painful legacy components with a modernized and cohesive product foundation. It became the default way we built product UI, reduced ad hoc CSS and misconfigured patterns, and made ordinary feature work much faster for engineers.
- React
- TypeScript
- Sass
- Storybook
- Figma
- Playwright
- Nx Monorepo
Overview
Quinine started because Tonic had accumulated UX drift from inconsistent design decisions and rushed early frontend implementation. Blueprint was part of the problem, especially where it locked us into specific React versions and contributed to awkward legacy UI, but the bigger issue was that the product suite no longer felt coherent in either design or engineering.
I built Quinine using lessons I had learned from Sprout at Kabbage, but the constraints were different. Tonic only needed React support, so instead of a CSS-first cross-framework layer, Quinine became a React library with strongly typed primitives and props that kept engineers inside the system instead of reaching for raw classes.
It started with simple components and forms, then expanded as products grew. Components like the table, select, and datepicker pushed the system forward, and once greenfield products like Ephemeral and Textual were built entirely on Quinine, it stopped being a promising library and became the default UI foundation.
The Problem
- Products had drifted apart visually and behaviorally over time, and a lot of early-stage frontend code had been written quickly enough that teams kept solving the same UI problems in slightly different ways.
- Blueprint contributed to the pain by locking us into specific React versions, weakening Tonic's brand cohesion, and leaving behind legacy abstractions that were cumbersome to evolve.
- The old DataTable, built on top of Blueprint Table, was especially painful: it was so hard to use that engineers mostly copied previous instances and tweaked them, which led to widespread misconfigured tables and a lot of accidental inconsistency.
- We needed a system that improved consistency and product quality without forcing teams through a separate platform rewrite or slowing down delivery.
My Role
I owned Quinine end to end. I was the sole developer until the system reached MVP, remained the primary architect and contributor afterward, and worked closely with design to define the core tokens, primitives, and system concepts together.
Key Contributions
- Defined Quinine's architecture and the core philosophy of guardrails first, escape hatches when real product complexity required them.
- Built the early primitives, forms layer, and layout abstractions that made ordinary product work simpler and more consistent.
- Designed major component APIs, especially the table system, along with smaller but still difficult components like select and datepicker.
- Worked directly with design to define the token surface area and the concepts the system should encode.
- Guided the page-by-page migration away from Blueprint and other legacy patterns while keeping feature work moving.
- Led the later migration to fully semantic tokens as Quinine matured into the default frontend foundation.
Architecture
Quinine was a typed React UI foundation made up of primitives, higher-level components, forms and layout abstractions, design tokens, and CSS-variable-driven theming seams. It was intentionally opinionated so product engineers rarely needed native HTML or custom CSS for common work.
Implementation Notes
- Quinine was consumed as a React library, not as a CSS-first or cross-framework layer.
- Internally it used utility-style styling, but that layer was home-built and abstracted behind components rather than exposed directly to product teams.
- CSS variables created a controlled seam for overriding design-system defaults without throwing teams back into ad hoc styling.
- Forms, layout primitives, and component APIs were built as pit-of-success abstractions so the easiest path was usually the correct one.
Key Technical Decisions
React-Library-First System
Quinine deliberately chose a typed React-library model rather than a CSS-first shared layer because Tonic only needed React support. That let engineers stay inside expressive component APIs instead of composing raw classes.
Benefits
- Stronger day-to-day ergonomics for product engineers.
- Much less ad hoc CSS.
- Less drift in spacing, typography, and layout patterns.
Trade-off: The system was intentionally opinionated, which meant it needed carefully designed escape hatches whenever product reality exceeded the default abstractions.
Guardrails + Escape Hatches
The core product philosophy was to make the common path intuitive and safe while still permitting edge cases. Most APIs were deliberately constrained, but Quinine always needed a way to drop into lower-level control when real use cases demanded it.
Benefits
- Most teams could use simple, consistent APIs for everyday work.
- Complex cases could stay inside the system instead of forcing one-off rewrites.
- Review burden dropped because correct usage was easier by default.
Trade-off: Designing escape hatches without collapsing the guardrails required constant judgment and iteration.
QuiTable + useQuiTable
Quinine replaced the old Blueprint-based DataTable with a more intuitive model. QuiTable had a heavily guardrailed top-level API, but also exposed an options prop as an escape hatch to the full underlying react-table API. Table configuration was built through a builder and passed into useQuiTable, which returned props ready to spread onto QuiTable, avoiding the usual giant flat prop surface.
Benefits
- Common table use cases became dramatically easier to configure.
- Special cases could still follow react-table documentation without missing functionality.
- Query concerns like sorting, pagination, and filtering stayed decoupled from presentation through a shared Query abstraction.
- Teams no longer had to memorize sprawling flat prop lists.
Trade-off: Designing a table API that was both flexible and comprehensible took substantial iteration, especially around column configuration, auto/fixed/resizable sizing, row prop selectors, renderer functions, and customizable loading states with skeletons and transitions.
Forms + Layout Primitives As Ergonomics Infrastructure
Quinine's biggest day-to-day win was simplifying ordinary product work. Composable fields and inferred form structure using final-form removed a lot of repetitive wiring, while QuiFlexH, QuiFlexV, QuiGrid, and QuiContainer covered nearly every layout pattern we needed, including responsive breakpoints.
Benefits
- Much less repetitive form wiring.
- Much less hand-written layout CSS.
- UI structure and spacing were more consistent by default.
Trade-off: The system had to keep broad enough coverage that engineers rarely needed to drop down to raw HTML or one-off styling.
Semantic Tokens As A Maturity Chapter
Later in Quinine's life, we migrated to fully semantic tokens. The largest audit category by far was color: there were many ad hoc values with no tokens at all, and many cases where a single raw token had to split into two tokens because the uses were semantically different. Components used semantic CSS variables heavily, which created a controlled seam for theming and overrides.
Benefits
- Cleaner and more flexible theming and component variants.
- More durable semantic clarity across the system.
- A stronger foundation for future customization.
Trade-off: The audit and refinement work was extensive because nearly every non-semantic value in the system had to be revisited. As a litmus test, I used ChatGPT to generate our first dark mode from the semantic token surface area alone; if it could make the app look coherent from token names, the model was doing its job.
Challenges
- Replacing legacy pain without freezing feature work required a page-by-page migration instead of a clean rewrite.
- The APIs had to stay opinionated enough to create consistency while still handling product edge cases when they appeared.
- Keeping the system broad enough that engineers rarely needed raw HTML, custom CSS, or one-off styling created constant pressure to expand coverage thoughtfully.
- Migrating off Blueprint and legacy abstractions gradually meant Quinine had to coexist with old patterns for a long time.
- Later, the semantic-token migration required auditing and refactoring a large amount of previously non-semantic styling, especially color usage.
Results
- Quinine became the default way Tonic built product UI. All products were migrated successfully.
- Greenfield products like Ephemeral and Textual were built entirely on Quinine, which proved the system could support real product development end to end.
- Engineers moved away from ad hoc CSS and legacy copy-paste UI patterns.
- Feature work became faster because forms, layout, and common UI patterns were easier to compose correctly.
- Tonic products achieved much stronger brand cohesion and implementation consistency.
- Even with simplified API surfaces and opinionated docs, teams still coalesced on their own unique Quinine patterns when isolated. Some of these influenced the direction of Quinine, improving it for everyone.
What I Learned
- The most valuable design systems are the ones that reduce ordinary feature friction by doing more with less. Consistent APIs are everything.
- Guardrails are an invaluable tool in sculpting APIs that are intuitive to use, but they only work if there's an effective escape hatch pattern for necessary deviations that come with real product complexity.
- Teams may still define their own patterns even within an opinionated ecosystem, and this can be a good thing.
- Managing large prop surfaces is a necessary challenge in component libraries. Dedicated hooks can be a better home than components for carrying complex configuration.
- Semantic tokens matter most once the system has stabilized enough to be able to define them well.
- Design systems work best when design and engineering define the concepts together instead of handing work across a boundary.