Styling

How className merging, tv() slots and the two mergers fit together.

cn() for every caller-supplied className

Uniwind does not deduplicate conflicting utilities on its own. A caller's className has to go through cn() or it will not beat the component's own classes.

import { cn } from "@delacour/react-native-ui/lib/cn";

<View className={cn(variants.root(), className)} />;

tv comes from lib/tv, never from tailwind-variants

There are two mergers in the library and both need the semantic size tokens: cn() merges a caller's className through cn — a drop-in, faster engine for clsx + tailwind-merge with the same output — and tv() merges slots and variants through a tailwind-merge instance of its own.

A bare tv from tailwind-variants does not know what button-md is. It drops text-button-md into tailwind-merge's text colour group, and silently strips the label's colour.

import { tv } from "@delacour/react-native-ui/lib/tv";

src/styles/tokens.ts holds the one config both mergers are built from.

Overriding sizes

Use size-*, not w-* with h-*. tailwind-merge conflicts size into w/h but not the reverse, so a trailing w-6 will not clear a leading size-5.

<Icon icon={IconHeart} className="size-8" />

What does not work

group-*, peer-* and :has() do not exist. Uniwind's compiler reads data-* off a single flat selector and its runtime matches them against props on the component carrying the class. No class on a parent can reach a child. This is why Field cascades its state through a React context instead of a parent-scoped selector — see Field.

Safe-area utilities resolve to zero. pt-safe and friends compile to env(safe-area-inset-top), which is nothing on React Native. The class applies, nothing moves, and a navbar draws over the status bar with no error anywhere. Use useSafeAreaInsets().

A runtime template class is never compiled. Tailwind's scanner is static, so `size-[${n}px]` has nothing in Uniwind's store to look up and silently draws nothing. Named sizes are classes; the numeric escape hatch stays a prop or an inline style.

A variant may not name a line height. tailwind-merge lists leading among font-size's conflicting groups, so a text-lg from a size axis silently deletes a leading-6 written beside a text-base. Tailwind's own --text-*--line-height companions survive tokens.css's size-only overrides, so every step already carries a paired leading; prose needing its own gets a pair, registered in tokens.ts.

On this page