System Thinking Behind the Interface: Designing a Scalable UI Architecture for Airline Survey Analysis
System Thinking Behind the Interface: Designing a Scalable UI Architecture for Airline Survey Analysis
Empowering airline teams to uncover passenger sentiment trends and drive smarter decisions with clarity and speed
Introduction
Introduction
To comply with non-disclosure agreement, I have omitted and obfuscated confidential information in this case study. All information in this case study is my own and does not necessarily reflect the views & facts of the company.
To comply with non-disclosure agreement, I have omitted and obfuscated confidential information in this case study. All information in this case study is my own and does not necessarily reflect the views & facts of the company.
Challenge
Challenge
While working on the Passenger Sentiment Dashboard for an airline-focused analytics platform, I faced a challenge that went far beyond UI polish: the product needed to translate raw survey data into clear, actionable insights—fast. But more critically, the UI needed to be modular, scalable, and built to adapt as new feedback types, metrics, and use cases evolved.
The product team wanted to reduce analysis time, and the design had to support that shift without becoming brittle or inconsistent.
While working on the Passenger Sentiment Dashboard for an airline-focused analytics platform, I faced a challenge that went far beyond UI polish: the product needed to translate raw survey data into clear, actionable insights—fast. But more critically, the UI needed to be modular, scalable, and built to adapt as new feedback types, metrics, and use cases evolved.
The product team wanted to reduce analysis time, and the design had to support that shift without becoming brittle or inconsistent.
The System-Driven Solution
The System-Driven Solution
To meet this challenge, I built a fully tokenised, variable-based design system in Figma—one that supported every layer of the dashboard UI, from colours and spacing to inputs, controls, and data states. Each component was built with system logic in mind:
Semantic colour tokens for data states (positive, neutral, negative)
Number-scale-driven spacing for consistent layout rhythm
Input fields, dropdowns, buttons—all driven by component tokens
Scoping and naming conventions that made developer handoff effortless
This case study walks through how that system was architected—not as a design artefact, but as a thinking model: one that enabled fast decision-making, consistent evolution, and cross-functional clarity at scale.
To meet this challenge, I built a fully tokenised, variable-based design system in Figma—one that supported every layer of the dashboard UI, from colours and spacing to inputs, controls, and data states. Each component was built with system logic in mind:
Semantic colour tokens for data states (positive, neutral, negative)
Number-scale-driven spacing for consistent layout rhythm
Input fields, dropdowns, buttons—all driven by component tokens
Scoping and naming conventions that made developer handoff effortless
This case study walks through how that system was architected—not as a design artefact, but as a thinking model: one that enabled fast decision-making, consistent evolution, and cross-functional clarity at scale.
Duration
2 weeks
My Role
UX Designer
Contribution
Tokenised design system; UI components; Colour system; UX strategy; Token mapping; Documentation
Team
Data Analyst; Data Engineer; Product Owner
Colour system (using variables and tokens)
From base colours to semantic meaning across surfaces, borders, and text
After defining our base colour palette and grayscale system, the next step was to create semantic colour tokens—a bridge between raw values and component-level usage. These tokens enable designers and engineers to apply colours like surface/default or text/caption without needing to remember hex codes or visual intent.
Why this matters:
A semantic system unlocks scalability. It removes the guesswork and supports theming, dark mode, and cross-product consistency—while still being flexible enough to evolve.
Strategic Palette Design
Each colour was developed from a 500-level base, then extended with shade values up to 950 and down to 50. The grayscale includes subtle hue shifts to avoid visual flatness—critical in modern UIs.
All tokens are named using a predictable structure (Colour/Blue/700), and all base colours were prepped in variables—even if not in active use—so future themes or feature modules can tap into them without friction.

Semantic Tokens: Bridging Visuals to Meaning
Here, I shifted from base colours to meaningful, abstracted tokens. Instead of assigning hex codes directly to components, I defined roles like:
Surface/Default
Border/Disabled
Text/Negative
These reference the core palette behind the scenes (e.g. Greyscale/700, Red/600, etc.). This abstraction allows:
Global theming with minimal change effort
Clearer handoff for devs and design QA
Future-proofing: swapping Text/Body from dark grey to white becomes trivial in dark mode

