Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3 - nativewind@next #308

Closed
marklawlor opened this issue Oct 31, 2022 · 179 comments
Closed

v3 - nativewind@next #308

marklawlor opened this issue Oct 31, 2022 · 179 comments

Comments

@marklawlor
Copy link
Collaborator

marklawlor commented Oct 31, 2022

NativeWind 3.0 is a major update for NativeWind that fixes a large variety of issues. For most users the upgrade will be simple and should only require a small configuration change.

Breaking Changes

Web exclusively uses CSS styles

NativeWind on web now only works with CSS styling. The minimum version of React Native Web is now v0.18.

There is known issue where React Native Web crashes when using NativeWind on Animated components.
You will need to update to the React Native Web 0.19 preview

Project setup

Native apps now require a Metro plugin

// Example metro.config.js for an Expo project
// Expo is not required, you just need to wrap your config using withNativewind
const { getDefaultConfig } = require("expo/metro-config");
const withNativewind = require("nativewind/metro");

module.exports = withNativewind(getDefaultConfig(__dirname));

Babel now uses a preset instead of a plugin

// babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
-   plugins: ["nativewind/babel"]
    presets: [
      "babel-preset-expo", 
+     "nativewind/babel"
    ],

  };
};

Tailwind now requires a preset instead of a plugin

// tailwind.config.js
+ const nativewind = require("nativewind/tailwind")

module.exports = {
  content: ["./App.{js,jsx,ts,tsx}", "./<custom directory>/**/*.{js,jsx,ts,tsx}"],
+ presets: [nativewind],
- plugins: [nativewind],
  theme: {
    extend: {},
  },
}

Base scaling has changed

NativeWind now supports the rem unit. This is a breaking change as it will affect all rem based styles.

Previously, NativeWind 1-1 matched the scaling of TailwindCSS documentation and overrode the rem values with their px equivalent. (example scaling)

This was undesirable, as components styled with NativeWind were different to the base component. For example, NativeWind's text was slightly larger than the default.

<Text> // fontSize: 14
<Text className="text-base" /> // fontSize: 16

Now that NativeWind supports rem, we can use an rem value of 14 to match the default <Text />.

The result of this is your app may "shrink", as everything scales down from the previous static 16px to the new 14.

You can restore the old behaviour by setting NativeWindStyleSheet.setVariables({ '--rem': 16 })

Dark mode

  • React Native projects will need to set darkMode: 'class' to enable manually controlling the color scheme
  • React Native Web projects will need to follow the Tailwind documentation if they wish for dark mode to honor the system preference while in darkMode: class

This aligns NativeWind's functionality with TailwindCSS handling of Dark Mode

styled() props options

The classProps option for styles has been removed. This has been merged into props.

The option baseClassName has been renamed to className

The props option still allows you to style non-style props but is now more flexible. Like before it is an object that accepts the name of component props, and it's value configures how that prop is mapped.

If the value is a Boolean, NativeWind will so transform that prop (but not rename it)

If the value is a String, NativeWind will transform and rename that prop. This is useful if you have a prop innerStyle but you'd like to expose it as innerClassName

Lastly, it accepts an object with the option

{
  name?: string,
  value?: keyof Style,
  class?: Boolean
}

name renames the prop, same as simply providing a string.

value extracts the style value from the ste object. This is the same behaviour that NativeWind used to do if you provide a string.

class a Boolean that indicates that this prop should only be transformed on native. On web, it will be pre-pended to the className string. This replaces the old classProps option.

Web className merging

Styles on native and web behaves slightly differently. Native styles are merged together with later classNames overriding earlier ones. However on the web classNames are prioritized based upon their CSS specificity and not the supplied order.

Normally this isn't an issue and can be easily overcome with the important modifier, however for larger apps you may want a more permanent solution. NativeWind exposes NativeWindStyleSheet.setWebClassNameMergeStrategy which you can use with libraries like tailwind-merge to make web behave more like the predictable native styles.

Theme functions

Theme functions like hairlineWidth and platformSelect are now exported from nativewind/theme. You will need to update your imports.

group and parent

group-isolate has been replaced with nested groups. This feature requires TailwindCSS 3.2

The parent variant has been removed and should be replaced with nested groups. The parent class still exists, but is only used for new variants like odd:/even:

