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
feat: native css styling #498
Conversation
27c4b6d
to
78ac554
Compare
Hey @marklawlor! Previous NativeWind consumer here. I'm super excited to see that you're enhancing the implementation of cross-platform styles (i.e., class names) here in Expo. With moving to Expo though, how much of the Expo ecosystem is going to be required to utilize Our use-case is to build and register a Tailwind-based utility StyleSheet and have our components utilize those utility styles (i.e., class names). A large chunk of our utility styles are driven by variables for the purpose of being able to switch between light and dark mode. Because our utility styles were variable driven, it was required to wrap our components to watch for changes (e.g., Any information you can provide would be greatly appreciated! Thanks!
|
ed230d9
to
3d347a1
Compare
aca3d30
to
bd482db
Compare
@jgornick This package works quite differently to NativeWind, so some concepts don't carry across. The biggest difference that NativeWind immediately converts This greatly improves performance as dynamic styles no longer cause a re-render high in the tree. The But I also don't want to break everything, so I will release a NativeWInd 4 with a compatible |
bd482db
to
c87edc7
Compare
Is this feature ready to use? Do we have any documentation on this? I tried to degit the apps/tailwind repo and ran into |
Just to confirm as the docs seem to state something different. Does this mean we can actually use global css in native? |
@EvanBacon I would also have the same question - is it possible to use CSS and CSS Modules in Expo also with the native parts? Really looking forward to this possibility! 🙌 |
Motivation
Allow native applications to be styled via
.css
and.module.css
files.This PR implements a css extractor and runtime for
View
andText
components. It supports:px
/%
/vw
/vh
/rem
units (the common Tailwind CSS units)prefers-color-scheme
media queriesclassName
supportExecution
This is quite a large PR, so I'll break it down into sections. The actual implementation is quite small (the native runtime is <800 LOC) with the majority of this PR being the test project, integration tests & project boiler plate.
The general flow is
SheetSheet.create
CSSInteropWrapper
CSSInteropWrapper
can calculate these dynamic values (referred from here onwards as flattening) and using signals, create a dependency graph.CSS transfomer
The metro transformer has been split into
webCssTransformer
andnativeCssTransformer
router/packages/expo-styling/src/transformer/index.tsx
Lines 42 to 46 in b0a27b2
webCssTransformer
contains the original logic.The
nativeCssTransformer
converts the CSS to RN style objects and collects metadata (e.g. is this style dynamic). The parsed styles and metadata are passed to a newStyleSheet
class that extends the original React NativeStyleSheet
.router/packages/expo-styling/src/transformer/nativeCssTransform.ts
Lines 21 to 44 in b0a27b2
It exposed two different functions
create
®ister
.create
works like the original create and the styles are returned.register
instead "registers" the styles as globals (to be used inclassName
). Both of these two functions remove the metadata and store it in a Weakmap.router/packages/expo-styling/src/runtime/native/stylesheet.ts
Lines 27 to 37 in b0a27b2
jsxImportSource
A new jsxImportSource is implemented. As this runs on every jsx transform the primary goal here is to reduce overhead. The
View
andText
components are tagged with a function signalling that we will transform their styles. All other components are ignored, and processed as normal.router/packages/expo-styling/src/runtime/render.ts
Lines 1 to 12 in b0a27b2
For tagged components, two things happen
className
props are converted to styles (from the global register) and merged with the style propMost styles are static (eg
padding: 30
), so the function early exits and renders as normal.If the component contains a dynamic style, the it is wrapped in a
CSSInteropWrapper
component.router/packages/expo-styling/src/runtime/native/css-interop.tsx
Lines 31 to 40 in b0a27b2
CSSInteropWrapper
This component creates a Signal computation and flattens the style prop into a single object. As the styles are being flattened, property values will be computed as subscribed to. If a value changes (e.g. the viewport width) the component will render.
router/packages/expo-styling/src/runtime/native/css-interop.tsx
Lines 79 to 96 in b0a27b2
Style flattening
Style flattening is the process of reducing a style prop into a single style object. As it passes over values, it will either calculate the value or set the value as a getter function so it can be computed at a later time.
Defferred computing is require as some values cannot be calculated until after the entire style has been flattened (e.g.
color: rgb(255, 0, 0, var(--text-opacity))
requires the value ofvar(--text-opacity)
to be known.During this flattening process, styles may also have associated media queries. These styles will only pass if the media query passes. The computation will also subscribe to any dependencies of the media query.
router/packages/expo-styling/src/runtime/native/flattenStyle.ts
Lines 37 to 88 in b0a27b2
Test Plan
Unit / integration tests.
For this new feature to work, it needs all the systems mentioned above to work in unison. Because of this I've focused on integration testing over unit testing.
A typically integration test takes the form of
Test project
A new Tailwind based test project has been created in
/apps