Uniwind Documentation

repository·main·Indexed 23 days ago

https://github.com/uni-stack/uniwind

A high-performance library providing Tailwind CSS bindings for React Native, React Native Web, Android TV, and Apple TV. Uniwind enables the use of `className` props for styling components by computing styles at build time. It features support for dark mode, customizable themes, pseudo-classes, responsive design, and CSS custom properties. The library includes integrations for Metro and Vite bundlers, a `withUniwind` HOC for custom components, and specialized runtime handling for both native and web platforms.

Tokens
37.2K
Snippets
89
Records
167
Agent score
79%

What's inside Uniwind

  1. Overview of Uniwind features

    main

    Uniwind provides high-performance Tailwind CSS bindings for React Native. Key capabilities include:

    • className bindings: Use standard Tailwind className props on every React Native component out-of-the-box.
    • Build-time computation: Styles are computed at build time to ensure maximum runtime performance.
    • Theming: Built-in support for dark mode and fully customizable themes.
    • Pseudo-classes: Support for states like focus, active, disabled, and others.
    • Responsive Design: Support for media queries to handle different screen sizes.
    • CSS Properties: Ability to use custom CSS properties directly within React Native components.
  2. Overview of Uniwind

    main

    Uniwind provides Tailwind CSS bindings for React Native and React Native Web. It allows developers to use className props on React Native components, performing as much styling work as possible at build time to ensure high performance.

    Key Features:

    • Out-of-the-box className bindings for React Native components.
    • Tailwind v4 CSS compilation into native runtime style artifacts or web CSS.
    • Support for light, dark, and extra named themes.
    • Support for variants: active, focus, disabled, RTL, orientation, responsive, data attributes, and platform-aware variants.
    • CSS custom property reads/updates from React Native code.
    • Scoped themes via ScopedTheme and scoped layout direction via LayoutDirection.
    • Integration with Metro and Vite bundlers.

    Supported Platforms: iOS, Android, web, Android TV, and Apple TV.

  3. Migrate themes to CSS using @theme and @variant

    main

    Uniwind uses CSS-based theming instead of JavaScript configuration. Move values from tailwind.config.js to your global.css using the @theme directive, and move NativeWind vars() logic to CSS @variant blocks.

    Migrating Tailwind Config Values

    Before (tailwind.config.js):

    module.exports = {
      theme: {
        extend: {
          colors: {
            primary: '#00a8ff',
          },
        },
      },
    };

    After (global.css):

    @theme {
      --color-primary: #00a8ff;
    }

    Note: Font families must specify a single font as React Native does not support fallbacks.

    Migrating NativeWind JS Themes

    Before (vars()):

    export const themes = {
      light: vars({
        '--color-primary': '#00a8ff',
      }),
      dark: vars({
        '--color-primary': '#273c75',
      }),
    };

    After (global.css):

    @layer theme {
      :root {
        @variant light {
          --color-primary: #00a8ff;
        }
        @variant dark {
          --color-primary: #273c75;
        }
      }
    }

    CRITICAL: All theme variants must define the exact same set of CSS variables. Mismatched variables will cause a Uniwind runtime error.

    Scoped Themes

    To preview or force a theme for a specific subtree (replacing NativeWind's nested ThemeProvider), use ScopedTheme:

    import { ScopedTheme } from 'uniwind';
    
    <ScopedTheme theme="dark">
      <PreviewCard />
    </ScopedTheme>
    @theme {
      --color-primary: #00a8ff;
      --color-secondary: #273c75;
      --font-normal: 'Roboto-Regular';
      --font-bold: 'Roboto-Bold';
    }
  4. Use accent- prefix for non-style color props

    main

    In Uniwind, standard Tailwind color classes (like text-blue-500) only apply to the className prop, which maps to the component's style object. For props that are not part of the style object—such as color, tintColor, thumbColor, or placeholderTextColor—you must use the specific {propName}ClassName prop combined with the accent- prefix.

    Example: To set the color of an ActivityIndicator, use colorClassName="accent-blue-500" instead of className="text-blue-500".

  5. Use @source for Monorepos and sibling directories

    main

    By default, Tailwind scans from the directory containing global.css. If you need to scan classes in sibling directories or packages in a monorepo, use the @source directive in your global.css file.

    /* For sibling directories if global.css is in app/ */
    @import 'tailwindcss';
    @import 'uniwind';
    @source '../components';
    
    /* For monorepo packages */
    @source "../../packages/ui/src";
    @source "../../packages/shared/src";
  6. Configure custom border behavior via @utility

    main

    To customize the default border class, use the @utility directive in your CSS. This completely replaces the built-in border behavior. When using @utility, you must re-declare any properties you still want to be part of that class (e.g., border-width or border-style).

    @utility border {
      border-width: 1px;
      border-style: solid;
      border-color: var(--color-primary);
    }
  7. Understand supported and unsupported Tailwind classes in Uniwind

    main

    Because Uniwind targets React Native (which uses the Yoga layout engine), certain web-specific CSS behaviors and properties are not supported and will be silently ignored.

    Supported

    All standard Tailwind classes for: Layout, spacing, sizing, typography, colors, borders, effects, flexbox, positioning, transforms, and interactive states.

    Unsupported (Web-specific)

    • Pseudo-classes: hover:, visited:. Use Pressable with active: instead.
    • Pseudo-elements: before:, after:, placeholder:.
    • Layout: float-*, clear-*, columns-*.
    • Media/Printing: print:, screen:.
    • Pagination/Breaks: break-before-*, break-after-*, break-inside-*.
  8. Engineering constraints for Uniwind development

    main

    When extending or modifying Uniwind, adhere to these core engineering principles to ensure performance and cross-platform consistency:

    • Performance: Prioritize build-time CSS processing. At runtime, aim for narrow invalidation rather than broad recomputation.
    • Style Precedence: Ensure component wrappers preserve user-defined style precedence.
    • Platform Alignment: Keep native and web behaviors aligned unless platform-specific constraints necessitate divergence.
    • Dependency Management: Any new runtime dependency must map to StyleDependency and should only invalidate its specific affected subscribers.
    • Theming: Theme-aware changes must account for three layers: the global theme, the adaptive system theme, and ScopedTheme.
    • CSS Variables (Native): On native platforms, CSS variables must maintain lazy getter semantics because values may depend on the current runtime state.
    • API Stability: Avoid introducing compatibility paths without known consumers or persisted behavior.
    • Testing Requirements: When changing public APIs or cross-platform behavior, you must add tests for native, web, and TypeScript types.
  9. Use platform-specific fonts in CSS

    main

    You cannot use platform-specific logic directly inside the @theme {} block because it only accepts custom properties. Instead, use @layer theme combined with the @variant directive to target specific platforms in your CSS.

    Example:

    @layer theme {
      :root {
        @variant ios {
          --font-sans: 'Roboto-Regular';
        }
      }
    }

    Note: Always use @variant rather than @media for platform selection in Uniwind CSS.

  10. How the Uniwind Runtime Works

    main

    Uniwind operates differently depending on the target platform to optimize performance.

    Native Runtime

    • Style Resolution: UniwindStore.getStyles(className, props, state, context) resolves classes into React Native style objects. It uses a cache that includes class names, component state, theme scoping, and layout direction.
    • Dependencies: Styles subscribe to StyleDependency types such as theme, dimensions, orientation, insets, font scale, RTL, and adaptive themes.
    • Post-processing: The runtime adapts CSS concepts to React Native shapes (e.g., line-height multipliers, shadows, transforms, and font variants).

    Web Runtime

    • Style Handling: Styles are kept in CSS. React Native Web (RNW) style arrays receive { $$css: true, tailwind: className }.
    • Style Extraction: getWebStyles uses a hidden DOM element to compute values when JS needs them (e.g., for color extraction).
    • Scoping: ScopedTheme renders a div with the theme class and display: contents. LayoutDirection renders a wrapper with direction/dir semantics to scope RTL/LTR variants.

    Shared Runtime API

    • Uniwind.setTheme(theme | 'system'): Switches to an explicit theme or returns to system-adaptive light/dark.
    • Uniwind.currentTheme: Accesses the current theme.
    • Uniwind.hasAdaptiveThemes: Checks for adaptive theme support.
    • Uniwind.updateCSSVariables(theme, variables): Updates theme variables and notifies subscribers.
    • Uniwind.updateInsets(insets): (Native only) Updates safe-area-style runtime values.
  11. How component bindings and the accent- prefix work

    main

    In uniwind, core React Native components use className to map to the standard style prop (handling layout, typography, borders, etc.). However, many React Native components have non-style color props (like tintColor, thumbColor, or placeholderTextColor) that exist outside the style object.

    To style these non-style props using Tailwind classes, you must use a specific {propName}ClassName prop combined with the accent- prefix.

    Key distinction:

    • className: Maps to style. Use for layout (flex, padding, margin) and visual styles (background, border).
    • {propName}ClassName: Maps to a specific prop (e.g., tintColor). Requires the accent- prefix to resolve the Tailwind class to a plain color string instead of a style object.
  12. Handle style specificity and conflicts

    main

    Understanding how Uniwind resolves styles is critical for predictable layouts:

    • Important Utilities: Utilities suffixed with ! (e.g., bg-red-500!) override non-important utilities for the same property and work with variants (e.g., active:bg-red-500!, ios:pt-12!).
    • Inline Styles: Standard React style={{...}} props always override className, including important utilities.
    • Class Deduplication: Uniwind does not automatically deduplicate conflicting classNames. If you are building component libraries where classNames might conflict, use the cn() utility from tailwind-merge to resolve them.