Achieving Design–Code Parity
Created a verification first workflow that aligned the design system with production code and enabled safe, scalable token migration across the app

Case study
Design to code parity
Flynas is a flight booking app that grew for years without a real colour system. I used an AI workflow to find every colour actually in use, clean it up, and prove not just assume that the design file and the shipped app has parity.
Role
Product Designer
Platform
iOS (Swift- Swift UI)
Timeline
1 week, May 2026
Tools
Figma, Cursor
Phase - 1 What was the problem
The design file couldn't be trusted on its own. A colour might look correct in Figma, but show up differently or not at all in the live app.
Engineering had no single source to build from, so the same colour kept getting recreated slightly differently, again and again, across hundreds of files.
Why this mattered right now
Flynas already had a colour system defined in Figma I was using it and extending it as I worked on new screens, not building it from scratch. On the design side, things looked fine.
The problem was on the other side of the fence. The tech team had no single source of truth to build from. There was no verified link between what Figma said a colour should be and what the app actually used — engineers were picking values however they always had, screen by screen, with nothing to check against. So even though the Figma file was solid, nobody could say with confidence that it matched what was shipping.
That's a dangerous kind of gap, because it's invisible until someone actually checks. A designer can keep scaling a clean, well-organised Figma file for months, and it means nothing if the app quietly drifted somewhere else the whole time. Closing that gap — proving code and Figma actually agreed, not just assuming they did — became the real goal.
My goal
I set out to answer three questions, in order
What colours is the app actually using right now?
Which of those are duplicates?
Does it actually match what's defined in Figma and if not, where exactly does it disagree?
Answering all three properly by hand wasn't realistic the app has over a thousand code files. So I used Cursor, an AI coding assistant, as a research partner: it did the heavy searching and listing, and I made every judgment call about what the findings actually meant and whether to trust them.
Analysing Code
Before I asked Cursor a single question, I spent time understanding the pieces I'd be directing rather than just naming them. I wasn't going to read a thousand files myself, but I needed to know enough to ask the right questions and judge whether the answers I got back actually made sense:
How iOS actually resolves a colour at runtime the difference between a colour defined directly in Swift versus one living in an asset catalog (.xcassets), and why both exist in the same app.
How light and dark mode are actually implemented in iOS that a single colour name can quietly resolve to different values depending on system appearance, and that this resolution can happen in more than one place in a codebase.
At a high level, how a regex search actually works — specifically, that matching a name doesn't guarantee matching the right thing, which turned out to matter more than I expected.
This step mattered more than it sounds like it should. Every time something looked wrong later, the fix depended on knowing which layer the problem belonged to was this a genuine design-token gap, a counting error, or a real difference in how iOS and Figma each modelled colour. Without that grounding, every discrepancy would have looked the same: a mysterious red flag with no way to tell which kind of problem I was actually looking at.
How I approached it
On paper, this sounds simple: list the colours, clean them up, compare them to Figma. In practice, each step had a real decision buried in it a place where the easy answer would have been the wrong one. Here's where it actually got hard.
List Everything
Find every color used in the app
Group the mess
Judge each colour by its real value,
Double check it
Verify the numbers before trusting them
Compare
Check it against the design file
Step 1 - Find every file that touches colour
The app has over a thousand code files. No one could check them by hand, and even trying would only catch colours sitting neatly in style files, not the ones typed randomly into screens with no pattern.
So the first job was mapping where colours could be hiding.
PROMPT
Model used - Opus Extra 4.7
I'm auditing this iOS codebase to find every place colors and styles are defined, so I can compare them against my Figma design system and find parity gaps. Don't change any code yet — this is a read-only audit.First, find every file that defines colors or visual styles. Look for:
- Files named Colors.swift, Theme.swift, AppColors.swift, or similar
- Any .xcassets color set folders (Assets.xcassets)
- Any UIColor(...) or Color(...) calls scattered directly in view files
- Any files with "light" and "dark" or "Any Appearance" color variants
List every file you find that matches, with full path. Don't extract values yet just give me the inventory of files first.
This was just a map of where to look not the colours themselves yet. But even at this stage, the size of it was a shock. Over a thousand places, scattered everywhere, with no single list connecting any of them.
Step 2 - Open the central files and see what's actually inside them
PROMPT
Model used - Opus Extra 4.7
Open these three files and show me their full contents, unmodified:
- /Users/user/Downloads/Code Flynas /flynas-iOS-dotRez/Reusable Components/AppColors.swift
- /Users/user/Downloads/Code Flynas /flynas-iOS-dotRez/Reusable Components/AppFonts.swift
- /Users/user/Downloads/Code Flynas /flynas-iOS-dotRez/Reusable Components/AppButtons.swift
Then summarize: how many color variables are defined in AppColors.swift, what naming convention they use (e.g. primaryText, brandBlue), and whether each one has a light/dark mode variant defined inline or relies on a separate colorset.
This step didn't reveal a single actual colour yet. What it revealed was the shape of the problem. AppColors.swift held 204 separate colour cases in one enum, with no consistent naming pattern across them. None of the 204 stored a real colour value directly. Every one of them was just a pointer, referencing a separate entry in the asset catalog.
Step 3 - Pull out the actual colours
With the structure understood, the next step was going through all 204 pointers and finding out what each one actually resolves to, in both light and dark mode, and using that same pass to flag anything that looked broken or suspicious.
PROMPT
Model used - Opus Extra 4.7
For the 204 colorTypes in AppColors.swift, group them by what they're visually doing, not their current name. I want a table:
| Current Name | Resolved Light Value | Resolved Dark Value | Proposed Semantic Group |
Use semantic groups like: Brand, Text-Primary, Text-Secondary, Background, Border, Status-Success, Status-Error, Disabled, etc.
Also separately list:
1. The 4 broken tokens (errorColor, disabledColor, aliceBlue, the typo one) and what color they SHOULD probably resolve to based on their name and where they're used in the codebase
2. Any tokens whose resolved light/dark values are near-identical to another token (likely duplicates with different names)
The output
A perfect example to showcase misalignment

