tests306 passingcore11.17 kb gziplicenseMIT

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.

ProblemLunarCSS
NativeWind setup is complexlunarcss init — single command
TWRNC is locked to Tailwind v3Native Tailwind v4 support
TWRNC has no web supportSame className works on RN + Web
TWRNC resolves at runtime every renderBuild-time extraction + LRU cache
Reanimated conflict via JSX transformMetro-layer transform — zero clash
Static themes onlyReactive CSS variables via lunar.config.ts
Manual rewrite per Tailwind updateModular 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 compile

Highlights

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 init
import { 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>
  )
}