Token Mapping in Action
Each colour family (e.g. Purple, Red) follows the same semantic mapping structure—ensuring consistency across interaction states and brand extensions. I applied the same logic across all UI layers:
Surface tokens for background containers
Border tokens for outlines and dividers
Text/Icon tokens for legibility, emphasis, and accessibility
This allowed for easy reuse, predictable interaction design, and contrast tuning at a semantic level.

UX Thinking Behind the System
A colour system isn't just a palette—it’s a contract between design and code, and a tool for UI clarity.
Here’s what made this system truly effective:
Predictable contrast ratios (especially across surface + text pairings)
Accessible defaults built into the semantic structure
Effortless design QA—tokens speak for themselves (Text/Caption or Border/Darker)
Theming readiness baked in from the beginning
Number Scale & Radius Tokens
Creating a universal sizing system that scales across layout, components, and interaction design
To support consistent and reusable component design, I established a number-based scale using Figma variables. The idea was simple: define a core set of primitive values and give them human-readable size labels (e.g. XS, M, 2XL) to reduce guesswork and support component logic later on.
Rather than choosing arbitrary pixel values, I mapped everything to this unified scale—from spacing and padding to width, height, and border radius. This means fewer inconsistencies, easier updates, and a system that scales with product complexity.
Number Scale
The number scale starts at 2 and extends up to 999:
Lower values (2–24) cover small-scale spacing and detail-level UI
Mid-range (32–40) works for containers, cards, and modular layouts
999 ("Full") is reserved for stretch and full-width scenarios
Each step in the scale corresponds to tokens like 2XS, XS, M, 3XL, etc., allowing us to design with intent rather than pixels.

Applying Tokens to Radius
I then used the scale to create semantic corner radius tokens. Instead of defining custom corner values for every component, I assigned radius values like Radius/S, Radius/M, and Radius/Full.
This made it easier to:
Keep consistent rounding across buttons, inputs, and cards
Adjust radii system-wide from a single source of truth
Handle special cases (e.g. pills, circular avatars) with Radius/Full

Radius
XS
S
M
L
XL
Full
Extending the System: Padding & Layout
Padding, spacing, and layout constraints were also aligned to the same number tokens. This let me build clean, responsive components without constantly checking pixel values.
I scoped each token to its relevant use case in Figma (e.g. Gap, Corner radius, Text content, etc.), so designers only see relevant values based on context. That alone saved a ton of confusion and UI clutter.
This scoped approach allowed my team to work faster and more confidently—less scrolling through irrelevant tokens, more focus on structure and behaviour.


Key Insight: Design Systems Need Design Logic
When you're building a component library at scale, consistency isn’t just about the visuals—it’s about systemic thinking. This number scale system gave us:
A language to talk about spacing (“use M for default padding”)
A flexible structure that works across products and themes
A logic-driven foundation that scales from 1px tweaks to complex design handoff
Buttons: Tokenised, Responsive, and Built to Scale
Designing interactive components that are consistent, flexible, and easy to maintain
After building out the base colour system, number scale, and semantic tokens, I moved on to one of the most reused and critical components in any UI system: buttons.
Rather than treating buttons as isolated UI blocks, I approached them as token-driven templates—composed of interchangeable variables for size, padding, colours, and interaction states. This gave our team both visual consistency and practical agility.
Why Buttons Need to Be System-Smart
Buttons carry heavy responsibility. They guide action, convey hierarchy, and often set the tone for the rest of the product's UI. That’s why I engineered them around:
✳️ Component tokens – for structure (padding, icon size, radius)
🎨 Semantic colour tokens – for surface, border, and label text
🧩 Variants + properties – to cover states, sizes, and icons without redundancy
This setup ensures that every interaction is predictable, accessible, and easy to adapt—across screens, themes, or even different brands.
Component Tokens: Anatomy of a Button
Each button size (Small, Medium, Large) is defined through its own set of component tokens, including:
Radius – from Radius/S
Spacing – controlling gaps between text & icon
H-padding / V-padding – tokenised horizontal/vertical space
Full padding – used when applying padding uniformly
Icon Size – tied to the number scale
Rather than hardcoding these values in every variant, I linked each token to the previously defined Number Scale. That means if spacing standards change, the component updates system-wide with a single variable tweak.

