[w3c] ☂️ Web Styles (Part 1) umbrella issue
Add support for Web styles to components
This is the umbrella issue for basic React DOM / Web style additions to React Native components, as described in this proposal: "RFC: Reduce fragmentation across platforms".
Each of the tasks listed below can be tackled with individual PRs that link back to this issue. Not every task has a known solution or implementation yet, so feel free to discuss implementation details in the comments. Each new style property should take priority over any existing equivalents.
Styles
Basic extensions (All available in React Native 0.71)
- [x]
aspectRatiosupport for string values, i.e.,'16 / 9', to align with CSS. https://github.com/facebook/react-native/pull/34629 - [x]
fontVariantsupport for space-separated string values, i.e.,'small-caps common-ligatures'.- [x] https://github.com/facebook/react-native/pull/34641
- [x] https://github.com/facebook/react-native/pull/36740
- [x]
fontWeightsupport for number values, i.e.,900. https://github.com/facebook/react-native/pull/34598 - [x]
transformsupport for string values, i.e.,'scaleX(2) translateX(20px)'. https://github.com/facebook/react-native/pull/34660#pullrequestreview-1104804653
Examples:
<View
style={{
aspectRatio: '16 / 9',
transform: 'scaleX(2) translateX(20px)'
}}
>
<Text
style={{
fontVariant: 'small-caps common-ligatures',
fontWeight: 900
}}
>
Equivalents
Available in React Native 0.71
- [x]
direction. - [x] Add
objectFit. Partial equivalent to theresizeModestyle and prop of<Image>. https://github.com/facebook/react-native/pull/34576- [x] Map
objectFit === 'contain'toresizeMode = 'contain'. - [x] Map
objectFit === 'cover'toresizeMode = 'cover' - [x] Map
objectFit === 'fill'toresizeMode = 'stretch' - [x] Map
objectFit === 'scale-down'toresizeMode = 'contain' - [ ] Support
objectFitvalue of'none'.
- [x] Map
- [x] Add
pointerEvents. Equivalent to thepointerEventsprop of<View>. https://github.com/facebook/react-native/pull/34586/- [x] Retain the React Native specific
box-noneandbox-onlyvalues.
- [x] Retain the React Native specific
- [x] Add
userSelect. Equivalent to usingselectableprop on<Text>. https://github.com/facebook/react-native/pull/34575 - [x] Add
verticalAlign. https://github.com/facebook/react-native/pull/34567- [x] Map
verticalAligntotextAlignVertical. - [x] Map
verticalAlign === 'middle'totextAlignVertical = 'center';
- [x] Map
Available in React Native 0.72
- Add CSS Logical Properties
- [x] Logical Border Radius
- [x] https://github.com/facebook/react-native/pull/35342
- [x] https://github.com/facebook/react-native/pull/35572
- [x]
borderEndEndRadiusis equivalent toborderBottomEndRadius. - [x]
borderEndStartRadiusis equivalent toborderBottomStartRadius. - [x]
borderStartEndRadiusis equivalent toborderTopEndRadius. - [x]
borderStartStartRadiusis equivalent toborderTopStartRadius.
- [x] Logical margin (Fabric-only)
- [x] Map
marginInlineStarttomarginStart.
- [x] Map
marginInlineEndtomarginEnd. - [x] Map
marginBlockStarttomarginTop. - [x] Map
marginBlockEndtomarginBottom. - [x] Map
marginBlocktomarginVertical. - [x] Map
marginInlinetomarginHorizontal.
- [x] Map
- [x] Logical padding (Fabric-only)
- [x] Map
paddingInlineStarttopaddingStart. - [x] Map
paddingInlineEndtopaddingEnd. - [x] Map
paddingBlockStarttopaddingTop. - [x] Map
paddingBlockEndtopaddingBottom. - [x] Map
paddingBlocktopaddingVertical. - [x] Map
paddingInlinetopaddingHorizontal.
- [x] Map
- [x] Logical insets (Fabric-only) https://github.com/facebook/react-native/commit/9669c10afceef65626c82149210afc07d47df98b
- [x]
insetis equivalent totop&bottom&right&left. - [x]
insetBlockis equivalent totop&bottom. - [x]
insetBlockEndis equivalent tobottom. - [x]
insetBlockStartis equivalent totop. - [x]
insetInlineis equivalent toright&left. - [x]
insetInlineEndis equivalent torightorleft. - [x]
insetInlineStartis equivalent torightorleft.
- [x]
- [x] https://github.com/facebook/react-native/pull/35999
- [x]
borderBlockColoris equivalent toborderTopColor&borderBottomColor. - [x]
borderBlockEndColoris equivalent toborderBottomColor. - [x]
borderBlockStartColoris equivalent toborderTopColor.
- [x] Logical Border Radius
Outstanding (version to be determined)
- [ ]
borderStylevalue of'none'. - [ ]
transformOriginhttps://github.com/facebook/react-native/pull/37606 - [ ] Add CSS Logical Properties.
- [ ] https://github.com/facebook/react-native/pull/36046
- [ ] https://github.com/facebook/react-native/pull/36242
- [ ]
borderInlineColoris equivalent toborderEndColor&borderStartColor. - [ ]
borderInlineEndColoris equivalent toborderEndColor. - [ ]
borderInlineStartColoris equivalent toborderStartColor. - [ ]
borderBlockStyleis equivalent toborderTopStyle&borderBottomStyle. - [ ]
borderBlockEndStyleis equivalent toborderBottomStyle. - [ ]
borderBlockStartStyleis equivalent toborderTopStyle. - [ ]
borderInlineStyleis equivalent toborderEndStyle&borderStartStyle. - [ ]
borderInlineEndStyleis equivalent toborderEndStyle. - [ ]
borderInlineStartStyleis equivalent toborderStartStyle. - [ ]
borderBlockWidthis equivalent toborderTopWidth&borderBottomWidth. - [ ]
borderBlockEndWidthis equivalent toborderBottomWidth. - [ ]
borderBlockStartWidthis equivalent toborderTopWidth. - [ ]
borderInlineWidthis equivalent toborderEndWidth&borderStartWidth. - [ ]
borderInlineEndWidthis equivalent toborderEndWidth. - [ ]
borderInlineStartWidthis equivalent toborderStartWidth.
Examples:
<View
style={{
pointerEvents: 'none'
}}
>
<Text
style={{
userSelect: 'none',
verticalAlign: 'middle'
}}
>
<Image
style={{
objectFit: 'cover'
}}
>
New features
Available in React Native 0.71
- [x] Add expanded support for CSS Colors, e.g.,
hsla(). Potentially via Colorjs.io. https://github.com/facebook/react-native/pull/34600 - [x] Add
gap. https://github.com/facebook/yoga/pull/1116
<View
style={{
boxShadow: '1px 1px 1px 1px #eee',
backgroundColor: 'hsla(50,50,50,0.5)',
backgroundImage: 'url(https://image.png)',
pointerEvents: 'none',
transform: 'scale(0.9)',
width: '5rem'
}}
>
<Text
style={{
textShadow: '1px 1px 1px #eee',
userSelect: 'none',
verticalAlign: 'middle'
}}
>
<Image
style={{
objectFit: 'cover'
}}
>
See companion issue related to props https://github.com/facebook/react-native/issues/34424
Would flex gap apply here, or does that fall out of scope since it's still missing in Yoga?
From experience, the lack of gap support is one of the worst parts of styling on native vs. web. Shadows are up there too, but I use them less often.
It would be great to add objectPosition for images too.
Would flex
gapapply here, or does that fall out of scope since it's still missing in Yoga?
Yes, that needs to be added to Yoga which is a whole other project.
It would be great to add
objectPositionfor images too.
Done.
please support for 'transform-origin' and 'display:grid'
Percentage values for translate would be good. We already have this on web.
Not sure if it's related to yoga, like flex gap.
translateX(50%), translateY(100%)
Yes, that needs to be added to Yoga which is a whole other project.
already done, waiting for merge for 6 months
- Add expanded support for CSS Colors, e.g.,
hsla(). Potentially via Colorjs.io.
Hi @necolas, which CSS Color Module Level do we wanna meet? React native already supports rgb(), rgba(), #rgb, #rrggbb, #rgba, #rrggbbaa, hsl(), hsla(), colors as int values (in RGB mode) and named colors, what else do we want to support? hwb()? lab()?
Level 4, as Colorjs.io does. Functions should also support space-separated values, not just comma separated.
I added a new section for improving cross-platform compatibility of Animated.
It should be relatively easy to make Animated a separate package that works on web for React Native for Web. This would allow developers to have the same version of Animated running and avoid me needing to update Animated at arbitrary points in time, which is also tedious and error prone, as I have to check for new sources of crashes and copy-paste-edit dozens of files that use different module syntaxes and internal import paths to RN public APIs, like StyleSheet.
already https://github.com/facebook/yoga/pull/1116, waiting for merge for 6 months
The RN team had planned to move to a new layout engine, but probably not anymore. So since there is an existing PR and it has a chance of being merged soon, I'll add gap too.
HI @necolas i am taking this.
Add [objectFit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit). Partial equivalent to the resizeMode style and prop of <Image>.
Map objectFit === 'contain' to resizeMode = 'contain'.
Map objectFit === 'cover' to resizeMode = 'cover'
Map objectFit === 'fill' to resizeMode = 'stretch'
Map objectFit === 'scale-down' to resizeMode = 'contain'
Updates: PR Here https://github.com/facebook/react-native/pull/34576
What about marginBlock for marginVertical and marginInline for marginHorizontal? And of course same for padding
Ah and also I wanted to note, that lineHeight currently only supports pixel values. What about adding support for values like 1.5 as a multiplier of the current font-size?
What about adding support for values like 1.5 as a multiplier of the current font-size?
That would be great. Unfortunately, it would that be a breaking change since RN currently treats unitless line-height as pixels. So first we'd probably need something like a codemod to change all existing lineHeight values from e.g. 14 to '14px', and then eventually re-introduce unitless value support as a multipler.
Hi @necolas I will be taking this
Add native support for single/multiple CSS box shadows.
Cross-platform shadows to replace Android elevation style, and buggy iOS shadow* styles.```
Please Add This Features List
- Display Grid & Table
- CSS Variable Support (For Global Theming)
- transform-origin
- position fixed&sticky
If flow layout is being added, then could grid layout also be considered? It's absolutely ideal for app layouts, and in many ways is a much simpler model than flexbox.
It would also be nice to add inset, a shorthand for setting left, right, top and bottom.
And also then maybe StyleSheet.absoluteFill and StyleSheet.absoluteFillObject can be deprecated?
I like this idea. inset-inline and inset-block would also be useful. I'll add a bunch more logical properties too once we have an idea of how to support them
@sarulathadurai are you still planning to work on box-shadow?
@sarulathadurai are you still planning to work on box-shadow?
@necolas apologies I can't take it up
@Marcoo09 yes please, there is no existing PR. Since this isn't a feature that can simply be aliased to existing styles, you might want to first share how you plan to implement this feature, so we can run it past the RN team before you get started. Thanks
- Prevent crashes outside of native environments. This is mostly caused by TurboModule calls in
NativeAnimatedHelper. We could either use a*.web.jsmock orPlatformguards. React Native for Web currently comments out theflushQueuefunction body to prevent crashes.
Hi @necolas, I was checking out NativeAnimatedHelper and it seems that we already have some Platform guards, do you mind elaborating a bit more on what needs to be updated?
https://github.com/facebook/react-native/blob/c63133202b015adc6cd94e77069586a619aca4a8/Libraries/Animated/NativeAnimatedHelper.js#L30-L33
The entire flushQueue function had to be commented out https://github.com/necolas/react-native-web/pull/2377/commits/ff72921b57951daf9c37939e9b4be108a1552718#diff-51312a304d660152aec0524a0aca4ca3667cbe604afe7f9c38a88bd8631e962d
The entire flushQueue function had to be commented out necolas/react-native-web@ff72921#diff-51312a304d660152aec0524a0aca4ca3667cbe604afe7f9c38a88bd8631e962d
@necolas perhaps this was fixed by https://github.com/facebook/react-native/commit/5e863fc42c8a2b27f4a785766eb643de9a243b2d? I did some testing uncommenting that function and updating the code on react-native-web and it seems to work fine
I already did something similar to that patch https://github.com/necolas/react-native-web/commit/ff72921b57951daf9c37939e9b4be108a1552718#diff-6ef00d3c5589ec81f1dbc51139b50801209066f362c5d4d4788cd1bebcd0a13b
I did some testing uncommenting that function and updating the code on react-native-web and it seems to work fine
That function calls turbomodules, which don't exist on web, so I'm curious what testing you did. And what code you updated...
I already did something similar to that patch necolas/react-native-web@ff72921#diff-6ef00d3c5589ec81f1dbc51139b50801209066f362c5d4d4788cd1bebcd0a13b
I did some testing uncommenting that function and updating the code on react-native-web and it seems to work fine
That function calls turbomodules, which don't exist on web, so I'm curious what testing you did. And what code you updated...
Actually you commented out the whole flushQueue, https://github.com/facebook/react-native/commit/5e863fc42c8a2b27f4a785766eb643de9a243b2d add some optional chaining to NativeAnimatedModule method calls and most importantly comments out invariant(NativeAnimatedModule, 'Native animated module is not available');
Here is how my `flushQueue` function looks like
flushQueue: function (): void {
// invariant(NativeAnimatedModule, 'Native animated module is not available');
flushQueueTimeout = null;
// Early returns before calling any APIs
if (useSingleOpBatching && singleOpQueue.length === 0) {
return;
}
if (!useSingleOpBatching && queue.length === 0) {
return;
}
if (useSingleOpBatching) {
// Set up event listener for callbacks if it's not set up
if (
!globalEventEmitterGetValueListener ||
!globalEventEmitterAnimationFinishedListener
) {
setupGlobalEventEmitterListeners();
}
// Single op batching doesn't use callback functions, instead we
// use RCTDeviceEventEmitter. This reduces overhead of sending lots of
// JSI functions across to native code; but also, TM infrastructure currently
// does not support packing a function into native arrays.
NativeAnimatedModule?.queueAndExecuteBatchedOperations?.(singleOpQueue);
singleOpQueue.length = 0;
} else {
Platform.OS === 'android' &&
NativeAnimatedModule?.startOperationBatch?.();
for (let q = 0, l = queue.length; q < l; q++) {
queue[q]();
}
queue.length = 0;
Platform.OS === 'android' &&
NativeAnimatedModule?.finishOperationBatch?.();
}
},
And I tested this through the Lists example as it uses an Animated FlatList, am I missing something @necolas ?
https://user-images.githubusercontent.com/11707729/199835759-c7d7fd4e-ac0f-4126-a04b-9c11ad4c2123.mov
OK cool, that looks like it should work then. My only concern is that at any moment an Animated patch could land that breaks web again
OK cool, that looks like it should work then. My only concern is that at any moment an Animated patch could land that breaks web again
Got it, but what would be the best way to prevent this from happening? Would it be ok to add a NativeAnimatedHelper.web.js mock file? I don't see any other web.js files in the repo