Communitygithub.com

Geruyang/agent-skills-library

Configura Tailwind CSS v4 en Expo con react-native-css y NativeWind v5 para estilos universales

¿Qué es agent-skills-library?

Esta habilidad proporciona una guía completa para configurar Tailwind CSS v4 en proyectos Expo, utilizando react-native-css y NativeWind v5 para lograr estilos universales en iOS, Android y Web. Es útil para desarrolladores que buscan aprovechar la potencia de Tailwind CSS en el ecosistema React Native, simplificando la creación de interfaces responsivas y mantenibles. La configuración se basa en un enfoque CSS-first, eliminando la necesidad de archivos babel.config.js y adoptando el plugin PostCSS `@tailwindcss/postcss`. El proceso incluye la instalación de dependencias específicas, la configuración de Metro con `withNativewind`, la creación de componentes envolventes (wrappers) con `useCssElement` para soportar la prop `className`, y el uso de variables de tema CSS y colores del sistema Apple mediante `platformColor()`. Además, se detalla cómo incorporar componentes animados y personalizar el tema directamente desde el archivo CSS global.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Geruyang/agent-skills-library/tree/HEAD/original-skills/x-openai-expo-tailwind-setup-db649d8

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Configuración de Tailwind CSS para Expo con react-native-css

Esta guía cubre la configuración de Tailwind CSS v4 en Expo usando react-native-css y NativeWind v5 para estilos universales en iOS, Android y Web.

Resumen

Esta configuración utiliza:

  • Tailwind CSS v4 - Configuración moderna CSS-first
  • react-native-css - Runtime CSS para React Native
  • NativeWind v5 - Transformador Metro para Tailwind en React Native
  • @tailwindcss/postcss - Plugin PostCSS para Tailwind v4

Instalación

# Instalar dependencias
npx expo install tailwindcss@^4 [email protected] [email protected] @tailwindcss/postcss tailwind-merge clsx

Agregar resoluciones para compatibilidad con lightningcss:

// package.json
{
  "resolutions": {
    "lightningcss": "1.30.1"
  }
}
  • autoprefixer no es necesario en Expo debido a lightningcss
  • postcss está incluido en expo por defecto

Archivos de Configuración

Configuración de Metro

Crear o actualizar 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, {
  // las variables en línea rompen PlatformColor en las variables CSS
  inlineVariables: false,
  // Agregamos soporte para className manualmente
  globalClassNamePolyfill: false,
});

Configuración de PostCSS

Crear postcss.config.mjs:

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

CSS Global

Crear src/global.css:

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

/* Familias de fuentes específicas por plataforma */
@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;
  }
}

IMPORTANTE: No se necesita Configuración de Babel

Con Tailwind v4 y NativeWind v5, NO necesitas un babel.config.js para Tailwind. Elimina cualquier preset de NativeWind de Babel si está presente:

// ELIMINA babel.config.js si solo contiene configuración de NativeWind
// Lo siguiente YA NO ES NECESARIO:
// module.exports = function (api) {
//   api.cache(true);
//   return {
//     presets: [
//       ["babel-preset-expo", { jsxImportSource: "nativewind" }],
//       "nativewind/babel",
//     ],
//   };
// };

Envolventes de Componentes CSS

Dado que react-native-css requiere envoltura explícita de elementos CSS, crea componentes reutilizables:

Componentes Principales (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";

// Link habilitado para CSS
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;

// Hook de Variable CSS
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 con extracción de underlayColor
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)";

Componente Image (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: Reasignar estilo objectFit a la propiedad contentFit
  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: El estilo se reasigna arriba
      style={style}
    />
  );
}

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

Image.displayName = "CSS(Image)";

Componentes Animados (src/tw/animated.tsx)

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

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

Uso

Importa los componentes envueltos con CSS desde tu directorio tw:

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">¡Hola Tailwind!</Text>
        <Image
          className="w-full h-48 rounded-lg object-cover"
          source={{ uri: "https://example.com/image.jpg" }}
        />
      </View>
    </ScrollView>
  );
}

Variables de Tema Personalizadas

Agrega variables de tema personalizadas en tu global.css usando @theme:

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

    /* Alturas de línea personalizadas */
    --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);

    /* Escalas de interlineado personalizadas */
    --leading-tight: 1.25em;
    --leading-snug: 1.375em;
    --leading-normal: 1.5em;
  }
}

Estilos Específicos por Plataforma

Usa media queries de plataforma para estilos específicos:

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

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

Colores del Sistema Apple con Variables CSS

Crea un archivo CSS para los colores semánticos de Apple:

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

:root {
  /* Colores de acento con modo claro/oscuro */
  --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));

  /* Escalas de grises */
  --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));

  /* Colores de texto */
  --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));

  /* Colores de fondo */
  --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));
}