Extending the System: Padding & Layout
For each button type (e.g. Primary, Secondary, etc.), I created colour tokens scoped by layer:
Surface/Default, Subtle, Lighter, Darker
Border/Default, Subtle, etc.
Text/Label
These tokens point back to foundational colours like Colours/Purple/500, enabling:
🔁 Theme swapping without redesign
⚖️ Predictable contrast and accessibility
🧼 Cleaner dev handoff (semantics > hex codes)

Button Variants: Interaction Without the Bloat
Using Figma’s component properties and variant logic, I covered all core use cases without creating visual clutter:
👆 States: Default, Hover, Pressed, Disabled
📏 Sizes: Small, Medium, Large
🖼 Icon presence: Start, End, Both, None
🔤 Text-only or filled buttons
By binding structure and style to tokens, each variant became an output of logic, not an isolated design artefact.
System Thinking Behind the Build
This wasn’t just about building “a button that looks good”—it was about creating a button that behaves well, plays nicely with others, and scales effortlessly.
Key principles applied:
Modularity over redundancy – Small changes don’t require full rework
Variables over overrides – Design decisions are made once, reused everywhere
Structure mirrors code – Devs can translate tokens to implementation with minimal ambiguity
Theming built-in – One button setup can support multiple brands or UI modes
Outcome
This button system became a template for interaction patterns across the product. Designers could move faster, devs had a clean map to implementation, and QA had a logical system to validate against.
It’s the kind of foundational UX infrastructure that doesn’t just look good—it makes everything else better too.
Inputs: Modular, Configurable, and Built with Tokens
Designing input fields that adapt to complexity without adding chaos
Input fields are deceptively simple. But in real-world products, they need to handle a variety of states (focus, error, disabled), sizes, icons, hints, labels, and accessibility concerns—often all at once.
Instead of designing one-off inputs, I approached them as flexible, token-powered components, made to scale across forms, modals, mobile screens, and edge cases.
Structured by Component Tokens
Each input is structured using its own component token set, tied directly to the global number scale. This gave me instant control over layout spacing, padding, radius, and icon sizing.
Radius → Radius/M
H-Padding → Horizontal space: Number Scale/2’s/L
V-Padding → Vertical space: Number Scale/2’s/XS
Spacing → Gap between label, text, and icons
Icon Size → Number Scale/2’s/2XL
These tokens ensured inputs visually aligned with buttons and other components, while still being flexible enough for size or theme tweaks. A design change in padding or icon size? Just update the token—no need to touch every variant.

Built for Flexibility, Powered by Properties
Using component properties, I enabled full control over what the input field shows without duplicating variants. Toggle-based controls let designers and developers:
Turn icons on/off (start or end)
Show or hide hint text
Update states (default, selected, error, success)
Inject label or input text inline
This meant we could support various use cases (e.g. search bars, date pickers, validation fields) within one single logic-driven component.

Token + Property Synergy = Scalable Forms
To manage state styling (e.g. error or success), I reused semantic colour tokens from the global system. For example:
Border/Error → Colours/Red/500
Border/Success → Colours/Green/500
Hint/Error → Colours/Red/700
This allowed:
🎨 Themeable validation feedback
✅ Accessible contrast by design
🔁 Reuse across any form without custom overrides
UX Thinking Behind the Build
My goal wasn’t just to design an input that looked polished—it was to build one that:
Works across edge cases without blowing up into 20 variants
Keeps dev handoff clean and modular
Follows WCAG alignment on spacing, contrast, and field labelling
Matches other components in feel and rhythm
This input structure became the template for forms across the system—unifying how we handle user data entry across web and mobile.
Outcome
With one master input component, we could build everything from simple forms to complex modals—without inconsistencies or maintenance debt. The tokens and properties made it future-proof, dev-friendly, and easy to extend.
It’s the kind of systemised interaction design that doesn’t just support product growth—it anticipates it.
Outcome & Reflections
By anchoring the dashboard in a scalable, token-driven system, we did more than just make it visually clean—we made it resilient to complexity. As new survey types, metrics, and customer segments were introduced, the design held its integrity. UI consistency remained intact. Developer velocity increased. And most importantly, the core goal was achieved: teams could now generate meaningful insights from survey data in under 5 minutes, with less cognitive friction and clearer decision paths.
This project reinforced something I deeply believe: great design isn’t just about what users see—it’s about what teams can build on.
Design systems aren’t just aesthetic frameworks; they’re decision frameworks. And when built with intent, they turn a reactive design process into a strategic asset.
Colour system (using variables and tokens)
From base colours to semantic meaning across surfaces, borders, and text
After defining our base colour palette and grayscale system, the next step was to create semantic colour tokens—a bridge between raw values and component-level usage. These tokens enable designers and engineers to apply colours like surface/default or text/caption without needing to remember hex codes or visual intent.
Why this matters:
A semantic system unlocks scalability. It removes the guesswork and supports theming, dark mode, and cross-product consistency—while still being flexible enough to evolve.
Strategic Palette Design
Each colour was developed from a 500-level base, then extended with shade values up to 950 and down to 50. The grayscale includes subtle hue shifts to avoid visual flatness—critical in modern UIs.
All tokens are named using a predictable structure (Colour/Blue/700), and all base colours were prepped in variables—even if not in active use—so future themes or feature modules can tap into them without friction.


Semantic Tokens: Bridging Visuals to Meaning
Here, I shifted from base colours to meaningful, abstracted tokens. Instead of assigning hex codes directly to components, I defined roles like:
Surface/Default
Border/Disabled
Text/Negative
These reference the core palette behind the scenes (e.g. Greyscale/700, Red/600, etc.). This abstraction allows:
Global theming with minimal change effort
Clearer handoff for devs and design QA
Future-proofing: swapping Text/Body from dark grey to white becomes trivial in dark mode


Token Mapping in Action
Each colour family (e.g. Purple, Red) follows the same semantic mapping structure—ensuring consistency across interaction states and brand extensions. I applied the same logic across all UI layers:
Surface tokens for background containers
Border tokens for outlines and dividers
Text/Icon tokens for legibility, emphasis, and accessibility
This allowed for easy reuse, predictable interaction design, and contrast tuning at a semantic level.


UX Thinking Behind the System
A colour system isn't just a palette—it’s a contract between design and code, and a tool for UI clarity.
Here’s what made this system truly effective:
Predictable contrast ratios (especially across surface + text pairings)
Accessible defaults built into the semantic structure
Effortless design QA—tokens speak for themselves (Text/Caption or Border/Darker)
Theming readiness baked in from the beginning
Number Scale & Radius Tokens
Creating a universal sizing system that scales across layout, components, and interaction design
To support consistent and reusable component design, I established a number-based scale using Figma variables. The idea was simple: define a core set of primitive values and give them human-readable size labels (e.g. XS, M, 2XL) to reduce guesswork and support component logic later on.
Rather than choosing arbitrary pixel values, I mapped everything to this unified scale—from spacing and padding to width, height, and border radius. This means fewer inconsistencies, easier updates, and a system that scales with product complexity.
Number Scale
The number scale starts at 2 and extends up to 999:
Lower values (2–24) cover small-scale spacing and detail-level UI
Mid-range (32–40) works for containers, cards, and modular layouts
999 ("Full") is reserved for stretch and full-width scenarios
Each step in the scale corresponds to tokens like 2XS, XS, M, 3XL, etc., allowing us to design with intent rather than pixels.


