U

uniwind

by @uni-stackv
4.3(37)

将Tailwind CSS v4样式应用于React Native项目,实现跨平台统一设计,提升开发效率。

css-frameworkstailwind-cssutility-first-cssfrontend-developmentresponsive-designGitHub
安装方式
npx skills add uni-stack/uniwind --skill uniwind
compare_arrows

Before / After 效果对比

1
使用前

在React Native中实现复杂的UI样式,通常需要编写大量CSS-in-JS或StyleSheet对象,导致样式代码冗长、难以维护,且缺乏统一的设计系统,开发效率受限。

使用后

Uniwind将Tailwind CSS v4的强大功能带入React Native,让开发者能够以原子化的方式快速构建美观的UI。它极大地简化了样式编写,提升了开发速度,并确保了跨平台的视觉一致性。

SKILL.md

Uniwind — Complete Reference

Uniwind 1.5.0+ / Tailwind CSS v4 / React Native 0.81+ / Expo SDK 54+

If user has lower version, recommend updating to 1.5.0+ for best experience.

Uniwind brings Tailwind CSS v4 to React Native. All core React Native components support the className prop out of the box. Styles are compiled at build time — no runtime overhead.

Critical Rules

  1. Tailwind v4 only — Use @import 'tailwindcss' not @tailwind base. Tailwind v3 is not supported.
  2. Never construct classNames dynamically — Tailwind scans at build time. bg-${color}-500 will NOT work. Use complete string literals, mapping objects, or ternaries.
  3. Never use cssInterop or remapProps — Those are NativeWind APIs. Uniwind does not override global components.
  4. No tailwind.config.js — All config goes in global.css via @theme and @layer theme.
  5. No ThemeProvider required — Use Uniwind.setTheme() directly.
  6. withUniwindConfig must be the outermost Metro config wrapper.
  7. NEVER wrap react-native or react-native-reanimated components with withUniwindView, Text, Pressable, Image, TextInput, ScrollView, FlatList, Switch, Modal, Animated.View, Animated.Text, etc. already have full className support built in. Wrapping them with withUniwind will break behavior. Only use withUniwind for third-party components (e.g., expo-image, expo-blur, moti).
  8. Font families: single font only — React Native doesn't support fallbacks. Use --font-sans: 'Roboto-Regular' not 'Roboto', sans-serif.
  9. All theme variants must define the same set of CSS variables — If light defines --color-primary, then dark and every custom theme must too. Mismatched variables cause runtime errors.
  10. accent- prefix is REQUIRED for non-style color props — This is crucial. Props like color (Button, ActivityIndicator), tintColor (Image), thumbColor (Switch), placeholderTextColor (TextInput) are NOT part of the style object. You MUST use the corresponding {propName}ClassName prop with accent- prefixed classes. Example: <ActivityIndicator colorClassName="accent-blue-500" /> NOT <ActivityIndicator className="text-blue-500" />. Regular Tailwind color classes (like text-blue-500) only work on className (which maps to style). For non-style color props, always use accent-.
  11. rem default is 16px — NativeWind used 14px. Set polyfills: { rem: 14 } in metro config if migrating.
  12. cssEntryFile must be a relative path string — Use './global.css' not path.resolve(__dirname, 'global.css').
  13. Deduplicate with cn() when mixing custom CSS classes and Tailwind — Uniwind does NOT auto-deduplicate. If a custom CSS class (.card { padding: 16px }) and a Tailwind utility (p-6) set the same property, both apply with unpredictable results. Always wrap with cn('card', 'p-6') when there's overlap.

Setup

Installation

# or other package manager
bun install uniwind tailwindcss

Requires Tailwind CSS v4+.

global.css

Create a CSS entry file:

@import 'tailwindcss';
@import 'uniwind';

Import in your App component (e.g., App.tsx or app/_layout.tsx), NOT in index.ts/index.js — importing there breaks hot reload:

// app/_layout.tsx or App.tsx
import './global.css';

The directory containing global.css is the app root — Tailwind scans for classNames starting from this directory.

Metro Configuration

const { getDefaultConfig } = require('expo/metro-config');
// Bare RN: const { getDefaultConfig } = require('@react-native/metro-config');
const { withUniwindConfig } = require('uniwind/metro');

const config = getDefaultConfig(__dirname);

// withUniwindConfig MUST be the OUTERMOST wrapper
module.exports = withUniwindConfig(config, {
  cssEntryFile: './global.css',           // Required — relative path from project root
  polyfills: { rem: 16 },                // Optional — base rem value (default 16)
  extraThemes: ['ocean', 'sunset'],       // Optional — custom themes beyond light/dark
  dtsFile: './uniwind-types.d.ts',        // Optional — TypeScript types output path
  debug: true,                            // Optional — log unsupported CSS in dev
  isTV: false,                            // Optional — enable TV platform support
});

