Styling (NativeWind)
We style with NativeWind v4 — Tailwind utility classes via className.
StyleSheet.create is retired; reach for inline style only for values you can
only compute at runtime (safe-area math, an SVG fill, an animated value).
import { View, Text } from "react-native";
export function Badge({ label }: { label: string }) {
return (
<View className="rounded-full bg-primary px-3 py-1">
<Text className="text-sm font-medium text-white">{label}</Text>
</View>
);
}
Design tokens are the single source of truth
Colors, spacing, font sizes, etc. live in one tokens.ts. The Tailwind config
consumes them, and JS-only consumers (an SVG fill, an ActivityIndicator color)
import the same file — so there’s exactly one place to change a color.
// tailwind.config.js
const { tokens } = require("./src/shared/constants/tokens");
module.exports = {
content: ["./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}"],
presets: [require("nativewind/preset")],
theme: {
extend: { colors: tokens.colors, spacing: tokens.spacing },
},
};
The cn() helper
Merge conditional and overriding classes with clsx + tailwind-merge:
// src/shared/constants/cn.ts
import clsx, { type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
<View className={cn("p-4", isActive && "bg-primary", className)} />
Add prettier-plugin-tailwindcss so classes are always sorted the same way.
Animation utilities (transition-*, animate-*) depend on Reanimated being
linked. If your project doesn’t link Reanimated, these classes crash at
runtime. Either don’t use them — drive animation from JavaScript — or add
Reanimated deliberately and confirm it’s linked before relying on them.