ChatGPT Claude Codex CLI Cohere Cursor DeepSeek Gemini GitHub Copilot GLM Grok Kimi Llama MiniMax Mistral OpenAI opencode Skill

expo-tailwind-setup

Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling

LLM Mart · 0 points · 20 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download sickn33-agentic-awesome-skills-skills_expo-tailwind-setup-286166a.zip · 4 KB
Part of sickn33/agentic-awesome-skills — 427 skills
This skill couldn't be refreshed from GitHub on the last check — you're seeing the last imported snapshot.

Install

skills CLI npx skills add https://github.com/sickn33/agentic-awesome-skills/tree/main/skills/expo-tailwind-setup
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install sickn33-agentic-awesome-skills@llmmart
Git git clone https://github.com/sickn33/agentic-awesome-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole sickn33/agentic-awesome-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Tailwind CSS Setup for Expo with react-native-css

When to Use

Use this skill when you need set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling.

This guide covers setting up Tailwind CSS v4 in Expo using react-native-css and NativeWind v5 for universal styling across iOS, Android, and Web.

Overview

This setup uses:

  • Tailwind CSS v4 - Modern CSS-first configuration
  • react-native-css - CSS runtime for React Native
  • NativeWind v5 - Metro transformer for Tailwind in React Native
  • @tailwindcss/postcss - PostCSS plugin for Tailwind v4

Installation

# Install dependencies
npx expo install tailwindcss@^4 nativewind@5.0.0-preview.2 react-native-css@0.0.0-nightly.5ce6396 @tailwindcss/postcss tailwind-merge clsx

Add resolutions for lightningcss compatibility:

// package.json
{
  "resolutions": {
    "lightningcss": "1.30.1"
  }
}
  • autoprefixer is not needed in Expo because of lightningcss
  • postcss is included in expo by default

Configuration Files

Metro Config

Create or update metro.config.js:

// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const { withNativewind } = require("nativewind/metro");

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

module.exports = withNativewind(config, {
  // inline variables break PlatformColor in CSS variables
  inlineVariables: false,
  // We add className support manually
  globalClassNamePolyfill: false,
});

PostCSS Config

Create postcss.config.mjs:

// postcss.config.mjs
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

Global CSS

Create src/global.css:

@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css";

/* Platform-specific font families */
@media android {
  :root {
    --font-mono: monospace;
    --font-rounded: normal;
    --font-serif: serif;
    --font-sans: normal;
  }
}

@media ios {
  :root {
    --font-mono: ui-monospace;
    --font-serif: ui-serif;
    --font-sans: system-ui;
    --font-rounded: ui-rounded;
  }
}

IMPORTANT: No Babel Config Needed

With Tailwind v4 and NativeWind v5, you do NOT need a babel.config.js for Tailwind. Remove any NativeWind babel presets if present:

// DELETE babel.config.js if it only contains NativeWind config
// The following is NO LONGER needed:
// module.exports = function (api) {
//   api.cache(true);
//   return {
//     presets: [
//       ["babel-preset-expo", { jsxImportSource: "nativewind" }],
//       "nativewind/babel",
//     ],
//   };
// };

CSS Component Wrappers

Since react-native-css requires explicit CSS element wrapping, create reusable components:

Main Components (src/tw/index.tsx)

import {
  useCssElement,
  useNativeVariable as useFunctionalVariable,
} from "react-native-css";

import { Link as RouterLink } from "expo-router";
import Animated from "react-native-reanimated";
import React from "react";
import {
  View as RNView,
  Text as RNText,
  Pressable as RNPressable,
  ScrollView as RNScrollView,
  TouchableHighlight as RNTouchableHighlight,
  TextInput as RNTextInput,
  StyleSheet,
} from "react-native";

// CSS-enabled Link
export const Link = (
  props: React.ComponentProps<typeof RouterLink> & { className?: string }
) => {
  return useCssElement(RouterLink, props, { className: "style" });
};

