Tailwind v4 for
React Native and Web
Zero-config. One config file. One source of truth.
The pitch
Why LunarCSS?
Existing solutions force a tradeoff between platforms, performance, or stability. LunarCSS doesn’t.
| Problem | LunarCSS |
|---|---|
| NativeWind setup is complex | lunarcss init — single command |
| TWRNC is locked to Tailwind v3 | Native Tailwind v4 support |
| TWRNC has no web support | Same className works on RN + Web |
| TWRNC resolves at runtime every render | Build-time extraction + LRU cache |
| Reanimated conflict via JSX transform | Metro-layer transform — zero clash |
| Static themes only | Reactive CSS variables via lunar.config.ts |
| Manual rewrite per Tailwind update | Modular utility groups |
Architecture
How it works
One lunar.config.ts drives both Metro on native and PostCSS on web. No CSS parsing on mobile. No duplicate token files.
lunar.config.ts (TypeScript, no CSS)
│
│ jiti
▼
flattenTokens()
│
┌──────────┴──────────┐
▼ ▼
Mobile (Metro) Web (PostCSS)
withLunarCSS lunarcss plugin
emit __theme__.js emit @theme {…}
│ │
▼ ▼
setTokens() Tailwind reads
on app boot tokens at compileHighlights
What you get
lunarcss init
One command auto-detects Expo, Next.js, or RN bare and wires every config file. Idempotent re-runs never overwrite your edits.
lunar.config.ts
A single TypeScript file is the source of truth for design tokens — read by Metro on native and PostCSS on web.
Metro-layer transform
className transformation runs in Metro after Babel — no JSX-pipeline conflict with Reanimated or other Babel plugins.
Tailwind v4 native
Built on the v4 @theme model, oklch colors, and v4 modifiers. Not a v3 backport.
Cross-platform
The same className string works on iOS, Android, and the browser. Mobile resolves to StyleSheet, web passes through to Tailwind.
LRU cache
Resolved styles are cached with a theme-hash key. O(1) invalidation when a token changes via the reverse token index.
Quick install
Two commands. Then write className.
$ pnpm add @lunar-kit/css$ npx lunar-css initimport { View, Text } from 'react-native'
export default function Screen() {
return (
<View className="flex-1 items-center justify-center bg-zinc-900">
<Text className="text-2xl font-bold text-white">Hello LunarCSS</Text>
</View>
)
}