For most flows, keep defaults, only provide cssEntryFile.

Wrapper order — Uniwind must wrap everything else:

// CORRECT
module.exports = withUniwindConfig(withOtherConfig(config, opts), { cssEntryFile: './global.css' });

// WRONG — Uniwind is NOT outermost
module.exports = withOtherConfig(withUniwindConfig(config, { cssEntryFile: './global.css' }), opts);

Vite Configuration (v1.2.0+)

If user has storybook setup, add extra vite config:

import tailwindcss from '@tailwindcss/vite';
import { uniwind } from 'uniwind/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    tailwindcss(),
    uniwind({
      cssEntryFile: './src/global.css',
      dtsFile: './src/uniwind-types.d.ts',
    }),
  ],
});

TypeScript

Uniwind auto-generates a .d.ts file (default: ./uniwind-types.d.ts) after running Metro. Place it in src/ or app/ for auto-inclusion, or add to tsconfig.json:

{ "include": ["./uniwind-types.d.ts"] }

If user has some typescript errors related to classNames, just run metro server to build the d.ts file.

Expo Router Placement

project/
├── app/_layout.tsx    ← import '../global.css' here
├── components/
├── global.css         ← project root (best location)
└── metro.config.js    ← cssEntryFile: './global.css'

If global.css is in app/ dir, add @source for sibling directories:

@import 'tailwindcss';
@import 'uniwind';
@source '../components';

Tailwind IntelliSense (VS Code / Cursor / Windsurf)

{
  "tailwindCSS.classAttributes": [
    "class", "className", "headerClassName",
    "contentContainerClassName", "columnWrapperClassName",
    "endFillColorClassName", "imageClassName", "tintColorClassName",
    "ios_backgroundColorClassName", "thumbColorClassName",
    "trackColorOnClassName", "trackColorOffClassName",
    "selectionColorClassName", "cursorColorClassName",
    "underlineColorAndroidClassName", "placeholderTextColorClassName",
    "selectionHandleColorClassName", "colorsClassName",
    "progressBackgroundColorClassName", "titleColorClassName",
    "underlayColorClassName", "colorClassName",
    "backdropColorClassName", "backgroundColorClassName",
    "statusBarBackgroundColorClassName", "drawerBackgroundColorClassName",
    "ListFooterComponentClassName", "ListHeaderComponentClassName"
  ],
  "tailwindCSS.classFunctions": ["useResolveClassNames"]
}

Monorepo Support

Add @source directives in global.css for packages outside the CSS entry file's directory:

@import 'tailwindcss';
@import 'uniwind';
@source "../../packages/ui/src";
@source "../../packages/shared/src";

Also needed for node_modules packages that contain Uniwind classes (e.g., shared UI libraries).

Component Bindings

All core React Native components support className out of the box. Some have additional className props for sub-styles (like contentContainerClassName) and non-style color props (requiring accent- prefix).

Complete Reference

Legend: Props marked with ⚡ require the accent- prefix. Props in parentheses are platform-specific.

View

PropMaps toPrefix
classNamestyle

Text

PropMaps toPrefix
classNamestyle
selectionColorClassNameselectionColoraccent-

Pressable

PropMaps toPrefix
classNamestyle

Supports active:, disabled:, focus: state selectors.

Image

PropMaps toPrefix
classNamestyle
tintColorClassNametintColoraccent-

TextInput

PropMaps toPrefix
classNamestyle
cursorColorClassNamecursorColoraccent-
selectionColorClassNameselectionColoraccent-
placeholderTextColorClassNameplaceholderTextColoraccent-
selectionHandleColorClassNameselectionHandleColoraccent-
underlineColorAndroidClassNameunderlineColorAndroid (Android)accent-

Supports focus:, active:, disabled: state selectors.

ScrollView

PropMaps toPrefix
classNamestyle
contentContainerClassNamecontentContainerStyle
endFillColorClassNameendFillColoraccent-

FlatList

PropMaps toPrefix
classNamestyle
contentContainerClassNamecontentContainerStyle
columnWrapperClassNamecolumnWrapperStyle
ListHeaderComponentClassNameListHeaderComponentStyle
ListFooterComponentClassNameListFooterComponentStyle
endFillColorClassNameendFillColoraccent-

SectionList

PropMaps toPrefix
classNamestyle
contentContainerClassNamecontentContainerStyle
ListHeaderComponentClassNameListHeaderComponentStyle
ListFooterComponentClassNameListFooterComponentStyle
endFillColorClassNameendFillColoraccent-

VirtualizedList

PropMaps toPrefix
classNamestyle
contentContainerClassNamecontentContainerStyle
ListHeaderComponentClassNameListHeaderComponentStyle
ListFooterComponentClassNameListFooterComponentStyle
endFillColorClassNameendFillColoraccent-

Switch