Link.Trigger = RouterLink.Trigger;
Link.Menu = RouterLink.Menu;
Link.MenuAction = RouterLink.MenuAction;
Link.Preview = RouterLink.Preview;

// CSS Variable hook
export const useCSSVariable =
  process.env.EXPO_OS !== "web"
    ? useFunctionalVariable
    : (variable: string) => `var(${variable})`;

// View
export type ViewProps = React.ComponentProps<typeof RNView> & {
  className?: string;
};

export const View = (props: ViewProps) => {
  return useCssElement(RNView, props, { className: "style" });
};
View.displayName = "CSS(View)";

// Text
export const Text = (
  props: React.ComponentProps<typeof RNText> & { className?: string }
) => {
  return useCssElement(RNText, props, { className: "style" });
};
Text.displayName = "CSS(Text)";

// ScrollView
export const ScrollView = (
  props: React.ComponentProps<typeof RNScrollView> & {
    className?: string;
    contentContainerClassName?: string;
  }
) => {
  return useCssElement(RNScrollView, props, {
    className: "style",
    contentContainerClassName: "contentContainerStyle",
  });
};
ScrollView.displayName = "CSS(ScrollView)";

// Pressable
export const Pressable = (
  props: React.ComponentProps<typeof RNPressable> & { className?: string }
) => {
  return useCssElement(RNPressable, props, { className: "style" });
};
Pressable.displayName = "CSS(Pressable)";

// TextInput
export const TextInput = (
  props: React.ComponentProps<typeof RNTextInput> & { className?: string }
) => {
  return useCssElement(RNTextInput, props, { className: "style" });
};
TextInput.displayName = "CSS(TextInput)";

// AnimatedScrollView
export const AnimatedScrollView = (
  props: React.ComponentProps<typeof Animated.ScrollView> & {
    className?: string;
    contentClassName?: string;
    contentContainerClassName?: string;
  }
) => {
  return useCssElement(Animated.ScrollView, props, {
    className: "style",
    contentClassName: "contentContainerStyle",
    contentContainerClassName: "contentContainerStyle",
  });
};

// TouchableHighlight with underlayColor extraction
function XXTouchableHighlight(
  props: React.ComponentProps<typeof RNTouchableHighlight>
) {
  const { underlayColor, ...style } = StyleSheet.flatten(props.style) || {};
  return (
    <RNTouchableHighlight
      underlayColor={underlayColor}
      {...props}
      style={style}
    />
  );
}

export const TouchableHighlight = (
  props: React.ComponentProps<typeof RNTouchableHighlight>
) => {
  return useCssElement(XXTouchableHighlight, props, { className: "style" });
};
TouchableHighlight.displayName = "CSS(TouchableHighlight)";

Image Component (src/tw/image.tsx)

import { useCssElement } from "react-native-css";
import React from "react";
import { StyleSheet } from "react-native";
import Animated from "react-native-reanimated";
import { Image as RNImage } from "expo-image";

const AnimatedExpoImage = Animated.createAnimatedComponent(RNImage);

export type ImageProps = React.ComponentProps<typeof Image>;

function CSSImage(props: React.ComponentProps<typeof AnimatedExpoImage>) {
  // @ts-expect-error: Remap objectFit style to contentFit property
  const { objectFit, objectPosition, ...style } =
    StyleSheet.flatten(props.style) || {};

  return (
    <AnimatedExpoImage
      contentFit={objectFit}
      contentPosition={objectPosition}
      {...props}
      source={
        typeof props.source === "string" ? { uri: props.source } : props.source
      }
      // @ts-expect-error: Style is remapped above
      style={style}
    />
  );
}

export const Image = (
  props: React.ComponentProps<typeof CSSImage> & { className?: string }
) => {
  return useCssElement(CSSImage, props, { className: "style" });
};

Image.displayName = "CSS(Image)";

Animated Components (src/tw/animated.tsx)

import * as TW from "./index";
import RNAnimated from "react-native-reanimated";

