What happened?
Exclusive CSS media-query operators lose their exclusivity when compiled for native platforms: (width > 402px) matches at exactly 402 points, and (width < 402px) also includes 402.
This caused a visible 4pt alignment error on a 402pt-wide iPhone: a content gutter intended to change from 16pt to 20pt only above 402pt instead changed at 402pt, while the native navigation title retained its 16pt inset.
According to CSS Media Queries range semantics, > and < must exclude equality; >= and <= must include it.
Steps to Reproduce
In a configured Uniwind app, add:
@import "tailwindcss";
@import "uniwind";
.probe-gt { padding-left: 16px; }
@media (width > 402px) {
.probe-gt { padding-left: 20px; }
}
Render:
<View className="probe-gt">
<Text>Margin probe</Text>
</View>
Run on a 402pt-wide iOS simulator (reproduced on iPhone 17 Pro, iOS 26.5):
- Expected: 16pt left padding at width 402.
- Actual: 20pt left padding at width 402.
- Below the boundary, width 393 correctly uses 16pt; widths 403 and 440 correctly use 20pt.
The linked Node reproduction also compiles generic > and < fixtures through the actual Uniwind Metro transformer for both iOS and Android. It captures the generated native stylesheet before Metro's downstream JS transform. On 1.10.1, all four cases incorrectly include equality:
| Platform |
Query |
Compiled bound |
Includes width 402? |
Expected |
| iOS |
width > 402px |
minWidth: 402 |
Yes |
No |
| iOS |
width < 402px |
maxWidth: 402 |
Yes |
No |
| Android |
width > 402px |
minWidth: 402 |
Yes |
No |
| Android |
width < 402px |
maxWidth: 402 |
Yes |
No |
Snack or Repository Link
https://gist.github.com/maxlapides/453a14dabd1f6ea5575ce362789c56f9
The gist contains an executable compiler reproduction and a minimal visual example. It uses no application code or application CSS.
Uniwind version
1.10.1 (reproduced).
The same compiler and runtime logic is also present on current main at 44388b459f9170ff0f34a9f7ca5488a1a72fc761. I have not run the reproduction against the latest published version, 1.12.0.
React Native Version
0.86.2
Platforms
- iOS: reproduced in the running app and generated native stylesheet.
- Android: reproduced in the generated native stylesheet; no Android device test performed.
Expo
Yes — Expo 57.0.16.
Likely cause
The media-query compiler maps greater-than and greater-than-equal to the same minWidth, and less-than and less-than-equal to the same maxWidth.
The native style resolver then checks both bounds inclusively, so the original operator is lost.
Workaround / possible fix
For our whole-point viewport breakpoint, explicitly using width >= 403px avoids the accidental match at 402. This is only a workaround: it is not generally equivalent to width > 402px for fractional widths.
An upstream fix should preserve inclusive versus exclusive comparisons through compilation and native resolution, including fractional widths. Useful regression cases would compare >, >=, <, and <= below, exactly at, and above the boundary.
Additional information 〰
What happened?
Exclusive CSS media-query operators lose their exclusivity when compiled for native platforms:
(width > 402px)matches at exactly 402 points, and(width < 402px)also includes 402.This caused a visible 4pt alignment error on a 402pt-wide iPhone: a content gutter intended to change from 16pt to 20pt only above 402pt instead changed at 402pt, while the native navigation title retained its 16pt inset.
According to CSS Media Queries range semantics,
>and<must exclude equality;>=and<=must include it.Steps to Reproduce
In a configured Uniwind app, add:
Render:
Run on a 402pt-wide iOS simulator (reproduced on iPhone 17 Pro, iOS 26.5):
The linked Node reproduction also compiles generic
>and<fixtures through the actual Uniwind Metro transformer for both iOS and Android. It captures the generated native stylesheet before Metro's downstream JS transform. On 1.10.1, all four cases incorrectly include equality:width > 402pxminWidth: 402width < 402pxmaxWidth: 402width > 402pxminWidth: 402width < 402pxmaxWidth: 402Snack or Repository Link
https://gist.github.com/maxlapides/453a14dabd1f6ea5575ce362789c56f9
The gist contains an executable compiler reproduction and a minimal visual example. It uses no application code or application CSS.
Uniwind version
1.10.1 (reproduced).
The same compiler and runtime logic is also present on current
mainat44388b459f9170ff0f34a9f7ca5488a1a72fc761. I have not run the reproduction against the latest published version, 1.12.0.React Native Version
0.86.2
Platforms
Expo
Yes — Expo 57.0.16.
Likely cause
The media-query compiler maps
greater-thanandgreater-than-equalto the sameminWidth, andless-thanandless-than-equalto the samemaxWidth.The native style resolver then checks both bounds inclusively, so the original operator is lost.
Workaround / possible fix
For our whole-point viewport breakpoint, explicitly using
width >= 403pxavoids the accidental match at 402. This is only a workaround: it is not generally equivalent towidth > 402pxfor fractional widths.An upstream fix should preserve inclusive versus exclusive comparisons through compilation and native resolution, including fractional widths. Useful regression cases would compare
>,>=,<, and<=below, exactly at, and above the boundary.Additional information 〰