Applying Tokens to Radius
I then used the scale to create semantic corner radius tokens. Instead of defining custom corner values for every component, I assigned radius values like Radius/S, Radius/M, and Radius/Full.
This made it easier to:
Keep consistent rounding across buttons, inputs, and cards
Adjust radii system-wide from a single source of truth
Handle special cases (e.g. pills, circular avatars) with Radius/Full


Radius
XS
S
M
L
XL
Full
Extending the System: Padding & Layout
Padding, spacing, and layout constraints were also aligned to the same number tokens. This let me build clean, responsive components without constantly checking pixel values.
I scoped each token to its relevant use case in Figma (e.g. Gap, Corner radius, Text content, etc.), so designers only see relevant values based on context. That alone saved a ton of confusion and UI clutter.
This scoped approach allowed my team to work faster and more confidently—less scrolling through irrelevant tokens, more focus on structure and behaviour.




Key Insight: Design Systems Need Design Logic
When you're building a component library at scale, consistency isn’t just about the visuals—it’s about systemic thinking. This number scale system gave us:
A language to talk about spacing (“use M for default padding”)
A flexible structure that works across products and themes
A logic-driven foundation that scales from 1px tweaks to complex design handoff
Buttons: Tokenised, Responsive, and Built to Scale
Designing interactive components that are consistent, flexible, and easy to maintain
After building out the base colour system, number scale, and semantic tokens, I moved on to one of the most reused and critical components in any UI system: buttons.
Rather than treating buttons as isolated UI blocks, I approached them as token-driven templates—composed of interchangeable variables for size, padding, colours, and interaction states. This gave our team both visual consistency and practical agility.
Why Buttons Need to Be System-Smart
Buttons carry heavy responsibility. They guide action, convey hierarchy, and often set the tone for the rest of the product's UI. That’s why I engineered them around:
✳️ Component tokens – for structure (padding, icon size, radius)
🎨 Semantic colour tokens – for surface, border, and label text
🧩 Variants + properties – to cover states, sizes, and icons without redundancy
This setup ensures that every interaction is predictable, accessible, and easy to adapt—across screens, themes, or even different brands.
Component Tokens: Anatomy of a Button
Each button size (Small, Medium, Large) is defined through its own set of component tokens, including:
Radius – from Radius/S
Spacing – controlling gaps between text & icon
H-padding / V-padding – tokenised horizontal/vertical space
Full padding – used when applying padding uniformly
Icon Size – tied to the number scale
Rather than hardcoding these values in every variant, I linked each token to the previously defined Number Scale. That means if spacing standards change, the component updates system-wide with a single variable tweak.


Extending the System: Padding & Layout
For each button type (e.g. Primary, Secondary, etc.), I created colour tokens scoped by layer:
Surface/Default, Subtle, Lighter, Darker
Border/Default, Subtle, etc.
Text/Label
These tokens point back to foundational colours like Colours/Purple/500, enabling:
🔁 Theme swapping without redesign
⚖️ Predictable contrast and accessibility
🧼 Cleaner dev handoff (semantics > hex codes)


Button Variants: Interaction Without the Bloat
Using Figma’s component properties and variant logic, I covered all core use cases without creating visual clutter:
👆 States: Default, Hover, Pressed, Disabled
📏 Sizes: Small, Medium, Large
🖼 Icon presence: Start, End, Both, None
🔤 Text-only or filled buttons
By binding structure and style to tokens, each variant became an output of logic, not an isolated design artefact.
System Thinking Behind the Build
This wasn’t just about building “a button that looks good”—it was about creating a button that behaves well, plays nicely with others, and scales effortlessly.
Key principles applied:
Modularity over redundancy – Small changes don’t require full rework
Variables over overrides – Design decisions are made once, reused everywhere
Structure mirrors code – Devs can translate tokens to implementation with minimal ambiguity
Theming built-in – One button setup can support multiple brands or UI modes
Outcome
This button system became a template for interaction patterns across the product. Designers could move faster, devs had a clean map to implementation, and QA had a logical system to validate against.
It’s the kind of foundational UX infrastructure that doesn’t just look good—it makes everything else better too.
Inputs: Modular, Configurable, and Built with Tokens
Designing input fields that adapt to complexity without adding chaos
Input fields are deceptively simple. But in real-world products, they need to handle a variety of states (focus, error, disabled), sizes, icons, hints, labels, and accessibility concerns—often all at once.
Instead of designing one-off inputs, I approached them as flexible, token-powered components, made to scale across forms, modals, mobile screens, and edge cases.
Structured by Component Tokens
Each input is structured using its own component token set, tied directly to the global number scale. This gave me instant control over layout spacing, padding, radius, and icon sizing.
Radius → Radius/M
H-Padding → Horizontal space: Number Scale/2’s/L
V-Padding → Vertical space: Number Scale/2’s/XS
Spacing → Gap between label, text, and icons
Icon Size → Number Scale/2’s/2XL
These tokens ensured inputs visually aligned with buttons and other components, while still being flexible enough for size or theme tweaks. A design change in padding or icon size? Just update the token—no need to touch every variant.


