Skip to content

@stylexjs/sort-keys ESLint rule breaks max-width media query cascade when using enableMediaQueryOrder #1416

@salvadorplj

Description

@salvadorplj

Description

The @stylexjs/sort-keys ESLint rule sorts style properties alphabetically, including media query keys inside responsive objects. This conflicts with CSS cascade requirements for max-width media queries, where larger breakpoints must appear before smaller ones in source order.

Even when following all documented best practices:

  • Using enableMediaQueryOrder: true (as recommended in v0.15.0+)
  • Using inline string literals for media queries (required for Vite compatibility)
  • Ordering breakpoints correctly for max-width cascade (larger first)

Running eslint --fix will re-sort the media queries alphabetically, breaking the cascade.

Reproduction

Setup (following best practices)

vite.config.js:

styleXUnplugin.vite({
  enableMediaQueryOrder: true,  // Recommended in v0.15.0+
})

eslint.config.mjs:

{
  plugins: { "@stylexjs": stylexPlugin },
  rules: {
    "@stylexjs/sort-keys": "warn",  // Recommended StyleX ESLint rule
  },
}

The Problem

Correct code (larger breakpoint first for max-width cascade):

const styles = stylex.create({
  container: {
    padding: {
      default: "2rem",
      "@media (max-width: 768px)": "1.5rem",  // tablet
      "@media (max-width: 640px)": "1rem",    // mobile (overrides tablet)
    },
  },
});

After running eslint --fix:

const styles = stylex.create({
  container: {
    padding: {
      default: "2rem",
      "@media (max-width: 640px)": "1rem",    // now first (alphabetically)
      "@media (max-width: 768px)": "1.5rem",  // now last - WINS on mobile!
    },
  },
});

Why This Breaks

With enableMediaQueryOrder: true, StyleX makes "later source order wins." After ESLint sorts:

  • 640px appears first in source
  • 768px appears last in source → takes precedence
  • On a 500px viewport, BOTH queries match, but 768px wins because it's later
  • Result: Mobile devices get 1.5rem instead of 1rem

Expected Behavior

The @stylexjs/sort-keys rule should either:

  1. Skip sorting inside objects containing media query keys - Detect @media keys and preserve author order
  2. Sort media queries by specificity - For max-width, sort descending (larger first); for min-width, sort ascending (smaller first)
  3. Provide a configuration option - Allow disabling sort-keys for media query objects

Current Workaround

We must add ESLint disable blocks around every responsive property:

const styles = stylex.create({
  container: {
    /* eslint-disable @stylexjs/sort-keys -- max-width cascade: larger breakpoints first */
    padding: {
      default: "2rem",
      "@media (max-width: 768px)": "1.5rem",
      "@media (max-width: 640px)": "1rem",
    },
    /* eslint-enable @stylexjs/sort-keys */
  },
});

This is tedious and error-prone, especially since the ESLint warning says:

StyleX property key "@media (max-width: 640px)" should be above "@media (max-width: 768px)"

This message actively encourages the wrong order for max-width queries.

Environment

  • @stylexjs/stylex: 0.10.1
  • @stylexjs/eslint-plugin: 0.10.1
  • @stylexjs/unplugin: 0.6.0
  • Vite: 6.0.5
  • Node: 22.x

Related Issues

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