Designing Scalable Design Systems for Multi-Platform Products

How to build a design system that works across web, iOS, and Android while keeping a single source of truth for tokens, components, and documentation.
A design system is the foundation on which consistent, scalable product experiences are built. Done well, it accelerates development by providing pre-built, tested components; ensures visual consistency across surfaces; and creates a shared language between designers and engineers. Done poorly, it becomes a maintenance burden that teams route around. This guide covers the architecture decisions that determine which outcome you get. ## The Three Layers of a Design System A robust design system has three layers: Design Tokens, Component Library, and Documentation/Governance. **Design Tokens** are the primitive values—colors, typography scales, spacing units, border radii, shadow definitions, animation durations—stored as platform-agnostic data (JSON or YAML). Tokens are the bridge between design tools (Figma) and code. When a designer changes "primary-500" from `#2563EB` to `#1D4ED8`, that change propagates to web CSS custom properties, iOS Swift constants, and Android XML resources through a single update. **Component Library** is the collection of reusable UI components built on top of tokens. Components encode interaction patterns, accessibility semantics, and responsive behavior. They are consumed by product teams to build features without reinventing UI primitives. **Documentation and Governance** determines adoption. The best component library in the world fails if teams don't know it exists, can't find the right component, or don't trust the documentation. Governance defines who can contribute to the system, how components are reviewed, and how breaking changes are communicated. ## Design Token Architecture Use a tiered token architecture to maintain flexibility: ```json { "global": { "color": { "blue-500": { "value": "#2563EB" }, "blue-600": { "value": "#1D4ED8" } }, "spacing": { "4": { "value": "16px" }, "6": { "value": "24px" } } }, "semantic": { "color": { "action-primary": { "value": "{global.color.blue-500}" }, "action-primary-hover": { "value": "{global.color.blue-600}" } } }, "component": { "button": { "background": { "value": "{semantic.color.action-primary}" }, "background-hover": { "value": "{semantic.color.action-primary-hover}" } } } } ``` Global tokens define raw values. Semantic tokens assign meaning. Component tokens map component properties to semantic tokens. This tiering means that when you rebrand from blue to green, you change one global token and the entire system cascades correctly. Tools like Style Dictionary transform token JSON into platform-specific outputs: CSS custom properties for web, Swift/Obj-C constants for iOS, XML resources for Android, and Kotlin constants for Android Compose. ## Component API Design Component APIs must balance flexibility with consistency. Too many props create inconsistency (teams compose components in unexpected ways); too few make the component unfit for edge cases. **Slot-based APIs** provide structural flexibility without prop explosion. A `Card` component with `header`, `body`, and `footer` slots can accommodate diverse content types while enforcing consistent padding and border structure. **Variant patterns**: Use explicit variant props rather than allowing arbitrary style overrides. `variant="primary" | "secondary" | "ghost"` is clearer and more controlled than accepting a `className` that overrides any style. **Composition over configuration**: Build small, focused primitives that compose into complex components rather than building omnibus components with 40 props. ## Cross-Platform Component Strategy True cross-platform design systems acknowledge that web, iOS, and Android have different interaction models, navigation paradigms, and user expectations. A "write once, render everywhere" approach often produces experiences that feel native nowhere. Preferred approach: **shared tokens and patterns, platform-native implementations**. Tokens (colors, spacing, typography scale) are truly shared. Interaction patterns (form validation, error states, loading states) are standardized. But the actual component implementations are native—React components for web, SwiftUI views for iOS, Jetpack Compose for Android. This gives users the native feel they expect while ensuring visual and behavioral consistency within each platform's conventions. ## Documentation That Drives Adoption Use Storybook for interactive component documentation. Each component should have: - A default story showing the component in its standard configuration - Stories for every variant and state (loading, error, disabled, with long text, with icons) - An "accessibility" story that demonstrates screen reader behavior and keyboard navigation - A "do/don't" visual guide for usage guidance Automate documentation where possible: generate prop tables from TypeScript types, screenshot all stories for visual regression testing, and run accessibility audits (axe-core) against every story in CI. ## Governance and Contribution Define a clear contribution process: - **Proposal stage**: Teams propose new components via RFC (Request for Comments), documenting the use case, API design, and accessibility requirements - **Design review**: Design system designers review for visual consistency - **Engineering review**: Design system engineers review the implementation for API quality, accessibility, and test coverage - **Deprecation process**: Old components are deprecated with a migration guide and a sunset date, not deleted immediately Track adoption metrics: which components are imported most frequently, which are rarely used (candidates for deprecation), and which teams haven't adopted the system (need help or have legitimate unmet needs).