Built for Flexibility, Powered by Properties
Using component properties, I enabled full control over what the input field shows without duplicating variants. Toggle-based controls let designers and developers:
Turn icons on/off (start or end)
Show or hide hint text
Update states (default, selected, error, success)
Inject label or input text inline
This meant we could support various use cases (e.g. search bars, date pickers, validation fields) within one single logic-driven component.


Token + Property Synergy = Scalable Forms
To manage state styling (e.g. error or success), I reused semantic colour tokens from the global system. For example:
Border/Error → Colours/Red/500
Border/Success → Colours/Green/500
Hint/Error → Colours/Red/700
This allowed:
🎨 Themeable validation feedback
✅ Accessible contrast by design
🔁 Reuse across any form without custom overrides
UX Thinking Behind the Build
My goal wasn’t just to design an input that looked polished—it was to build one that:
Works across edge cases without blowing up into 20 variants
Keeps dev handoff clean and modular
Follows WCAG alignment on spacing, contrast, and field labelling
Matches other components in feel and rhythm
This input structure became the template for forms across the system—unifying how we handle user data entry across web and mobile.
Outcome
With one master input component, we could build everything from simple forms to complex modals—without inconsistencies or maintenance debt. The tokens and properties made it future-proof, dev-friendly, and easy to extend.
It’s the kind of systemised interaction design that doesn’t just support product growth—it anticipates it.
Next project →
Colour system (using variables and tokens)
From base colours to semantic meaning across surfaces, borders, and text
After defining our base colour palette and grayscale system, the next step was to create semantic colour tokens—a bridge between raw values and component-level usage. These tokens enable designers and engineers to apply colours like surface/default or text/caption without needing to remember hex codes or visual intent.
Why this matters:
A semantic system unlocks scalability. It removes the guesswork and supports theming, dark mode, and cross-product consistency—while still being flexible enough to evolve.
Strategic Palette Design
Each colour was developed from a 500-level base, then extended with shade values up to 950 and down to 50. The grayscale includes subtle hue shifts to avoid visual flatness—critical in modern UIs.
All tokens are named using a predictable structure (Colour/Blue/700), and all base colours were prepped in variables—even if not in active use—so future themes or feature modules can tap into them without friction.


Semantic Tokens: Bridging Visuals to Meaning
Here, I shifted from base colours to meaningful, abstracted tokens. Instead of assigning hex codes directly to components, I defined roles like:
Surface/Default
Border/Disabled
Text/Negative
These reference the core palette behind the scenes (e.g. Greyscale/700, Red/600, etc.). This abstraction allows:
Global theming with minimal change effort
Clearer handoff for devs and design QA
Future-proofing: swapping Text/Body from dark grey to white becomes trivial in dark mode


Token Mapping in Action
Each colour family (e.g. Purple, Red) follows the same semantic mapping structure—ensuring consistency across interaction states and brand extensions. I applied the same logic across all UI layers:
Surface tokens for background containers
Border tokens for outlines and dividers
Text/Icon tokens for legibility, emphasis, and accessibility
This allowed for easy reuse, predictable interaction design, and contrast tuning at a semantic level.


