Skip to content

Exclusive media-query operators > and < become inclusive on native platforms #674

Description

@maxlapides

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 〰

  • I've searched for similar issues in this repository and found none.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions