How to Sync Design Tokens Between React and Flutter (Without Losing Your Mind)
Style Dictionary's Flutter support has been broken for years. I built tokensync — a CLI that generates CSS and Flutter ThemeData from one tokens.json file, then verifies they match numerically. Your designer just updated the brand color. You open your CSS file. Update --color-brand-500 . Then you open your Flutter file. Update AppTheme._lightColors.primary . Then you grep for anywhere else it might appear. Then you do the same for dark mode. Then you hope you got them all. Three weeks later a designer screenshots both apps side by side. The web button is #5C6BC0 . The mobile button is #5B6BC0 . Off by one digit. Nobody noticed. It shipped. If you maintain both a React web app and a Flutter mobile app, this is a design token sync problem — and it costs teams 6–20 hours every time tokens cha
Style Dictionary's Flutter support has been broken for years. I built tokensync — a CLI that generates CSS and Flutter ThemeData from one tokens.json file, then verifies they match numerically. Your designer just updated the brand color.
You open your CSS file. Update --color-brand-500. Then you open your Flutter file. Update AppTheme.lightColors.primary. Then you grep for anywhere else it might appear. Then you do the same for dark mode. Then you hope you got them all.
Three weeks later a designer screenshots both apps side by side. The web button is #5C6BC0. The mobile button is #5B6BC0.
Off by one digit. Nobody noticed. It shipped.
If you maintain both a React web app and a Flutter mobile app, this is a design token sync problem — and it costs teams 6–20 hours every time tokens change.
Why existing design token tools don't solve this
Style Dictionary is the industry standard for design token transformation. It's genuinely great for CSS. But its Flutter support produces flat key-value constants — not the ThemeData, ColorScheme, or TextTheme that Flutter apps actually need:
// What Style Dictionary gives you for Flutter: static const double typographyDisplayFontSize = 48.0; static const double typographyDisplayFontWeight = 700; // That's it. You still write TextStyle yourself.// What Style Dictionary gives you for Flutter: static const double typographyDisplayFontSize = 48.0; static const double typographyDisplayFontWeight = 700; // That's it. You still write TextStyle yourself.Enter fullscreen mode
Exit fullscreen mode
GitHub issues requesting proper Flutter support have been open since 2022 with no resolution. Most teams end up writing a custom script. It works until the first rebrand, then breaks.
The other pain point is letter-spacing conversion. CSS letter-spacing: -0.02em at font-size: 48px should become Flutter letterSpacing: -0.96. Every team figures this out independently. Every team gets it wrong at least once.
Introducing tokensync: one source of truth for React and Flutter design tokens
tokensync is an open-source CLI that reads a single tokens.json file in W3C DTCG format and generates:
-
tokens.css — CSS custom properties with :root (light) and [data-theme="dark"] (dark) selectors
-
app_theme.dart — complete Flutter ThemeData, ColorScheme, TextTheme, and TextStyle
-
tokens.ts — typed TypeScript constants for shared logic
Then it runs a parity check — numerically comparing every token value between the CSS and Dart outputs to catch drift before it ships.
npx tokensync init # scaffold config + tokens.json npx tokensync build # generate all platform outputs npx tokensync check # verify React and Flutter values matchnpx tokensync init # scaffold config + tokens.json npx tokensync build # generate all platform outputs npx tokensync check # verify React and Flutter values matchEnter fullscreen mode
Exit fullscreen mode
What the generated Flutter output looks like
Given this design token:
{ "typography": { "display": { "$type": "typography", "$value": { "fontFamily": "Inter", "fontSize": "48px", "fontWeight": 700, "lineHeight": "1.1", "letterSpacing": "-0.02em" } } } }{ "typography": { "display": { "$type": "typography", "$value": { "fontFamily": "Inter", "fontSize": "48px", "fontWeight": 700, "lineHeight": "1.1", "letterSpacing": "-0.02em" } } } }Enter fullscreen mode
Exit fullscreen mode
tokensync generates idiomatic Flutter ThemeData:
static const TextTheme _textTheme = TextTheme( displayLarge: TextStyle( fontFamily: 'Inter', fontSize: 48.0, fontWeight: FontWeight.w700, height: 1.1, letterSpacing: -0.96, // ← -0.02em × 48px, correctly converted ), );static const TextTheme _textTheme = TextTheme( displayLarge: TextStyle( fontFamily: 'Inter', fontSize: 48.0, fontWeight: FontWeight.w700, height: 1.1, letterSpacing: -0.96, // ← -0.02em × 48px, correctly converted ), );Enter fullscreen mode
Exit fullscreen mode
And the matching CSS:
:root { --typography-display-font-family: Inter; --typography-display-font-size: 48px; --typography-display-font-weight: 700; --typography-display-line-height: 1.1; --typography-display-letter-spacing: -0.02em; }:root { --typography-display-font-family: Inter; --typography-display-font-size: 48px; --typography-display-font-weight: 700; --typography-display-line-height: 1.1; --typography-display-letter-spacing: -0.02em; }Enter fullscreen mode
Exit fullscreen mode
The generated Dart file passes dart analyze with zero errors out of the box.
How the parity checker works
After building, tokensync compares every token value between platforms:
Parity: web vs flutter 11 tokens · 11 passed ✓ Parity check passedParity: web vs flutter 11 tokens · 11 passed ✓ Parity check passedEnter fullscreen mode
Exit fullscreen mode
It normalizes values before comparing: em to px, named font weights ("SemiBold" → 600), 0em and 0.0 both treated as zero. If anything diverges, it reports the exact token name and delta.
For CI, add this to your pipeline:
tokensync check --ci # exits with code 1 if CSS and Dart diverge
Enter fullscreen mode
Exit fullscreen mode
Mismatches block merges. No more #5C6BC0 vs #5B6BC0 in production.
Dark mode with a single token definition
Define your semantic tokens with light and dark values:
{ "semantic": { "$modes": { "light": { "color": { "primary": { "$value": "{color.brand.500}" } } }, "dark": { "color": { "primary": { "$value": "{color.brand.100}" } } } }, "color": { "primary": { "$type": "color", "$value": "{color.brand.500}" } } } }{ "semantic": { "$modes": { "light": { "color": { "primary": { "$value": "{color.brand.500}" } } }, "dark": { "color": { "primary": { "$value": "{color.brand.100}" } } } }, "color": { "primary": { "$type": "color", "$value": "{color.brand.500}" } } } }Enter fullscreen mode
Exit fullscreen mode
tokensync generates both :root / [data-theme="dark"] blocks in CSS and both ThemeData.light() / ThemeData.dark() in Flutter — from the same source of truth.
Figma integration
If your design tokens live in Figma Variables (Professional plan):
FIGMA_ACCESS_TOKEN=your_token FIGMA_FILE_KEY=your_key tokensync pull
Enter fullscreen mode
Exit fullscreen mode
On the free Figma plan, use the Styles API instead:
tokensync pull --figma-api styles
Enter fullscreen mode
Exit fullscreen mode
Both write a tokens.json ready for tokensync build.
Tokens Studio exports are also supported via a built-in adapter.
How it compares to Style Dictionary
Feature Style Dictionary tokensync
CSS custom properties ✓ ✓
Flutter ColorScheme (light + dark)
✗
✓
Flutter TextTheme with named slots
✗
✓
TextStyle with correct letterSpacing
✗ ✓
em → px letter-spacing conversion ✗ ✓
Named font weights ("SemiBold" → FontWeight.w600)
✗
✓
Cross-platform parity check ✗ ✓
Figma Variables pull ✗ ✓
Getting started
# Install globally npm install -g tokensync# Install globally npm install -g tokensyncOr run with npx
npx tokensync init && npx tokensync build`
Enter fullscreen mode
Exit fullscreen mode
The init command scaffolds a tokensync.config.ts and a sample tokens.json with colors, spacing, and typography — including light/dark modes. Running build generates all three platform outputs and runs the parity check.
Requirements: Node.js 18+. Zero runtime dependencies. MIT license.
GitHub: [https://github.com/rahulpatwa1303/tokensync]
Current status and what's next
This is v0.1.0. It handles the token types I've encountered in real projects: color, dimension, typography, shadow, number. The generated Dart is idiomatic and passes static analysis.
Planned for v0.2.0: React Native formatter, oklch color support, watch mode improvements.
Do you have this problem?
If you ship both React and Flutter, I'd genuinely like to know how you handle design token sync today. Manual copy-paste? A custom script? A SaaS tool? Something in your CI?
Drop a comment — trying to understand whether this pain is widespread or specific to how my team works.
Sign in to highlight and annotate this article

Conversation starters
Daily AI Digest
Get the top 5 AI stories delivered to your inbox every morning.
More about
versionupdateopen-sourceb8679
llama-bench: add -fitc and -fitt to arguments ( #21304 ) llama-bench: add -fitc and -fitt to arguments update README.md address review comments update compare-llama-bench.py macOS/iOS: macOS Apple Silicon (arm64) macOS Intel (x64) iOS XCFramework Linux: Ubuntu x64 (CPU) Ubuntu arm64 (CPU) Ubuntu s390x (CPU) Ubuntu x64 (Vulkan) Ubuntu arm64 (Vulkan) Ubuntu x64 (ROCm 7.2) Ubuntu x64 (OpenVINO) Windows: Windows x64 (CPU) Windows arm64 (CPU) Windows x64 (CUDA 12) - CUDA 12.4 DLLs Windows x64 (CUDA 13) - CUDA 13.1 DLLs Windows x64 (Vulkan) Windows x64 (SYCL) Windows x64 (HIP) openEuler: openEuler x86 (310p) openEuler x86 (910b, ACL Graph) openEuler aarch64 (310p) openEuler aarch64 (910b, ACL Graph)

15 Datasets for Training and Evaluating AI Agents
Datasets for training and evaluating AI agents are the foundation of reliable agentic systems. Agents don’t magically work — they need structured data that teaches action-taking: tool calling, web interaction, and multi-step planning. Just as importantly, they need evaluation datasets that catch regressions before those failures hit production. This is where most teams struggle. A chat model can sound correct while failing at execution, like returning invalid JSON, calling the wrong API, clicking the wrong element, or generating code that doesn’t actually fix the issue. In agentic workflows, those small failures compound across steps, turning minor errors into broken pipelines. That’s why datasets for training and evaluating AI agents should be treated as infrastructure, not a one-time res

The Minds Shaping AI: Meet the Keynote Speakers at ODSC AI East 2026
If you want to understand where AI is actually going, not just what’s trending, you look at who’s building it, scaling it, and questioning its limits. That’s exactly what the ODSC AI East 2026 keynote speakers lineup delivers. This year’s speakers span the full spectrum of AI: from foundational theory and cutting-edge research to enterprise deployment, governance, and workforce transformation. These are the people defining how AI moves from hype to real-world impact. Here’s who you’ll hear from and why missing them would mean missing where AI is headed next. The ODSC AI East 2026 Keynote Speakers Matt Sigelman, President at Burning Glass Institute Matt Sigelman is one of the foremost experts on labor market dynamics and the future of work. As President of the Burning Glass Institute, he ha
Knowledge Map
Connected Articles — Knowledge Graph
This article is connected to other articles through shared AI topics and tags.
More in Products

Why TSMC grew four times faster than its foundry rivals in 2025 — price hikes, vertical integration, and commanding technology lead pay dividends
Why TSMC grew four times faster than its foundry rivals in 2025 — price hikes, vertical integration, and commanding technology lead pay dividends

The AI Stack: A Practical Guide to Building Your Own Intelligent Applications
Beyond the Hype: What Does "Building with AI" Actually Mean? Another week, another wave of AI headlines. From speculative leaks to existential debates, the conversation often orbits the sensational. But for developers, the real story is happening in the trenches: the practical, stack-by-stack integration of intelligence into real applications. While the industry debates "how it happened," we're busy figuring out how to use it . Forget the monolithic "AI" label for a moment. Modern AI application development is less about creating a sentient being and more about strategically assembling a set of powerful, specialized tools. It's about choosing the right component for the job—be it generating text, analyzing images, or making predictions—and wiring it into your existing systems. This guide b

Our Email Provider Banned Us Overnight -- Here's What We Learned
April 6, 2026 | 8 min read We woke up on a Tuesday morning to find that every single email our products sent -- password resets, welcome messages, subscription confirmations, grading notifications -- was bouncing. Not some of them. All of them. Our email provider had permanently disabled our account overnight, with no warning and no appeal process. Just a single-line notification: "Your account has been suspended due to policy violations." We are a small group of friends from Tennessee building SaaS products under our company, Obsidian Clad Labs. We run five live products, and every one of them depends on transactional email to function. This was not an inconvenience. It was a full-blown emergency. Here is what happened, what we did wrong, and what we learned so you do not make the same mi



Discussion
Sign in to join the discussion
No comments yet — be the first to share your thoughts!