This is where the real data appeared, 204 actual colours with real hex values in both modes, laid out side by side, not just names. The same pass also surfaced two useful leads: 4 tokens that weren't actually connected to any real colour at all, and a first list of tokens whose values were close enough to look like possible duplicates.
Neither of those lists was something to accept as-is. A tool flagging two values as "near identical" is a starting point, not proof, the same way a usage count needed checking before it could be trusted. Both lists still needed to be verified against how each token was actually used before treating any of them as confirmed.
Step 4 — Don't trust the first answer
The storage mechanism was fine. Colours lived in the asset catalog, correctly, exactly where they should. But having the right mechanism didn't mean the information Cursor reported about that mechanism was automatically correct. That distinction became the whole point of this step.
One colour was reported as being used 160 times, supposedly for a specific kind of badge across the app's bundle pricing screens. When I asked to see ten real examples, none of them were badges. The number itself was wrong too, the real count was 8, not 160.
ISSUE -
A reported usage count didn't match reality
PROMPT
Model used - Auto
Show me 10 random call sites in the codebase that use .standardWhiteBlack [...] Same for .standard — show me 10 random call sites for that too.
That's not a small error to find midway through a project you've already built several conclusions on top of. If that one number was wrong by 20 times, every other number I hadn't personally checked was suddenly suspect too. The honest reaction wasn't relief at catching it, it was a genuine "wait, what else is wrong" moment.
ISSUE -
Root cause, and whether to trust the rest
PROMPT
Model used - Auto
You previously said .standard had 160 usages and mapped it to product/bundle-tag. I checked the actual codebase and found only 8 Swift references plus 1 storyboard usage- none of them are bundle-related.
They're selection borders, password-strength indicators, and dark-mode icon backgrounds.
Re-verify: where did the 160 number come from? Then correct the token-collapse-map.md row for .standard — either merge it into brand/accent if the hex is close enough, or give it an accurate semantic name based on its real usage (something like brand/accent-alt or status/success depending on majority use). Update the file in place.
The counting had mixed up two completely unrelated things sharing a similar name, like confusing two people with the same first name. The real usage had nothing to do with bundle pricing at all.
Rechecking all 204 colours properly meant redoing work I thought was already finished. It found nine more errors, including one colour no longer used in the app at all.
This became the most valuable, and most uncomfortable, habit in the whole project: a confident, specific sounding answer is not the same as a correct one, and the only way to know the difference is to go looking for reasons to doubt it, not reasons to accept it.
Step 5 — Check it against the real design file
With a verified list in hand, the real question was simple: does any of this actually match Figma? I pulled the live variables straight from the design file and compared them against what the app was actually using, colour by colour, mode by mode.
The result-
14
Colours matched exactly
44
looked similar, but didn't match
23
existed on only one figma
The result: only 14 tokens matched exactly. 44 existed in the code too, but with slightly different values, close but not correct. 23 tokens defined in Figma had no matching entry in the codebase at all. That meant 78% of the design system had drifted from what people were actually seeing on their phones, either the value was wrong, or the token wasn't represented in code in the first place.
A second layer to the same problem
By this point, themePink was confirmed as the app's real, correctly identified brand pink. But fixing its value wasn't as simple as swapping in the right hex code everywhere.
The same token, themePink, was being used for three different things: a button, a card border, and a text label. A token can only hold one value per mode, one for light, one for dark. That's fine when one element uses it. It breaks down when three unrelated elements share it, because each of them actually needed a different answer in dark mode.
A bright pink button needed to stay bright pink at night, or it would look washed out. A bright pink border needed to turn a deeper maroon, or it would glow oddly against a dark background. Bright pink text needed to turn white, or it would be unreadable. Three different needs, one shared token that could only give one answer.
themepink
Current scenario in darkmode
Button
Card Brder
Text Label
themepink
Possible scenario in darkmode
Button
Card Brder
Text Label
Phase 2 — Proving it works in real code
Finding a problem and fixing it are two different jobs. A finished audit is just a document until you test it on something real. So instead of changing all 431 places at once, I picked one real screen to fix and test first: the trip card on the My Trips tab.
Why this screen, and what before and after actually looked like
I picked this screen on purpose, because it had already been fully analysed earlier in the project. I knew exactly what each text label on it was for, and I already knew which correct Figma colour each one should be using. In other words, the thinking was already done. What had not been done yet was actually changing the code.
Before
Seven text labels (date, times, stations, layover text, PNR) were pulling from standardWhiteBlack, a colour invented directly in code with no equivalent in Figma.
Four hero elements, the city title, flight chip text, time-left label, and guest chip text, were using a legacy token, themeWhite, that happened to resolve to the right colour by coincidence, but wasn't connected to Figma at all..
The two airport codes were using textColorsTextPrimary1, the dark grey/black primary text colour, when Figma specifies they should be teal, a real, visibly wrong colour that was live in the app.
The "Non-stop" label and the flight duration text were both borrowing iconFrameColorsFrameBrand, a token built for icon frames, to colour text, the right shade by luck, the wrong token by role.
After
The seven original labels now use textColorsTextPrimary1, matching Figma exactly.
The four hero elements now use textColorsTextWhite, a new token created directly from Figma's Text colors/text-white.
The airport codes now use textColorsTextTeal, a new token created from Figma's Text colors/text-teal, correcting a colour that was genuinely wrong before, not just unconnected.
The "Non-stop" label and duration text now use that same textColorsTextTeal token, properly sourced from a text role instead of an icon role.
One more element, the secondary button's text and border, was checked against Figma and confirmed already correct as it stood. Figma has no single "outlined button" token, it uses two separate tokens for text and border, and the card was already using exactly those, with values matching Figma precisely in both light and dark mode. Applying the same background token used on the primary button here would have actually broken dark mode, so it was deliberately left untouched.
Passing it on to the developers
Once this one screen was proven to work, this became something else: proof that the method works, not a finished job. I shared the fix and the full list of matched colours with the iOS development team, walked them through exactly how and why it worked, and handed over the responsibility of applying the same fix to the rest of the app. From there, it became their work to carry forward, which is exactly how it should go once the idea has been proven to actually work.
Why only one screen, and what happens next
Fixing and fully checking just one screen was never meant to be the finish line. It was meant to prove that the method itself is solid: trace every colour back to its real source in Figma, give it a name that matches, connect that name properly in code, and then check that it actually builds. The same steps are meant to be repeated across the rest of the app's colours, one at a time, each one checked properly before any code is touched.
A few colours are still waiting on a decision from the design side before they can be added, and those have been set aside for now.
The real result here was proving that two systems that had been disconnected for years, the design file and the actual app, can be brought back in sync, carefully and honestly, one verified step at a time.
What this taught me about working with AI
This project changed how I think about using AI tools day to day, mostly because of how many times the easy, obvious answer turned out to be the wrong one. A few things stuck with me:
1- A confident answer isn't the same as a correct one
This project changed how I think about using AI tools day to day, mostly because of how many times the easy, obvious answer turned out to be the wrong one. A few things stuck with me:
2- Catching one mistake should make you check everything else
The easy move, after finding one wrong number, was to fix that one number and move on. The harder and more useful move was assuming the same mistake was probably hiding elsewhere, and going back to check work I'd already considered finished which is exactly where the other nine errors were waiting. My own ideas needed the same scrutiny
3-My own ideas needed the same scrutiny
At one point I suggested a tidy new naming system for the colours myself. It was wrong too it invented a vocabulary that didn't match what was already in the design file, which would have just created a second system to keep track of. The instinct to trust my own “better” idea over what already existed was just as risky as trusting the AI's first answer.
4- The scariest-looking number isn't always the real problem
One colour appeared to have a 97% mismatch with the design file which looked, for a moment, like a huge, expensive gap in the design system. It turned out to be one wrong, overused value, not 400 separate problems. Reacting to the size of a number before understanding where it came from is how you end up solving the wrong thing.