export const Animated = {
  ...RNAnimated,
  View: RNAnimated.createAnimatedComponent(TW.View),
};

Usage

Import CSS-wrapped components from your tw directory:

import { View, Text, ScrollView, Image } from "@/tw";

export default function MyScreen() {
  return (
    <ScrollView className="flex-1 bg-white">
      <View className="p-4 gap-4">
        <Text className="text-xl font-bold text-gray-900">Hello Tailwind!</Text>
        <Image
          className="w-full h-48 rounded-lg object-cover"
          source={{ uri: "https://example.com/image.jpg" }}
        />
      </View>
    </ScrollView>
  );
}

Custom Theme Variables

Add custom theme variables in your global.css using @theme:

@layer theme {
  @theme {
    /* Custom fonts */
    --font-rounded: "SF Pro Rounded", sans-serif;

    /* Custom line heights */
    --text-xs--line-height: calc(1em / 0.75);
    --text-sm--line-height: calc(1.25em / 0.875);
    --text-base--line-height: calc(1.5em / 1);

    /* Custom leading scales */
    --leading-tight: 1.25em;
    --leading-snug: 1.375em;
    --leading-normal: 1.5em;
  }
}

Platform-Specific Styles

Use platform media queries for platform-specific styling:

@media ios {
  :root {
    --font-sans: system-ui;
    --font-rounded: ui-rounded;
  }
}

@media android {
  :root {
    --font-sans: normal;
    --font-rounded: normal;
  }
}

Apple System Colors with CSS Variables

Create a CSS file for Apple semantic colors:

/* src/css/sf.css */
@layer base {
  html {
    color-scheme: light;
  }
}

:root {
  /* Accent colors with light/dark mode */
  --sf-blue: light-dark(rgb(0 122 255), rgb(10 132 255));
  --sf-green: light-dark(rgb(52 199 89), rgb(48 209 89));
  --sf-red: light-dark(rgb(255 59 48), rgb(255 69 58));

  /* Gray scales */
  --sf-gray: light-dark(rgb(142 142 147), rgb(142 142 147));
  --sf-gray-2: light-dark(rgb(174 174 178), rgb(99 99 102));

  /* Text colors */
  --sf-text: light-dark(rgb(0 0 0), rgb(255 255 255));
  --sf-text-2: light-dark(rgb(60 60 67 / 0.6), rgb(235 235 245 / 0.6));

  /* Background colors */
  --sf-bg: light-dark(rgb(255 255 255), rgb(0 0 0));
  --sf-bg-2: light-dark(rgb(242 242 247), rgb(28 28 30));
}

/* iOS native colors via platformColor */
@media ios {
  :root {
    --sf-blue: platformColor(systemBlue);
    --sf-green: platformColor(systemGreen);
    --sf-red: platformColor(systemRed);
    --sf-gray: platformColor(systemGray);
    --sf-text: platformColor(label);
    --sf-text-2: platformColor(secondaryLabel);
    --sf-bg: platformColor(systemBackground);
    --sf-bg-2: platformColor(secondarySystemBackground);
  }
}

/* Register as Tailwind theme colors */
@layer theme {
  @theme {
    --color-sf-blue: var(--sf-blue);
    --color-sf-green: var(--sf-green);
    --color-sf-red: var(--sf-red);
    --color-sf-gray: var(--sf-gray);
    --color-sf-text: var(--sf-text);
    --color-sf-text-2: var(--sf-text-2);
    --color-sf-bg: var(--sf-bg);
    --color-sf-bg-2: var(--sf-bg-2);
  }
}

Then use in components:

<Text className="text-sf-text">Primary text</Text>
<Text className="text-sf-text-2">Secondary text</Text>
<View className="bg-sf-bg">...</View>

Using CSS Variables in JavaScript

Use the useCSSVariable hook:

import { useCSSVariable } from "@/tw";

function MyComponent() {
  const blue = useCSSVariable("--sf-blue");

  return <View style={{ borderColor: blue }} />;
}

