← Back to Work

What happens when Spark has no shared components?

Spark Design System gave the team a shared foundation for the controls they use every day — from Figma patterns and Tailwind variables to tested Vue components in Storybook. The system was built iteratively, starting with what Spark already used and refining it with the people shipping it.

Role
Design systems
& product design
Built
Figma system +
Vue Storybook
Impact
Shared decisions,
built in
The system

Spark Design System
built in layers.

Spark was built in layers: shared foundations such as colour, type and spacing; reusable components built from them; and patterns that bring those components together in real product flows. Each layer gives the next a clearer starting point, while leaving room to refine the system as Spark evolves.

The system grew from shared foundations, into reusable components, then into patterns for real product work. Lift any layer clear of the stack.
The brief

No Design System. No Source Of Truth.

Spark had grown through many local decisions: inconsistent front-end patterns, no dependable component record and no library of real components to build from. Other Netcall teams had embryonic design systems of their own, but Spark could lead a more joined-up way of working between design and front-end development. The task was to understand what existed, define the common controls, and create a component library the team could trust and reuse.

01

No Shared Inventory

There was no reliable record of the components already in use, so the team repeatedly recreated buttons, inputs and cards.

02

Repeated Components, Endless Names

Similar controls were named and implemented differently across Spark, making them hard to find, compare or reuse.

03

Decisions Without Guidance

Without shared strategy or production components, design and engineering decisions were made screen by screen — including accessibility.

Spark · Component library
Spark design system component library sheet: buttons, form controls, navigation, data and workflow, status and feedback, AI and rich content, and overlay components— view full size

A working inventory of the component families Spark needed most. The work started with the shared controls the team and Spark users encountered every day. Hover the markers.

Start with what people use every day

Accessibility no longer
an afterthought

Spark started with the CTAs, buttons and controls people use every day. Each was mapped in the states and contexts it needed to support, then refined with the team building Spark.

That made accessibility part of the work, not an afterthought. Contrast, focus, keyboard behaviour and clear error feedback were designed into the component as it was made — not added once it had already spread across the product.

Text input · Primary button
Spark · Form controls Default
Workflow name
Onboarding — tier 2
Save changes Cancel
Tokens that produce it
  • surface/default/base
  • border/default/strong
  • content/default/primary
  • surface/brand/strong
  • surface/brand/base not for a filled label
Measured contrast

Field text on surface/default/base

14.64 Passes AA 4.5:1

Button label on surface/brand/strong

5.14 Passes AA 4.5:1

Ruled out — button label on surface/brand/base

3.43 Below AA 4.5:1

A filled button takes the strong step of the brand ramp, not the base step, because the base step carries white at 3.43:1. The team never has to weigh that choice again. They ask for a primary button.

Spark · Form controls Hover
Workflow name
Onboarding — tier 2
Save changes Cancel
Tokens that produce it
  • surface/default/hover
  • border/default/strong
  • surface/brand/strong-hover
Measured contrast

Button label on surface/brand/strong-hover

7.17 Passes AA 4.5:1

Hover only ever moves down the same ramp, so a hovered control can never end up lighter than the one beside it. It also never carries meaning on its own: there is no hover on a touch device and none for a keyboard, so anything only hover says is unsaid for them.

Spark · Form controls Focus
Workflow name
Onboarding — tier 2
Save changes Cancel
Tokens that produce it
  • focus/ring/brand
  • focus/ring/offset
  • border/default/strong
Measured contrast

Focus ring on surface/default/base

4.78 Passes AA 3:1

Two pixels of ring, two of offset, drawn outside the border so it survives a dense form. One token, applied by the component, is the whole reason a keyboard user gets the same ring on a chip, a card and a select.

Spark · Form controls Error
Workflow name
Onboarding tier 2 #

Use letters, numbers and hyphens only.

Save changes Cancel
Tokens that produce it
  • surface/danger/subtle
  • border/danger/strong
  • content/danger/default
Measured contrast

Error message on surface/danger/subtle

6.84 Passes AA 4.5:1

Red is never the only carrier. The state ships with an icon and a sentence naming what to change, and that pairing lives in the component, so a screen in a hurry cannot drop half of it.

Spark · Form controls Disabled
Workflow name
Onboarding — tier 2
Save changes Cancel

Locked while this workflow is running. Stop it to edit.

Tokens that produce it
  • surface/default/disabled
  • border/default/subtle
  • content/default/disabled