PropMaps toPrefix
thumbColorClassNamethumbColoraccent-
trackColorOnClassNametrackColor.true (on)accent-
trackColorOffClassNametrackColor.false (off)accent-
ios_backgroundColorClassNameios_backgroundColor (iOS)accent-

Note: Switch does NOT support className (className?: never in types). Use only the color-specific className props above. Supports disabled: state selector.

ActivityIndicator

PropMaps toPrefix
classNamestyle
colorClassNamecoloraccent-

Button

PropMaps toPrefix
colorClassNamecoloraccent-

Note: Button does not support className (no style prop on RN Button).

Modal

PropMaps toPrefix
classNamestyle
backdropColorClassNamebackdropColoraccent-

RefreshControl

PropMaps toPrefix
classNamestyle
colorsClassNamecolors (Android)accent-
tintColorClassNametintColor (iOS)accent-
titleColorClassNametitleColor (iOS)accent-
progressBackgroundColorClassNameprogressBackgroundColor (Android)accent-

ImageBackground

PropMaps toPrefix
classNamestyle
imageClassNameimageStyle
tintColorClassNametintColoraccent-

SafeAreaView

PropMaps toPrefix
classNamestyle

KeyboardAvoidingView

PropMaps toPrefix
classNamestyle
contentContainerClassNamecontentContainerStyle

InputAccessoryView

PropMaps toPrefix
classNamestyle
backgroundColorClassNamebackgroundColoraccent-

TouchableHighlight

PropMaps toPrefix
classNamestyle
underlayColorClassNameunderlayColoraccent-

Supports active:, disabled: state selectors.

TouchableOpacity

PropMaps toPrefix
classNamestyle

Supports active:, disabled: state selectors.

TouchableNativeFeedback

PropMaps toPrefix
classNamestyle

Supports active:, disabled: state selectors.

TouchableWithoutFeedback

PropMaps toPrefix
classNamestyle

Supports active:, disabled: state selectors.

Usage Examples

import { View, Text, Pressable, TextInput, ScrollView, FlatList, Switch, Image, ActivityIndicator, Modal, RefreshControl, Button } from 'react-native';

// View — basic layout
<View className="flex-1 bg-background p-4">
  <Text className="text-foreground text-lg font-bold">Title</Text>
</View>

// Pressable — with press/focus states
<Pressable className="bg-primary px-6 py-3 rounded-lg active:opacity-80 active:bg-primary/90 focus:ring-2">
  <Text className="text-white text-center font-semibold">Press Me</Text>
</Pressable>

// TextInput — with focus state and accent- color props
<TextInput
  className="border border-border rounded-lg px-4 py-2 text-base text-foreground focus:border-primary"
  placeholderTextColorClassName="accent-muted"
  selectionColorClassName="accent-primary"
  cursorColorClassName="accent-primary"
  selectionHandleColorClassName="accent-primary"
  underlineColorAndroidClassName="accent-transparent"
  placeholder="Enter text..."
/>

// ScrollView — with content container
<ScrollView className="flex-1" contentContainerClassName="p-4 gap-4">
  {/* content */}
</ScrollView>

// FlatList — with all sub-style props
<FlatList
  className="flex-1"
  contentContainerClassName="p-4 gap-3"
  columnWrapperClassName="gap-3"
  ListHeaderComponentClassName="pb-4"
  ListFooterComponentClassName="pt-4"
  endFillColorClassName="accent-gray-100"
  numColumns={2}
  data={items}
  renderItem={({ item }) => <ItemCard item={item} />}
/>

// Switch — no className support, use color-specific props only
<Switch
  thumbColorClassName="accent-white"
  trackColorOnClassName="accent-primary"
  trackColorOffClassName="accent-gray-300 dark:accent-gray-700"
  ios_backgroundColorClassName="accent-gray-200"
/>

// Image — tint color
<Image className="w-6 h-6" tintColorClassName="accent-primary" source={icon} />

// ActivityIndicator
<ActivityIndicator className="m-4" colorClassName="accent-primary" size="large" />

// Button — only colorClassName (no className)
<Button colorClassName="accent-primary" title="Submit" onPress={handleSubmit} />

// Modal — backdrop color
<Modal className="flex-1" backdropColorClassName="accent-black/50">
  {/* content */}
</Modal>

// RefreshControl

...

用户评价 (0)

发表评价

效果
易用性
文档
兼容性

暂无评价

统计数据

安装量2.0K
评分4.3 / 5.0
版本
更新日期2026年5月21日
对比案例1 组

用户评分

4.3(37)
5
70%
4
30%
3
0%
2
0%
1
0%

为此 Skill 评分

0.0

兼容平台

🔧Claude Code
🔧OpenClaw
🔧OpenCode
🔧Codex
🔧Gemini CLI
🔧GitHub Copilot
🔧Amp
🔧Kimi CLI

时间线

创建2026年3月16日
最后更新2026年5月21日