UX Thinking Behind the System
A colour system isn't just a palette—it’s a contract between design and code, and a tool for UI clarity.
Here’s what made this system truly effective:
Predictable contrast ratios (especially across surface + text pairings)
Accessible defaults built into the semantic structure
Effortless design QA—tokens speak for themselves (Text/Caption or Border/Darker)
Theming readiness baked in from the beginning
Number Scale & Radius Tokens
Creating a universal sizing system that scales across layout, components, and interaction design
To support consistent and reusable component design, I established a number-based scale using Figma variables. The idea was simple: define a core set of primitive values and give them human-readable size labels (e.g. XS, M, 2XL) to reduce guesswork and support component logic later on.
Rather than choosing arbitrary pixel values, I mapped everything to this unified scale—from spacing and padding to width, height, and border radius. This means fewer inconsistencies, easier updates, and a system that scales with product complexity.
Number Scale
The number scale starts at 2 and extends up to 999:
Lower values (2–24) cover small-scale spacing and detail-level UI
Mid-range (32–40) works for containers, cards, and modular layouts
999 ("Full") is reserved for stretch and full-width scenarios
Each step in the scale corresponds to tokens like 2XS, XS, M, 3XL, etc., allowing us to design with intent rather than pixels.


Applying Tokens to Radius
I then used the scale to create semantic corner radius tokens. Instead of defining custom corner values for every component, I assigned radius values like Radius/S, Radius/M, and Radius/Full.
This made it easier to:
Keep consistent rounding across buttons, inputs, and cards
Adjust radii system-wide from a single source of truth
Handle special cases (e.g. pills, circular avatars) with Radius/Full


Radius
XS
S
M
L
XL
Full
Extending the System: Padding & Layout
Padding, spacing, and layout constraints were also aligned to the same number tokens. This let me build clean, responsive components without constantly checking pixel values.
I scoped each token to its relevant use case in Figma (e.g. Gap, Corner radius, Text content, etc.), so designers only see relevant values based on context. That alone saved a ton of confusion and UI clutter.
This scoped approach allowed my team to work faster and more confidently—less scrolling through irrelevant tokens, more focus on structure and behaviour.




Key Insight: Design Systems Need Design Logic
When you're building a component library at scale, consistency isn’t just about the visuals—it’s about systemic thinking. This number scale system gave us:
A language to talk about spacing (“use M for default padding”)
A flexible structure that works across products and themes
A logic-driven foundation that scales from 1px tweaks to complex design handoff
Buttons: Tokenised, Responsive, and Built to Scale
Designing interactive components that are consistent, flexible, and easy to maintain
After building out the base colour system, number scale, and semantic tokens, I moved on to one of the most reused and critical components in any UI system: buttons.
Rather than treating buttons as isolated UI blocks, I approached them as token-driven templates—composed of interchangeable variables for size, padding, colours, and interaction states. This gave our team both visual consistency and practical agility.
Why Buttons Need to Be System-Smart
Buttons carry heavy responsibility. They guide action, convey hierarchy, and often set the tone for the rest of the product's UI. That’s why I engineered them around:
✳️ Component tokens – for structure (padding, icon size, radius)
🎨 Semantic colour tokens – for surface, border, and label text
🧩 Variants + properties – to cover states, sizes, and icons without redundancy
This setup ensures that every interaction is predictable, accessible, and easy to adapt—across screens, themes, or even different brands.
Component Tokens: Anatomy of a Button
Each button size (Small, Medium, Large) is defined through its own set of component tokens, including:
Radius – from Radius/S
Spacing – controlling gaps between text & icon
H-padding / V-padding – tokenised horizontal/vertical space
Full padding – used when applying padding uniformly
Icon Size – tied to the number scale
Rather than hardcoding these values in every variant, I linked each token to the previously defined Number Scale. That means if spacing standards change, the component updates system-wide with a single variable tweak.