Measured contrast

Disabled label on surface/default/disabled

2.34 Below AA 4.5:1

The one pairing here that misses 4.5:1, and the only one WCAG exempts. The system fills the gap with its own rule instead: a disabled control always renders the reason it is disabled as ordinary text beside it.

A static recreation of the states, so nothing here takes focus away from the page. Every ratio is the computed WCAG value for the two colours the panel beside it actually paints.

Semantic colour tokens

Surfaces and interaction states

Colour primitives are named by purpose, so the system can express context consistently across product screens and component states.

Surfaces

surface/default/base
surface/default/level-1
surface/brand/subtle
surface/brand/strong

Interaction States (Brand Primary)

Default
Hover
Pressed

These names describe purpose rather than a raw colour value — surface/brand/strong, not teal-700 — making decisions easier to find, discuss and refine without changing every screen that uses them.

The trade-off

A System That Can
Evolve With Spark

Spark was never a one-off handover. It was an iterative process: understand Spark, build the common parts, then use real work to find the gaps, edge cases and repeated components worth bringing back into the system.

Naming conventions made the shared parts easier to find and discuss. Storybook made the coded components visible and testable. Together, they gave design and engineering a common place to decide whether a pattern should be reused, extended or retired.

The practical trade-off was progress versus safety. We identified what could be refactored without disrupting Spark, and what needed to wait. That let the system improve day-to-day work without asking the team to pause and rebuild everything at once.

Set by the system
  • The shared states and behaviours of core controls
  • Accessibility requirements: focus, keyboard behaviour and contrast
  • Component names and usage guidance
  • The tested Vue component the team can reuse

Change it here and the whole Spark experience inherits the improvement.

Refined with the team
  • Workflow-specific layout and composition
  • Edge cases and new states found in Spark
  • Which existing components to consolidate or extend
  • What can be safely refactored now, and what must wait

The system evolves as new work exposes real gaps and edge cases.

What we did not build
Ruled out

A component per feature

The quickest route in the moment, and the one already causing drift. It loses a shared vocabulary, repeats decisions and leaves no single place to improve an accessibility defect.

Ruled out

Tokens without components

Foundations alone would have documented the system, not made it usable. The coded component is where decisions become something the team can test, trust and reuse.

Demoted

A single big-bang migration

Replacing everything at once would have stalled product work and made safe refactoring harder. Spark instead grew through progressive adoption: improve the common controls first, then refine and consolidate over time.

From Figma to Vue

One System,
In Use

The Figma design system established the shared patterns and foundations. Tailwind variables carried those decisions into implementation, while the Vue component library in Storybook became the coded source of truth for trying, testing and reusing them.

This was more than a hand-off. Components could be tested with realistic Spark data, pushed through awkward states and improved before they were reused in more product work. The same names, states and usage guidance travelled from design into code.

Interactive specimen

A working recreation of the Form Controls pattern from the SPARK component library. The controls use native behaviours, focus states and labels.

Target environments
Production Staging
Availability
Role
Execution mode

The coded controls the system supplies: native inputs, real labels and real focus behaviour. This is where the team could test a component against realistic Spark data before making it part of another workflow.

Spark chat components: chat headers, AI chat input area, launcher button and AI response cards— view full size
Exploring the workflow with Claude

AI was useful only when it strengthened the system, not when it created another one.

Claude was explored as part of the component workflow: challenging new and existing Vue components, surfacing inconsistent behaviours and helping the team think through states to test. Storybook remained the code source of truth, where components could be pushed with realistic Spark data rather than judged from a static design alone.

What I'd measure

Consistency Is
the Result

A design system earns its value through adoption and consistency: whether the team uses the shared components, whether new duplicates stop appearing, and whether everyday controls become clearer and more effective for the people using Spark.

Where I’d Start

These outcomes were not formally measured. They are the first signals I would track as Spark Design System matures and the team adopts it.

01 Projected

Screens built from the library

Share of new screens using shared components rather than beginning again with one-off controls.

02 Projected

Duplicate components prevented

Whether new components stop appearing outside the library, and whether existing duplicates can be safely consolidated.

03 Projected

From pattern to tested component

The time it takes to move a shared pattern from Figma into a tested Vue component with realistic Spark data.

04 Projected

Accessibility defects prevented early

Whether contrast, focus and keyboard issues are resolved in the shared component rather than discovered later in a screen review.

Next case study

Netcall / Liberty AI

View case study