Removed features

  • NativeWind no longer exports a PostCSS plugin. This was mostly used for troubleshooting, for which there are now better methods.
  • The polyfill for aspect with ratio values has been removed. Has been added to React Native . If you require this feature and cannot upgrade you can define your aspect ratios in your theme.
  • NativeWindStyleSheet.setOutput() has been removed

New Features

Variant support

styled() has been updated to include variant support allowing for more complex and flexible components.

import { Pressable } from "react-native"
import { styled, sharedProps } from "nativewind"

// Add variant to styled() components
const Button = styled(Pressable, "border rounded", {
  variants: {
    intent: {
      primary: "bg-blue-500 border-transparent hover:bg-blue-600"
      secondary: "bg-white border-gray-400 hover:bg-gray-100",
    },
    size: {
      small: "py-1 px-2",
      medium: "py-2 px-4",
    },
  },
  defaultProps: {
    intent: "primary",
    size: "medium",
  },
});

const ButtonText = styled(Text, "font-semibold", {
  variants: {
    intent: {
      primary: "text-white"
      secondary: "text-gray-800",
    },
    size: {
      small: "text-sm",
      medium: "text-base",
    },
  },
  defaultProps: {
    intent: "primary",
    size: "medium",
  },
});

export function MyButton({ intent, size, ...props }) {
  return (
    <Button intent={intent} size={size} {...props}>
      <ButtonText intent={intent} size={size}>Hello, World</ButtonText>
   </Button>
  )
}

The function signature for styled is

styled(Component: ComponentType, defaultClassNames?: string, options?: StyledOptions)
styled(Component: ComponentType, options?: StyledOptions)

variants

The variants option is an schema for what classNames to apply when a prop matches the value.

variants: {
    // When <Text size="<size>" /> is set - apply the appropriate style 
    size: {
      small: "text-sm",
      base: "text-base",
    },
    // When <Text bold /> is set - apply the bold className, otherwise apply the regular className
    bold: {
      true: "font-bold",
      false: "font-regular",
    },
  },
}

compoundVariants

{
  // When BOTH intent="primary" and size="medium", apply the uppercase className
  compoundVariants: [{ 
    intent: "primary", 
    size: "medium", 
    className: "uppercase" 
  }],
}

defaultProps

{
  size: {
    small: "text-sm",
    base: "text-base",
  },
  // When size is not set - apply the base variant
  defaultProps: {
    size: "base",
  },
}

Additional usage

Variants are not only limited to styled(), you can write the variant logic inline

import { Pressable } from "react-native"
import { variants, VariantProps } from "nativewind"

const button = variants({
  size: {
    small: "text-sm",
    base: "text-base",
  },
})
type MyButtonProps = VariantProps<typeof button>

export default function MyButton(props: MyButtonProps) {
  const className = button(props)
  return <Pressable {...props } className={className} />
}

Variant autocomplete

If you're using the "Tailwind CSS IntelliSense" Visual Studio Code extension, you can enable autocompletion by adding the following to your settings.json:

