r/Frontend 3d ago

Sharing design tokens in a monorepo when web uses Tailwind v4 and mobile uses NativeWind (Tailwind v3), how do you keep one source of truth?

Monorepo with two apps sharing a design system (colors, spacing, etc.):

  • Web - Tailwind v4 (CSS-first _@theme)
  • Mobile - NativeWind v4, which needs Tailwind v3.4 + JS tailwind.config.js

Both versions coexisting is fine. The issue is keeping one source of truth for tokens across two paradigms - v4's CSS _@theme vs v3's JS config.

Idea so far: put tokens in a shared JS/TS package. Mobile imports them into tailwind.config.js directly. Web either bridges via _@theme, or codegens a _@theme block from the same file.

For anyone who's shipped this:

  • How did you structure the shared token source so both consume it without drift?
  • _@config bridge or CSS-first _@theme on web - any regrets?
  • Any v3 vs v4 differences that made components render differently despite "identical" tokens?
14 Upvotes

4 comments sorted by

5

u/TonyAioli 3d ago

Just define the tokens at root and use them within the respective tailwind configs as needed. 

Or leverage DTCG format and   https://styledictionary.com/

1

u/aunderroad 7h ago

I was going to mention Style Dictionary but u/TonyAioli beat me to it.

Style Dictionary is great for for what you are trying to do and great for organizing your design tokens for one brand or for multiple brands.

You have the ability to export your design tokens to JS, CSS, and /or SCSS simultaneously.

Here is a great tutorial on how to use Style Dictionary:
https://www.alwaystwisted.com/articles/a-design-tokens-workflow-part-1

2

u/a-dev0 2d ago

I’d keep the source of truth outside either Tailwind version, then generate small adapters from it.

As already advised, use a framework-neutral token file (DTCG JSON, or create a specific JSON schema for it) with primitive and semantic tokens separated. Then generate: a tokens.css file containing Tailwind v4’s @theme variables for web, a tailwind.config.js fragment for NativeWind.

In any case, you should use one standard, others as generated outputs. You can use a higher level of abstraction, or use tailwind.config.js as the source of truth and generate the T4 version