/* Colores nativos de iOS mediante 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);
  }
}

/* Registrar como colores del tema de Tailwind */
@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);
  }
}

Luego úsalos en los componentes:

<Text className="text-sf-text">Texto primario</Text>
<Text className="text-sf-text-2">Texto secundario</Text>
<View className="bg-sf-bg">...</View>

Uso de Variables CSS en JavaScript

Usa el hook useCSSVariable:

import { useCSSVariable } from "@/tw";

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

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

Diferencias Clave con NativeWind v4 / Tailwind v3

  1. Sin babel.config.js - La configuración ahora es CSS-first
  2. Plugin PostCSS - Usa @tailwindcss/postcss en lugar de tailwindcss
  3. Importaciones CSS - Usa @import "tailwindcss/..." en lugar de directivas @tailwind
  4. Configuración de tema - Usa @theme en CSS en lugar de tailwind.config.js
  5. Envolventes de componentes - Debes envolver los componentes con useCssElement para soportar className
  6. Configuración de Metro - Usa withNativewind con opciones diferentes (inlineVariables: false)

Solución de Problemas

Los estilos no se aplican

  1. Asegúrate de tener el archivo CSS importado en la entrada de tu aplicación
  2. Verifica que los componentes estén envueltos con useCssElement
  3. Confirma que la configuración de Metro tenga withNativewind aplicado

Los colores de plataforma no funcionan

  1. Usa platformColor() en bloques @media ios
  2. Recurre a light-dark() para web/Android

Errores de TypeScript

Agrega className a las props del componente:

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

Individual skills in this repo

This repo contains 12 individual skills — each has its own dedicated page.

Geruyang/agent-skills-library

Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.

Geruyang/agent-skills-library

Use for new frontend applications, dashboards, games, creative websites, hero sections, and visually driven UI from scratch, or when the user explicitly asks for a redesign/restyle/modernization. Builds from clean, airy, high-taste, readable image-generated concept design with section-specific references, faithful implementation, and browser testing.

Geruyang/agent-skills-library

Use when testing, debugging, or making targeted improvements to rendered frontend apps through the Build Web Apps or web dev plugin: local dev servers, UI regressions, interaction bugs, console errors, responsive layout, and visual QA. Check whether the Browser plugin is available and use it first when it is; otherwise use regular Playwright with the recorded reason.

Geruyang/agent-skills-library

Design UI surfaces for browser games. Use when the user asks for HUDs, menus, overlays, responsive layouts, or visual direction that must protect the playfield.

Geruyang/agent-skills-library

GSAP animation reference for HyperFrames. Covers gsap.to(), from(), fromTo(), easing, stagger, defaults, timelines (gsap.timeline(), position parameter, labels, nesting, playback), and performance (transforms, will-change, quickTo). Use when writing GSAP animations in HyperFrames compositions.

Geruyang/agent-skills-library

Guide for building, configuring, and deploying microfrontends on Vercel. Use this skill when the user mentions microfrontends, multi-zones, splitting an app across teams, independent deployments, cross-app routing, incremental migration, composing multiple frontends under one domain, microfrontends.json, @vercel/microfrontends, the microfrontends local proxy, or path-based routing between Vercel projects. Also use when the user asks about shared layouts across projects, navigation between microfrontends, fallback environments, asset prefixes, or feature flag controlled routing.

Geruyang/agent-skills-library

Next.js App Router expert guidance. Use when building, debugging, or architecting Next.js applications — routing, Server Components, Server Actions, Cache Components, layouts, middleware/proxy, data fetching, rendering strategies, and deployment on Vercel.

Geruyang/agent-skills-library

Use when sizing Public Equity Investing positions, finding equity hedges, or building an integrated position-and-hedge risk plan from a listed-equity thesis. Do not use for thesis construction, standalone event underwriting, trade execution, personal investment advice, or credit-instrument risk.

Geruyang/agent-skills-library

Integrate data visualizations into React and Next.js applications. Use when the user needs chart components, UML-like or architecture diagram components, React integration patterns, Next.js client or server boundaries, hydration-safe rendering, lazy loading, framework-aware performance, scroll-driven visual stories, or export guidance.

Geruyang/agent-skills-library

shadcn/ui expert guidance — CLI, component installation, composition patterns, custom registries, theming, Tailwind CSS integration, and high-quality interface design. Use when initializing shadcn, adding components, composing product UI, building custom registries, configuring themes, or troubleshooting component issues.

Geruyang/agent-skills-library

v0 by Vercel expert guidance. Use when discussing AI code generation, generating UI components from prompts, v0 CLI usage, v0 SDK/API integration, or integrating v0 into development workflows with GitHub and Vercel deployment.

Geruyang/agent-skills-library

Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for "shadcn init", "create an app with --preset", or "switch to --preset".

Skills relacionados