Extending the System: Padding & Layout
For each button type (e.g. Primary, Secondary, etc.), I created colour tokens scoped by layer:
Surface/Default, Subtle, Lighter, Darker
Border/Default, Subtle, etc.
Text/Label
These tokens point back to foundational colours like Colours/Purple/500, enabling:
🔁 Theme swapping without redesign
⚖️ Predictable contrast and accessibility
🧼 Cleaner dev handoff (semantics > hex codes)


Button Variants: Interaction Without the Bloat
Using Figma’s component properties and variant logic, I covered all core use cases without creating visual clutter:
👆 States: Default, Hover, Pressed, Disabled
📏 Sizes: Small, Medium, Large
🖼 Icon presence: Start, End, Both, None
🔤 Text-only or filled buttons
By binding structure and style to tokens, each variant became an output of logic, not an isolated design artefact.
System Thinking Behind the Build
This wasn’t just about building “a button that looks good”—it was about creating a button that behaves well, plays nicely with others, and scales effortlessly.
Key principles applied:
Modularity over redundancy – Small changes don’t require full rework
Variables over overrides – Design decisions are made once, reused everywhere
Structure mirrors code – Devs can translate tokens to implementation with minimal ambiguity
Theming built-in – One button setup can support multiple brands or UI modes
Outcome
This button system became a template for interaction patterns across the product. Designers could move faster, devs had a clean map to implementation, and QA had a logical system to validate against.
It’s the kind of foundational UX infrastructure that doesn’t just look good—it makes everything else better too.
Inputs: Modular, Configurable, and Built with Tokens
Designing input fields that adapt to complexity without adding chaos
Input fields are deceptively simple. But in real-world products, they need to handle a variety of states (focus, error, disabled), sizes, icons, hints, labels, and accessibility concerns—often all at once.
Instead of designing one-off inputs, I approached them as flexible, token-powered components, made to scale across forms, modals, mobile screens, and edge cases.
Structured by Component Tokens
Each input is structured using its own component token set, tied directly to the global number scale. This gave me instant control over layout spacing, padding, radius, and icon sizing.
Radius → Radius/M
H-Padding → Horizontal space: Number Scale/2’s/L
V-Padding → Vertical space: Number Scale/2’s/XS
Spacing → Gap between label, text, and icons
Icon Size → Number Scale/2’s/2XL
These tokens ensured inputs visually aligned with buttons and other components, while still being flexible enough for size or theme tweaks. A design change in padding or icon size? Just update the token—no need to touch every variant.


Built for Flexibility, Powered by Properties
Using component properties, I enabled full control over what the input field shows without duplicating variants. Toggle-based controls let designers and developers:
Turn icons on/off (start or end)
Show or hide hint text
Update states (default, selected, error, success)
Inject label or input text inline
This meant we could support various use cases (e.g. search bars, date pickers, validation fields) within one single logic-driven component.


Token + Property Synergy = Scalable Forms
To manage state styling (e.g. error or success), I reused semantic colour tokens from the global system. For example:
Border/Error → Colours/Red/500
Border/Success → Colours/Green/500
Hint/Error → Colours/Red/700
This allowed:
🎨 Themeable validation feedback
✅ Accessible contrast by design
🔁 Reuse across any form without custom overrides
UX Thinking Behind the Build
My goal wasn’t just to design an input that looked polished—it was to build one that:
Works across edge cases without blowing up into 20 variants
Keeps dev handoff clean and modular
Follows WCAG alignment on spacing, contrast, and field labelling
Matches other components in feel and rhythm
This input structure became the template for forms across the system—unifying how we handle user data entry across web and mobile.
Outcome
With one master input component, we could build everything from simple forms to complex modals—without inconsistencies or maintenance debt. The tokens and properties made it future-proof, dev-friendly, and easy to extend.
It’s the kind of systemised interaction design that doesn’t just support product growth—it anticipates it.
charlesjaja.dny@gmail.com
charlesjaja.dny@gmail.com
2025 Charles Jaja
2025 Charles Jaja
Say hello