Key Differences from NativeWind v4 / Tailwind v3

  1. No babel.config.js - Configuration is now CSS-first
  2. PostCSS plugin - Uses @tailwindcss/postcss instead of tailwindcss
  3. CSS imports - Use @import "tailwindcss/..." instead of @tailwind directives
  4. Theme config - Use @theme in CSS instead of tailwind.config.js
  5. Component wrappers - Must wrap components with useCssElement for className support
  6. Metro config - Use withNativewind with different options (inlineVariables: false)

Troubleshooting

Styles not applying

  1. Ensure you have the CSS file imported in your app entry
  2. Check that components are wrapped with useCssElement
  3. Verify Metro config has withNativewind applied

Platform colors not working

  1. Use platformColor() in @media ios blocks
  2. Fall back to light-dark() for web/Android

TypeScript errors

Add className to component props:

type Props = React.ComponentProps<typeof RNView> & { className?: string };

Limitations

  • Use this skill only when the task clearly matches its upstream product or API scope.
  • Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
  • Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.
Files (agentic-awesome-skills)
  • agents
    • openai.yaml 359 B
      interface:
        display_name: "Expo Tailwind Setup"
        short_description: "Set up Tailwind CSS v4, react-native-css, NativeWind v5, and CSS wrappers in Expo"
        default_prompt: "Use $expo-tailwind-setup to install and configure Tailwind CSS v4 with Expo, NativeWind v5, react-native-css, Metro, PostCSS, global CSS, typed component wrappers, and theme variables."
      
  • SKILL.md 12.4 KB
    ---
    name: expo-tailwind-setup
    description: Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling
    risk: critical
    source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-tailwind-setup
    source_repo: expo/skills
    source_type: official
    date_added: 2026-07-01
    license: MIT
    license_source: https://github.com/expo/skills/blob/main/LICENSE
    ---
    
    # Tailwind CSS Setup for Expo with react-native-css
    ## When to Use
    
    Use this skill when you need set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling.
    
    
    This guide covers setting up Tailwind CSS v4 in Expo using react-native-css and NativeWind v5 for universal styling across iOS, Android, and Web.
    
    ## Overview
    
    This setup uses:
    
    - **Tailwind CSS v4** - Modern CSS-first configuration
    - **react-native-css** - CSS runtime for React Native
    - **NativeWind v5** - Metro transformer for Tailwind in React Native
    - **@tailwindcss/postcss** - PostCSS plugin for Tailwind v4
    
    ## Installation
    
    ```bash
    # Install dependencies
    npx expo install tailwindcss@^4 nativewind@5.0.0-preview.2 react-native-css@0.0.0-nightly.5ce6396 @tailwindcss/postcss tailwind-merge clsx
    ```
    
    Add resolutions for lightningcss compatibility:
    
    ```json
    // package.json
    {
      "resolutions": {
        "lightningcss": "1.30.1"
      }
    }
    ```
    
    - autoprefixer is not needed in Expo because of lightningcss
    - postcss is included in expo by default
    
    ## Configuration Files
    
    ### Metro Config
    
    Create or update `metro.config.js`:
    
    ```js
    // metro.config.js
    const { getDefaultConfig } = require("expo/metro-config");
    const { withNativewind } = require("nativewind/metro");
    
    /** @type {import('expo/metro-config').MetroConfig} */
    const config = getDefaultConfig(__dirname);
    
    module.exports = withNativewind(config, {
      // inline variables break PlatformColor in CSS variables
      inlineVariables: false,
      // We add className support manually
      globalClassNamePolyfill: false,
    });
    ```
    
    ### PostCSS Config
    
    Create `postcss.config.mjs`:
    
    ```js
    // postcss.config.mjs
    export default {
      plugins: {
        "@tailwindcss/postcss": {},
      },
    };
    ```
    
    ### Global CSS
    
    Create `src/global.css`:
    
    ```css
    @import "tailwindcss/theme.css" layer(theme);
    @import "tailwindcss/preflight.css" layer(base);
    @import "tailwindcss/utilities.css";
    
    /* Platform-specific font families */
    @media android {
      :root {
        --font-mono: monospace;
        --font-rounded: normal;
        --font-serif: serif;
        --font-sans: normal;
      }
    }
    
    @media ios {
      :root {
        --font-mono: ui-monospace;
        --font-serif: ui-serif;
        --font-sans: system-ui;
        --font-rounded: ui-rounded;
      }
    }
    ```
    
    ## IMPORTANT: No Babel Config Needed
    
    With Tailwind v4 and NativeWind v5, you do NOT need a babel.config.js for Tailwind. Remove any NativeWind babel presets if present:
    
    ```js
    // DELETE babel.config.js if it only contains NativeWind config
    // The following is NO LONGER needed:
    // module.exports = function (api) {
    //   api.cache(true);
    //   return {
    //     presets: [
    //       ["babel-preset-expo", { jsxImportSource: "nativewind" }],
    //       "nativewind/babel",
    //     ],
    //   };
    // };
    ```
    
    ## CSS Component Wrappers
    
    Since react-native-css requires explicit CSS element wrapping, create reusable components:
    
    ### Main Components (`src/tw/index.tsx`)
    
    ```tsx
    import {
      useCssElement,
      useNativeVariable as useFunctionalVariable,
    } from "react-native-css";
    
    import { Link as RouterLink } from "expo-router";
    import Animated from "react-native-reanimated";
    import React from "react";
    import {
      View as RNView,
      Text as RNText,
      Pressable as RNPressable,
      ScrollView as RNScrollView,
      TouchableHighlight as RNTouchableHighlight,
      TextInput as RNTextInput,
      StyleSheet,
    } from "react-native";
    
    // CSS-enabled Link
    export const Link = (
      props: React.ComponentProps<typeof RouterLink> & { className?: string }
    ) => {
      return useCssElement(RouterLink, props, { className: "style" });
    };
    
    Link.Trigger = RouterLink.Trigger;
    Link.Menu = RouterLink.Menu;
    Link.MenuAction = RouterLink.MenuAction;
    Link.Preview = RouterLink.Preview;
    
    // CSS Variable hook
    export const useCSSVariable =
      process.env.EXPO_OS !== "web"
        ? useFunctionalVariable
        : (variable: string) => `var(${variable})`;
    
    // View
    export type ViewProps = React.ComponentProps<typeof RNView> & {
      className?: string;
    };
    
    export const View = (props: ViewProps) => {
      return useCssElement(RNView, props, { className: "style" });
    };
    View.displayName = "CSS(View)";
    
    // Text
    export const Text = (
      props: React.ComponentProps<typeof RNText> & { className?: string }
    ) => {
      return useCssElement(RNText, props, { className: "style" });
    };
    Text.displayName = "CSS(Text)";
    
    // ScrollView
    export const ScrollView = (
      props: React.ComponentProps<typeof RNScrollView> & {
        className?: string;
        contentContainerClassName?: string;
      }
    ) => {
      return useCssElement(RNScrollView, props, {
        className: "style",
        contentContainerClassName: "contentContainerStyle",
      });
    };
    ScrollView.displayName = "CSS(ScrollView)";
    
    // Pressable
    export const Pressable = (
      props: React.ComponentProps<typeof RNPressable> & { className?: string }
    ) => {
      return useCssElement(RNPressable, props, { className: "style" });
    };
    Pressable.displayName = "CSS(Pressable)";
    
    // TextInput
    export const TextInput = (
      props: React.ComponentProps<typeof RNTextInput> & { className?: string }
    ) => {
      return useCssElement(RNTextInput, props, { className: "style" });
    };
    TextInput.displayName = "CSS(TextInput)";
    
    // AnimatedScrollView
    export const AnimatedScrollView = (
      props: React.ComponentProps<typeof Animated.ScrollView> & {
        className?: string;
        contentClassName?: string;
        contentContainerClassName?: string;
      }
    ) => {
      return useCssElement(Animated.ScrollView, props, {
        className: "style",
        contentClassName: "contentContainerStyle",
        contentContainerClassName: "contentContainerStyle",
      });
    };
    
    // TouchableHighlight with underlayColor extraction
    function XXTouchableHighlight(
      props: React.ComponentProps<typeof RNTouchableHighlight>
    ) {
      const { underlayColor, ...style } = StyleSheet.flatten(props.style) || {};
      return (
        <RNTouchableHighlight
          underlayColor={underlayColor}
          {...props}
          style={style}
        />
      );
    }
    
    export const TouchableHighlight = (
      props: React.ComponentProps<typeof RNTouchableHighlight>
    ) => {
      return useCssElement(XXTouchableHighlight, props, { className: "style" });
    };
    TouchableHighlight.displayName = "CSS(TouchableHighlight)";
    ```
    
    ### Image Component (`src/tw/image.tsx`)
    
    ```tsx
    import { useCssElement } from "react-native-css";
    import React from "react";
    import { StyleSheet } from "react-native";
    import Animated from "react-native-reanimated";
    import { Image as RNImage } from "expo-image";
    
    const AnimatedExpoImage = Animated.createAnimatedComponent(RNImage);
    
    export type ImageProps = React.ComponentProps<typeof Image>;
    
    function CSSImage(props: React.ComponentProps<typeof AnimatedExpoImage>) {
      // @ts-expect-error: Remap objectFit style to contentFit property
      const { objectFit, objectPosition, ...style } =
        StyleSheet.flatten(props.style) || {};
    
      return (
        <AnimatedExpoImage
          contentFit={objectFit}
          contentPosition={objectPosition}
          {...props}
          source={
            typeof props.source === "string" ? { uri: props.source } : props.source
          }
          // @ts-expect-error: Style is remapped above
          style={style}
        />
      );
    }
    
    export const Image = (
      props: React.ComponentProps<typeof CSSImage> & { className?: string }
    ) => {
      return useCssElement(CSSImage, props, { className: "style" });
    };
    
    Image.displayName = "CSS(Image)";
    ```
    
    ### Animated Components (`src/tw/animated.tsx`)
    
    ```tsx
    import * as TW from "./index";
    import RNAnimated from "react-native-reanimated";
    
    export const Animated = {
      ...RNAnimated,
      View: RNAnimated.createAnimatedComponent(TW.View),
    };
    ```
    
    ## Usage
    
    Import CSS-wrapped components from your tw directory:
    
    ```tsx
    import { View, Text, ScrollView, Image } from "@/tw";
    
    export default function MyScreen() {
      return (
        <ScrollView className="flex-1 bg-white">
          <View className="p-4 gap-4">
            <Text className="text-xl font-bold text-gray-900">Hello Tailwind!</Text>
            <Image
              className="w-full h-48 rounded-lg object-cover"
              source={{ uri: "https://example.com/image.jpg" }}
            />
          </View>
        </ScrollView>
      );
    }
    ```
    
    ## Custom Theme Variables
    
    Add custom theme variables in your global.css using `@theme`:
    
    ```css
    @layer theme {
      @theme {
        /* Custom fonts */
        --font-rounded: "SF Pro Rounded", sans-serif;
    
        /* Custom line heights */
        --text-xs--line-height: calc(1em / 0.75);
        --text-sm--line-height: calc(1.25em / 0.875);
        --text-base--line-height: calc(1.5em / 1);
    
        /* Custom leading scales */
        --leading-tight: 1.25em;
        --leading-snug: 1.375em;
        --leading-normal: 1.5em;
      }
    }
    ```
    
    ## Platform-Specific Styles
    
    Use platform media queries for platform-specific styling:
    
    ```css
    @media ios {
      :root {
        --font-sans: system-ui;
        --font-rounded: ui-rounded;
      }
    }
    
    @media android {
      :root {
        --font-sans: normal;
        --font-rounded: normal;
      }
    }
    ```
    
    ## Apple System Colors with CSS Variables
    
    Create a CSS file for Apple semantic colors:
    
    ```css
    /* src/css/sf.css */
    @layer base {
      html {
        color-scheme: light;
      }
    }
    
    :root {
      /* Accent colors with light/dark mode */
      --sf-blue: light-dark(rgb(0 122 255), rgb(10 132 255));
      --sf-green: light-dark(rgb(52 199 89), rgb(48 209 89));
      --sf-red: light-dark(rgb(255 59 48), rgb(255 69 58));
    
      /* Gray scales */
      --sf-gray: light-dark(rgb(142 142 147), rgb(142 142 147));
      --sf-gray-2: light-dark(rgb(174 174 178), rgb(99 99 102));
    
      /* Text colors */
      --sf-text: light-dark(rgb(0 0 0), rgb(255 255 255));
      --sf-text-2: light-dark(rgb(60 60 67 / 0.6), rgb(235 235 245 / 0.6));
    
      /* Background colors */
      --sf-bg: light-dark(rgb(255 255 255), rgb(0 0 0));
      --sf-bg-2: light-dark(rgb(242 242 247), rgb(28 28 30));
    }
    
    /* iOS native colors via platformColor */
    @media ios {
      :root {
        --sf-blue: platformColor(systemBlue);
        --sf-green: platformColor(systemGreen);
        --sf-red: platformColor(systemRed);
        --sf-gray: platformColor(systemGray);
        --sf-text: platformColor(label);
        --sf-text-2: platformColor(secondaryLabel);
        --sf-bg: platformColor(systemBackground);
        --sf-bg-2: platformColor(secondarySystemBackground);
      }
    }
    
    /* Register as Tailwind theme colors */
    @layer theme {
      @theme {
        --color-sf-blue: var(--sf-blue);
        --color-sf-green: var(--sf-green);
        --color-sf-red: var(--sf-red);
        --color-sf-gray: var(--sf-gray);
        --color-sf-text: var(--sf-text);
        --color-sf-text-2: var(--sf-text-2);
        --color-sf-bg: var(--sf-bg);
        --color-sf-bg-2: var(--sf-bg-2);
      }
    }
    ```
    
    Then use in components:
    
    ```tsx
    <Text className="text-sf-text">Primary text</Text>
    <Text className="text-sf-text-2">Secondary text</Text>
    <View className="bg-sf-bg">...</View>
    ```
    
    ## Using CSS Variables in JavaScript
    
    Use the `useCSSVariable` hook:
    
    ```tsx
    import { useCSSVariable } from "@/tw";
    
    function MyComponent() {
      const blue = useCSSVariable("--sf-blue");
    
      return <View style={{ borderColor: blue }} />;
    }
    ```
    
    ## Key Differences from NativeWind v4 / Tailwind v3
    
    1. **No babel.config.js** - Configuration is now CSS-first
    2. **PostCSS plugin** - Uses `@tailwindcss/postcss` instead of `tailwindcss`
    3. **CSS imports** - Use `@import "tailwindcss/..."` instead of `@tailwind` directives
    4. **Theme config** - Use `@theme` in CSS instead of `tailwind.config.js`
    5. **Component wrappers** - Must wrap components with `useCssElement` for className support
    6. **Metro config** - Use `withNativewind` with different options (`inlineVariables: false`)
    
    ## Troubleshooting
    
    ### Styles not applying
    
    1. Ensure you have the CSS file imported in your app entry
    2. Check that components are wrapped with `useCssElement`
    3. Verify Metro config has `withNativewind` applied
    
    ### Platform colors not working
    
    1. Use `platformColor()` in `@media ios` blocks
    2. Fall back to `light-dark()` for web/Android
    
    ### TypeScript errors
    
    Add className to component props:
    
    ```tsx
    type Props = React.ComponentProps<typeof RNView> & { className?: string };
    ```
    
    ## Limitations
    
    - Use this skill only when the task clearly matches its upstream product or API scope.
    - Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
    - Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related