{
  "tailwindCSS.experimental.classRegex": [
    ["styled\\([^,)]+,([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
  ]
}

:root CSS Variables

One of the downsides of precompiled styles is static theming and being unable to change values at runtime. NativeWind now allows you to assign a CSS variable as a theme value. These values can be change at runtime using the NativeWindStyleSheet.setVariables function.

NativeWind only supports global CSS variables (those set on :root and the special .dark selector

A common pattern is changing CSS variables with the colorScheme, so NativeWind also support .dark as a special selector. It assumes that .dark is being used in a global manner (eg on ). When Dark Mode is active, the :root and .dark variables will be merged together.

CSS variables support being nested inside CSS functions rgba(255, 255, 255, var(--opacity, 1)).

Setting via theme

// tailwind.config.js
module.exports = {
  theme: {
    variables: {
      "--colors-custom-500": "black"
    },
    darkVariables: {
      "--colors-custom-500": "white"
    },
  }
}

// Compiles to
:root { --custom-500: 'black'; }
.dark { --custom-500: 'white'; }

Setting during runtime

import { NativeWindStyleSheet} from "nativewind"

NativeWindStyleSheet.setVariables({ "--brand-color": "blue" })

Setting via plugins

// tailwind.config.js
module.exports = {
  plugins: [
     plugin(function ({ addBase }) {
       addBase({
        ":root": { "--custom-500": "black" },
        ".dark": { "--custom-500": "white" },
      })
     })
  ]
}

Setting via CSS

// styles.css
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  --custom-500: black;
}
.dark {
  --custom-500: white;
}

Web SSR

On the web, NativeWindStyleSheet.setVariables() will add variables to the root element, which will cause hydration errors. You can use <Html lang="en" style={NativeWindStyleSheet.getSSRStyles()}>

Limitations

NativeWind only implements a subset of the CSS variable specification. The main two differences are

  • Can only be set on :root or .dark
  • Does not support multiple values eg --background: white url(...)

useUnsafeVariable()

NativeWind exposes a new useUnsafeVariable hook which allows your components to react to variable updates. useUnsafeVariable is unsafe because it behaves slightly differently between platforms, especially with units. For example 10vw on native will return a number that is the calculated value of 10vw while on web it returns the string "10vw".

For consistent behaviour across platforms, it is recommend you only use this hook for string variables such as colors.

import { MotiView } from "moti"
import { useVariable } from "nativewind"

function MyView() {
 const [brandColorFrom] = useUnsafeVariable("--brand-color-300")
 const [brandColorTo] = useUnsafeVariable("--brand-color-500")
 
  return (
    <MotiView
      from={{ backgroundColor: brandColorFrom }}
      animate={{ backgroundColor: brandColorTo }}
      exit={{ backgroundColor: brandColorFrom }}
    />
  )
}

useUnsafeVariable also allows you to set the variable, similar to useState

const [value, setValue] = useUnsafeVariable("--brand-color-300")

rem Units

Many developers use rem units to ensure consistent scaling across their application. NativeWind now mimics this behaviour using the CSS variable --rem, which defaults to 14. Developers can change their either by adding a font-size to :root or by calling NativeWindStyleSheet.setVariable({ --rem: <value > })

Inline theme functions

Theme functions like platformSelect previously could only be used with other theme functions. Now they can be used inline with other CSS functions var(--error-color, ${platformColor({ ios: "systemRed", default: "red" }))

RTL Support

The rtl and ltr variants are now supported. Developers can change this value via NativeWindStyleSheet.setDirection() or NativeWindStyleSheet.toggleDirection()

Odd/Even/First/Last

On native, the parent element will need the parent className. This provides the context for these variants to work

The odd/even/first/last variants are now supported for styling children.

Bug Fixes & Improvements

  • NativeWind 3.0 styles are properly deduplicated, significantly improving startup time
  • A number of caching issues have been fixed
  • Fixed edge cases where modifiers and variants did not apply correctly

Experimental CSS support

You can now optionally include a single .css import at your App entrypoint which NativeWind will use as the input for your Tailwind styles. This file needs to contain the TailwindCSS atRules and can also contain additional CSS rules.

Custom CSS is a highly experimental feature that allows developers to write custom CSS classes and use features like @apply without learning Tailwind's plugin system.

NativeWind implements a very small subset of the CSS spec, enough for the supported Tailwind CSS classes to work. This feature is not intended for general use

// global.css
+ @tailwind base;
+ @tailwind components;
+ @tailwind utilities;

.my-class {
  @apply text-base text-black
}


// App.tsx
import { Text, View } from "react-native";

+ import "./styles.css";

export function Test() {
  return (
    <View className="container">
      <Text>Hello world!</Text>
    </View>
  );
}
@marklawlor marklawlor pinned this issue Oct 31, 2022
@marklawlor
Copy link
Collaborator Author

marklawlor commented Oct 31, 2022

nativewind@next is now available for public testing, please leave any feedback in this issue.

This is a preview release - so don't be surprised if there are some stray bugs. I hope to use your feedback in stabilizing for a stable release.

Docs will be available at https://next.nativewind.dev/, but are currently out of date.

I will be hiding comments of resolved issues so I can keep track of incoming comments.

Known issues

  • Docs are incorrect/missing
  • Babel transform allow/block module options are not implemented
  • styled() & <StyledComponent /> ref prop is incorrectly typed

@fortezhuo

This comment was marked as resolved.

@marklawlor

This comment was marked as resolved.

@fortezhuo

This comment was marked as resolved.

@marklawlor

This comment was marked as resolved.

@fortezhuo

This comment was marked as resolved.

@reanzi

This comment was marked as resolved.

@marklawlor

This comment was marked as resolved.

@marklawlor

This comment was marked as resolved.

@fortezhuo

This comment was marked as resolved.

@reanzi

This comment was marked as resolved.

@EliasGcf

This comment was marked as resolved.

@reanzi

This comment was marked as resolved.

@marklawlor

This comment was marked as resolved.

@marklawlor

This comment was marked as resolved.

@steida

This comment was marked as resolved.

@EliasGcf

This comment was marked as resolved.

@bidah

This comment was marked as resolved.

@marklawlor

This comment was marked as resolved.

@marklawlor

This comment was marked as resolved.

@feg624
Copy link

feg624 commented Feb 22, 2023

Hello, first of all, great library!

Maybe someone can point me into the right direction. I've been trying to make v3 work on web, but no matter I change versions and configurations, no Tailwind style is applied on web. On native, the styles work okay. I'm using Expo SDK 47 with react-native-web 0.18.9

Thanks in advance!

@esinanturan
Copy link

esinanturan commented Mar 4, 2023

@marklawlor Thank you for the library. I am trying to use it with vite bundler and everything works except manually toggling dark mode on the web gives this error "color-scheme.js:38 Cannot manually control color scheme. Please set "darkMode: 'class'" in your 'tailwind.config.js'"
but I have already set it to darkMode:class what else can be the issue ?
Another issue is colorScheme property is emtpy in web.
I have checked it with expo web and it behaves same so its not something vite specific.

@anurag-alla
Copy link

Hello, first of all, great library!

Maybe someone can point me into the right direction. I've been trying to make v3 work on web, but no matter I change versions and configurations, no Tailwind style is applied on web. On native, the styles work okay. I'm using Expo SDK 47 with react-native-web 0.18.9

Thanks in advance!

npx tailwindcss -i ./main.css -o ./web/output.css && expo start --web
use this command as a temporary workaround to make styles work on web

@HZSamir
Copy link

HZSamir commented Mar 10, 2023

Same issue as @esinanturan

@Wolfleader101
Copy link

Hey,

Having issues with building an expo app with native wind. It causes the builds on EAS to timeout and never complete when using V3.

@anurag-alla
Copy link

Hey,

Having issues with building an expo app with native wind. It causes the builds on EAS to timeout and never complete when using V3.

Check here (#363) for workaround solution

@XantreDev
Copy link

@marklawlor Are you need help with maintaining? I want to help you with this lib

@chrisarts
Copy link

@marklawlor Im working on a lib that took so much learn from your utils but with a different approach, putting tailwindcss compiler in the runtime (real fast) to avoid problems with module transforms, if you want to discuss a collaboration between libs I will be happy to give you some feedback.

@sannajammeh
Copy link

sannajammeh commented Mar 19, 2023

Also putting in a comment for maintenance. I'm the author of https://tw-classed.vercel.app/ . I was initially thinking of creating a @tw-classed/nativewind package to internally use the styled function, but I see V3 has already handled this case! This is great. We use this combo heavily in production and would love to migrate to V3 once the Expo EAS fixes land.

If you are in need of maintainers let me know, I'm happy to help!

@abduraufsherkulov
Copy link

Do we have an update?

@atanaskanchev
Copy link

It seems like the project is abandoned, so sad. I've moved over to tamagui...

@jamesallain
Copy link

jamesallain commented Apr 18, 2023

He's doing major work on expo-router to make it compatible with nativewind.

@Jonatthu
Copy link

@atanaskanchev I do not blame you to think that here is the real reason:
expo/router#498

@marklawlor
Copy link
Collaborator Author

Hi everyone.

As explained in #464, development is now moving to a new project within the Expo organisation. While this project may be put on hold - the vision for Tailwind in React Native is stronger than ever. More updates coming soon.

@pixelkoduje
Copy link

image
The odd class and other child classes - even/odd etc. only work when I add "divide-y-0", the parent class doesn't seem to work. But it works when I add divide-y-0. I am using the latest version as of today. How to use group classes and odd/even etc. properly, as of today?

@senghuotlay
Copy link

How can i make it work as UI package in turborepo, and i've tried, adding

app/tailwind.config.js

/** @type {import('tailwindcss').Config} */
const nativewind = require("nativewind/tailwind");

// /** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./app/**/*.{js,ts,jsx,tsx}",
    "../../packages/ui/src/**/*.{js,ts,jsx,tsx}",
    "../../packages/dist/src/**/*.{js,ts,jsx,tsx}",
    "../../node_modules/@klotti/ui/src/**/*.{js,ts,jsx,tsx}",
    "../../node_modules/@klotti/ui/dist/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  presets: [nativewind],
  plugins: [],
};

i've even added nativewind to the UI package,
ui/tailwind.config.js

module.exports = {
  content: [
    "./src/**.*{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  presets: [nativewind],
  plugins: [],
};

No matter what i've done, the tailwind isn't picking up any styling from my UI package at all

@XantreDev
Copy link

How can i make it work as UI package in turborepo, and i've tried, adding

app/tailwind.config.js

/** @type {import('tailwindcss').Config} */
const nativewind = require("nativewind/tailwind");

// /** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./app/**/*.{js,ts,jsx,tsx}",
    "../../packages/ui/src/**/*.{js,ts,jsx,tsx}",
    "../../packages/dist/src/**/*.{js,ts,jsx,tsx}",
    "../../node_modules/@klotti/ui/src/**/*.{js,ts,jsx,tsx}",
    "../../node_modules/@klotti/ui/dist/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  presets: [nativewind],
  plugins: [],
};

i've even added nativewind to the UI package,
ui/tailwind.config.js

module.exports = {
  content: [
    "./src/**.*{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  presets: [nativewind],
  plugins: [],
};

No matter what i've done, the tailwind isn't picking up any styling from my UI package at all

Every time when you changing tailwind config you need to do yarn start --reset-cache

@senghuotlay
Copy link

it's still not working for some reason, it's only the UI package that isn't working. Do u have an example guide that i can follow?

@hukpo
Copy link
Contributor

hukpo commented Aug 13, 2023

A small tip I found digging in source code
You can use color variables with opacity, just set them right in your tailwind.config.js file

  1. It only works with rgba colors. From what I found hsl needs a Pull Request
  2. Define your colors like 255, 0, 0 commas are REQUIRED
  3. In your tailwind.config.js file use legacy rgba format for colors rgba(var(--primary), <alpha-value>)
  4. Thats it active:bg-primary/90 works now

P.S. useUnsafeVariable needs a wrapper to work properly (see the last image)

UPDATE: read my comment below to fix issue with variables object and colors in your tailwind config

I was able to support multiple themes using this approach, works like a charm for both Web and Native

@nareshbhatia you probably had the same issue.

image image image image

@yekta
Copy link

yekta commented Sep 5, 2023

A small tip I found digging in source code You can use color variables with opacity, just set them right in your tailwind.config.js file

  1. It only works with rgba colors. From what I found hsl needs a Pull Request
  2. Define your colors like 255, 0, 0 commas are REQUIRED
  3. In your tailwind.config.js file use legacy rgba format for colors rgba(var(--primary), <alpha-value>)
  4. Thats it active:bg-primary/90 works now

P.S. useUnsafeVariable needs a wrapper to work properly (see the last image)

I was able to support multiple themes using this approach, works like a charm for both Web and Native

@nareshbhatia you probably had the same issue.

image image image image

Even with this setup, I'm still getting the error on native (not web), is there anything else to this? Or do you have a public repo I can check out, that would be very helpful. Spent 2 hours trying to make this work, trying all sorts of combinations 😄

Screenshot 2023-09-05 at 16 10 52

Here is the Tailwind config:

Screenshot 2023-09-05 at 16 11 49

Here is the CSS file:

Screenshot 2023-09-05 at 16 13 12

@hukpo
Copy link
Contributor

hukpo commented Sep 5, 2023

@yekta Try to JSON.stringify each color in variables object

I found it later because I wasn't using the variables property, but it works
These are all temporary hacks before stable v4 is live

image

@yekta
Copy link

yekta commented Sep 5, 2023

@yekta Try to JSON.stringify each color in variables object

I found it later because I wasn't using the variables property, but it works These are all temporary hacks before stable v4 is live

image

That did it, thanks a lot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests