Skip to content

Design System Engineering: Owning Helium from Figma Library to Production Code

How I built Helium, Shiga’s design system, so designers, engineers and AI coding agents can all build Enta and Pulse from the same foundations, components and rules.

In this case study
At a glance

Helium started as a style guide in Figma. I rebuilt it so people and AI agents could build with it.

Company
Shiga, Enta and Pulse’s parent company
Industry & platform
Fintech, Web3, B2C, B2B, Web, Mobile, SaaS
Role
Design system owner, Figma library + production React codebase
Team
Product, Engineering
Timeline
June to October 2026 (code layer)
Tools
Figma, Claude Code, Helium React, Storybook, Style Dictionary, Chromatic

Helium is Shiga’s design system, used across Enta and Pulse. I own it end to end: the foundations, the Figma library, the production React code and the rules that tell people and AI agents how to use it.

Project TLDR:

  • I wanted more than consistent screens. I wanted a code layer that AI coding agents could build with, so their work matches the design as closely as an engineer’s.
  • I started with the foundations, turned them into 1,608 Figma variables with names ready for Web, iOS and Android, then built 48 components in Figma and React, linked to each other and documented in a public Storybook.
  • Pulse’s first build, in July 2026, used Helium’s components and took five working days.

What I wanted Helium to do

Every design system is meant to help a team build consistently. That was the starting point for Helium too, but I had a second goal. AI coding agents were starting to write our front-end code, and left to themselves, each one invents its own colours, spacing and components. I wanted Helium to give agents a code layer to build with, so a screen comes out right whether a designer, an engineer or an agent builds it.

Helium 1.0 couldn’t do that. It was a style guide in Figma. Designers had one place for colours, type and shared components, but engineers rebuilt every screen by eye from Figma links and screenshots. Nothing tied the code back to the design, so small differences crept in, like mixed casing and a label that read “Ban account” in our send flows.

Helium 2.0 is built on Untitled UI, a widely used Figma kit. I kept that base and rebuilt everything on top of it, from the foundations up, so the same names run from Figma to code.

Starting with the foundations

Before any component, I settled the foundations every screen draws on: type, colour, size and space.

Typefaces
Two typefaces, each with its own job: Instrument Sans for the interface, and IBM Plex Mono as the number font for amounts, wallet addresses, transaction IDs and verification codes.
Interface

Instrument Sans

Regular 400
Send money
Medium 500
Send money
SemiBold 600
Send money
Bold 700
Send money

ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789

I l 1 O 0

$ £ € ¥ ¢

Open source on Google Fonts, so it works on web, iOS and Android without licensing. One variable family covers weights from 400 to 700, enough for every level of hierarchy. It has the dollar, pound, euro and yen signs, but not the naira, cedi, bitcoin or tether signs.

Numbers, addresses and codes

IBM Plex Mono

₦250,000.00

1,250.00 USD₮

0x7a3f…b91c

I l 1 O 0

$ £ € ¥ ₦ ₵ ₿ ₮

People copy and check amounts, wallet addresses, transaction IDs and verification codes character by character. Plex Mono’s serifed I, tailed l, flagged 1 and dotted zero mean nobody mistakes one for another. Every character is the same width, so amounts line up in columns, and it has the naira, cedi and bitcoin signs and the ₮ in USD₮.

Colour
The blue comes from Enta’s brand. One blue isn’t enough for an interface, so it’s a full ramp, with lighter and darker steps for hover, pressed, focus and text on brand. Error, warning and success have their own ramps, 13 accent ramps cover tags, avatars and charts, and separate grey ramps keep contrast right in light and dark. Text has to meet WCAG AA contrast, 4.5:1, or 3:1 for large text, and the semantic variables are tuned so that holds in each of the four themes. Colour is never the only signal either: an error also gets an icon and a message, not just red. The brand, grey and status ramps are below.
Brand
  1. AAA 17.9625#F5FAFF
  2. AAA 17.5550#EFF8FF
  3. AAA 15.10100#D1E9FF
  4. AAA 13.18200#B2DDFF
  5. AAA 8.15300#53B1FD
  6. AA 5.82400#2E90FA
  7. AA 4.57500#1570EF
  8. AA 5.99600#175CD3
  9. AAA 8.19700#1849A9
  10. AAA 9.83800#194185
  11. AAA 14.12900#102A56
  12. AAA 15.81950#0E2243
Grey
  1. AAA 18.3925#FCFCFD
  2. AAA 18.0550#F9FAFB
  3. AAA 17.12100#F2F4F7
  4. AAA 15.21200#E4E7EC
  5. AAA 12.79300#D0D5DD
  6. AAA 7.32400#98A2B3
  7. AA 4.97500#667085
  8. AAA 7.69600#475467
  9. AAA 10.46700#344054
  10. AAA 16.03800#182230
  11. AAA 17.75900#101828
  12. AAA 18.86950#0C111D
Error
  1. AAA 18.3525#FFFBFA
  2. AAA 17.3550#FEF3F2
  3. AAA 15.63100#FEE4E2
  4. AAA 13.29200#FECDCA
  5. AAA 9.71300#FDA29B
  6. AA 6.77400#F97066
  7. AA 5.02500#F04438
  8. AA 4.83600#D92D20
  9. AA 6.57700#B42318
  10. AAA 8.66800#912018
  11. AAA 9.85900#7A271A
  12. AAA 13.94950#55160C
Warning
  1. AAA 18.4025#FFFCF5
  2. AAA 18.0750#FFFAEB
  3. AAA 16.62100#FEF0C7
  4. AAA 14.49200#FEDF89
  5. AAA 12.19300#FEC84B
  6. AAA 10.25400#FDB022
  7. AAA 8.04500#F79009
  8. AA 5.41600#DC6803
  9. AA 5.43700#B54708
  10. AAA 7.52800#93370D
  11. AAA 9.44900#7A2E0D
  12. AAA 13.97950#4E1D09
Success
  1. AAA 18.3825#F6FEF9
  2. AAA 17.8850#ECFDF3
  3. AAA 16.94100#DCFAE6
  4. AAA 14.25200#AAEFC6
  5. AAA 11.65300#75E0A7
  6. AAA 9.31400#47CD89
  7. AA 6.84500#17B26A
  8. AA 4.83600#079455
  9. AA 5.69700#067647
  10. AAA 7.96800#085D3A
  11. AAA 9.93900#074D31
  12. AAA 13.98950#053321
Helium’s brand, grey and status ramps. Each step shows its contrast ratio and WCAG rating, so it’s clear which steps can carry text.
Type scale
Eleven sizes from 12 to 72px. Text sizes from 12 to 20px handle the interface, and display sizes from 24 to 72px handle headings. Each size has its own line height, and emphasis comes from weight, not colour.
Spacing
An 8-point grid. Most screen sizes divide by 8, so layouts scale cleanly across screen densities on iOS and Android, and designers and engineers choose from the same short list of numbers instead of guessing. Most steps are multiples of 8; smaller 4px steps, and 2 and 6px for the tightest spots, handle the space inside components.
Radius
From 2px for small details to fully rounded for pills and avatars.

Turning the foundations into variables

I put every foundation into Figma variables, 1,608 of them, in three tiers. Primitives hold the raw values, like each step of the blue ramp. Semantic variables name what a value is for across the interface, like the colour of secondary text or a page background. Component variables name the part of a component they colour, like the background of a primary button. Semantic and component variables point to primitives, so a change to a primitive in Figma reaches every variable that points to it. Here’s how that works for the primary button.

  • Component

    Button, Hierarchy: Primary

    A primary button labelled Button CTA, in Brand 600 blue.<Button hierarchy="primary">
    • Component variable

      button-primary-bg

      Web
      var(--color-components-buttons-primary-button-primary-bg)
      iOS
      HeliumColors.components.buttons.primary.buttonPrimaryBg
      Android
      HeliumColors.components.buttons.primary.buttonPrimaryBg
      Individual light
      Brand 600 #175CD3
      Individual dark
      Brand 600 #175CD3
      Business light
      Neutral 900 #171717
      Business dark
      Neutral 700 #404040
      • Primitive

        Brand/600

        #175CD3

How the primary button gets its colour. Read from the bottom up: a raw value, the variable that names its job, and the component that uses it.

I used AI to test the naming and to generate the code side of each variable. 1,585 of them carry code syntax for Web, iOS and Android, and that part matters most for AI agents. When an agent reads a Helium design through Figma’s MCP server, it gets each variable’s code name, not just its hex value, so it reaches for the right token instead of hard-coding a colour. Helium’s Figma to code guide then maps those names to the library’s CSS variables. 1,298 variables are also scoped, so a radius only shows up where corners are set and a spacing value only where gaps are. The middle layer above shows the code names for the primary button’s background.

Designing themes for Individual and Business users

Enta has two types of users, Individual and Business, and each can use the app in light or dark. That gives Helium four themes. Which type of user someone is depends on the version of the app they use; light or dark follows their device or their own choice.

Individual keeps Enta’s blue. Business swaps the blue brand colours and the greys for neutrals, and uses a neutral version of the Enta logo with a Business caption. That makes it obvious which account you’re in, and gives business users a more understated, professional look. Here’s the app’s sidebar in each theme, live from Helium’s Storybook. Hover the menu, or open the account switcher.

Individual light
Individual dark
Business light
Business dark
The same sidebar component in all four themes. Business drops the blue for neutrals, and the logo switches to the Business version. The Business sidebars show a new account before its wallet is set up.

Only colour changes between themes. Spacing, type and radius stay the same, and no component checks which theme it’s in: the same 311 colour tokens simply resolve to different values. Adding a theme means adding a mode to the variables, not rewriting components. An app sets its theme with one attribute on the page:

ThemeUsersLight or darkAttribute
Individual lightIndividualLightnone, it’s the default
Individual darkIndividualDarkdata-theme="dark"
Business lightBusinessLightdata-theme="business-light"
Business darkBusinessDarkdata-theme="business-dark"

Try it on the button from the variables section.

Live from Helium’s Storybook

The same button in all four modes

Switch modes and watch the primary button go from blue in Individual to neutral in Business. An app picks the mode with one attribute on the page, shown under the preview.

View in Storybook
Loading Individual light…
<!-- Individual light is the default: no attribute -->
<html>

Helium’s Button, live from Storybook. Between modes only the variables change; the component and its props stay the same.

Building from the smallest pieces up

I organised the components the way atomic design does. Atoms are the smallest parts, like a button, an input or a badge. Molecules combine atoms, like a form field with its label, hint and error message. Organisms are complete parts of a screen, like the sidebar that frames every page of the Enta web app, or a transactions table. Here’s one of each from the Figma library.

Atom
Molecule
Organism
From smallest to largest: a button, a form field built from a label, an input and a message, and the sidebar built from many smaller parts.

I didn’t build all 48 at once. I started with what every Enta screen needs: buttons, form fields, tabs and the app shell. Then Helium grew with the product. Pulse needed tables, statuses and pagination. Enta’s money flows needed components a generic kit doesn’t have: a swap card for what you send and what arrives, a token avatar that shows a coin with its network, a wallet card with balances you can hide, and a timeline for transaction and approval history.

Asset sets exported from Figma keep prototypes moving too: 519 currency icons, 235 flags, 99 company logos, 39 payment methods and 232 integration logos.

Showing assets, networks and conversions

In a crypto wallet, a coin on its own isn’t enough information. USD₮ exists on several networks, and sending it on the wrong one can mean losing it. So Helium pairs icons in two ways, each with its own job.

An asset on its network

TokenAvatar shows an asset with the network it lives on. The network sits as a small badge in the bottom-right corner, and the coin is cut away around it, so the badge reads as a separate token rather than an overlap. Everything scales with the avatar’s size. Here are a few, with and without a network:

  • USD₮ on Optimism
  • USDC on Base
  • ETH on Arbitrum
  • BTC
  • 20px
  • 32px
  • 48px
  • 64px
TokenAvatar: USD₮ on Optimism, USDC on Base, ETH on Arbitrum, and BTC on its own. The badge, the cut-out and the gap scale together at every size.

One currency into another

Conversions need a different picture, because nothing is sitting on anything. Helium draws them as one coin split on a diagonal: the currency you start with on one side, the one you end up with on the other. The same style covers every direction a payment can go: a currency into an asset, an asset into a currency, one currency into another, or one asset into another. Helium ships 22 of these pairs as ready-made art.

  • GBP → USDC
  • USD₮ → NGN
  • EUR → GBP
  • USDC → USD₮
Conversion pairs, read from the currency you start with to the one you end up with. The diagonal split keeps a conversion from ever looking like an asset on a network.

Connecting Figma to code

Each component went from Figma to React the same way. An AI coding agent read the component in Figma and built it in React, using Helium’s tokens and never a raw value. Figma’s properties became the component’s props, so a designer’s “Hierarchy: Primary” is an engineer’s hierarchy="primary". I checked every component against Figma in the browser before it counted as done. Here’s the path every component takes.

  1. Figma component

    Variables and properties, such as Hierarchy: Primary.

  2. AI coding agent

    Reads the design through Figma’s MCP server, with each variable’s code name.

    AGENTS.md: reuse components, use tokens.

  3. React component

    Built with Helium’s tokens. Figma properties become props.

  4. My review

    Checked against Figma in the browser before it counts as done.

  5. Link file and manifest

    Records the Figma node and how each property maps to a prop.

  6. Storybook

    Docs, all four modes and the Figma design beside the code. Chromatic checks every story, and each Figma component links back to its Storybook page.

Every component takes the same path from Figma to Storybook, and Storybook links back to Figma, so you can start from either end.

Our Figma plan doesn’t include Code Connect, so I built the link myself. Each component has a small file that records its Figma node and how its properties map to props, and a script gathers them into one manifest. Each Figma component also links back to its Storybook page, so the connection works in both directions. Here’s Button’s file.

Button.figma.json
{
  "component": "Button",
  "figma": {
    "fileKey": "6JDUb53AbMLtIJPanHJUfP",
    "nodeId": "3287:427074",
    …
  },
  "propMap": {
    "hierarchy": {
      "figmaProperty": "Hierarchy",
      "values": {
        "Primary": "primary",
        "Secondary": "secondary",
        …
      }
    },
    "size": {
      "figmaProperty": "Size",
      "values": { "sm": "sm", "md": "md", "lg": "lg", … }
    },
    …
  },
  "status": "linked"
}
Button’s link file: where it lives in Figma, and how Figma’s Hierarchy and Size become its props.

Storybook is where everything gets checked. All 48 components are documented there with more than 300 stories, a switch for the four modes and the Figma design beside the code. Chromatic captures every story in every mode, so a change to a token can’t quietly break a theme. Here’s Button’s page.

Button in Storybook: how to import it, a playground to try it, and every prop.

For agents, the rules live in AGENTS.md: reuse a Helium component before writing new markup, use tokens instead of raw values, and look up how Figma maps to code in the manifest. Agents read it every time they work in the repo. These are the rules as agents read them.

AGENTS.md
- **Look before you build.** Find the Figma node and the existing component
  (§7) before writing markup. Reuse a public component rather than restyling
  one. Don't invent what Figma doesn't have.
…
- Use only `--helium-*` tokens and component-local custom properties. No hex. Use raw px only for off-scale Figma measurements, and comment each one.
…
- **Look it up first.** Before building any UI, find the Figma node in `docs/figma-to-code.md` or `figma-manifest.json`, and use the existing component in place of new markup.
From Helium’s AGENTS.md, which every agent reads before it writes code.

Designing how components behave

Consistent looks aren’t enough if components behave differently from screen to screen. I wrote 13 UX patterns that set how things work, and six decision records that explain the harder calls, so nobody has to reopen them.

Forms
Primary buttons stay clickable. When something is missing, tapping the button shows exactly what and where, right under the field, instead of a greyed-out button that gives no reason. The error clears as soon as the person edits the field, and what they typed stays if they go back. Try it below.
Live from Helium’s Storybook

The form error state in all four modes

View in Storybook
Loading Individual light…
<!-- Individual light is the default: no attribute -->
<html>

Helium’s Input error states, live from Storybook: the red border, the message under the field and the error focus ring.

Loading
Skeletons for content that’s on its way. Spinners only for transactions the person is waiting to confirm.
Menus
Clicking outside or pressing Escape closes them, and only one is open at a time.
Navigation
Back retraces the person’s steps. Close returns them to the start of the section.
Focus
One 2px focus ring across every component, for people using a keyboard.
Motion
Toasts animate in over 300ms, stay for 3.5 seconds, leave over 700ms, and can be swiped away early.

Writing the words into the system

Words are part of the interface, so they’re part of Helium too. Every error message follows one formula: say what happened, then how to fix it. I rewrote more than 100 messages across sign-up, verification and payments. The first draft read like a policy document:

BeforeAfter
“Password must comprise a minimum of 8 characters.”“Password must be at least 8 characters.”
“Kindly rectify the errors indicated above prior to proceeding.”“Some fields have errors. Fix them to continue.”
“This email is already registered. Kindly log in or utilize an alternative email address.”“This email may already be registered. Log in or reset your password.”Note: Doesn’t confirm whether an account exists.
“Payment Order Not Found”“Payment session has expired. Go back and start again.”Note: No internal system terms in front of users.

Who uses Helium

Pulse
Pulse’s console imports Helium’s React components, icons and tokens directly, and its embedded checkout follows Helium with a few local changes. The first build took five working days in July 2026.
Read the Pulse case study
Enta prototypes
The onboarding and multisig prototypes use built copies of Helium instead of styling their own controls.
Mobile engineering
The mobile team is bringing Helium’s tokens and core components into the Enta app.
Design tools
Helium is set up in Claude Design for the team, and I removed the makeshift copies people had made.
AI coding agents
Every agent working in a Helium-based repo starts from AGENTS.md and the manifest.

How I keep Helium reliable

I built the code layer with Claude Code. I owned the foundations, the Figma library, the decisions, the reviews and the QA. The agent wrote code to rules I set, and nothing counted as done until I had checked it against Figma in the browser.

Two reviewers audited the library and found 198 issues, 7 of them critical. I fixed 139 in one pass; the rest were resolved, turned down with a reason, or logged as product decisions.

Chromatic checks every story in all four modes, and a list of settled decisions keeps the system steady as it grows.

Work in progress

Helium is still growing. Here’s what I’m working on now, and what comes next:

  1. Finish native themingIn progress

    iOS and Android get the core tokens and the light theme. Dark and Business modes come next.

  2. Add automated testsNext

    Today, quality depends on type checks, the audit and visual snapshots. Component tests would catch behaviour bugs earlier.

  3. Retest muted text contrastNext

    A few muted text colours fall below 4.5:1 in some modes. We kept them as a shared decision; I’d test them with users and fix them in the tokens.

  4. Move the error copy into HeliumNext

    The message library lives in a spreadsheet. As a UX pattern in the repo, it would sit next to the components that use it.

  5. Publish the packagesNext

    Products use local copies of Helium today. A private package registry would make each update a simple version bump.