Skip to content

Nested at-rules are not sorted #1846

@michaelabrahamian

Description

@michaelabrahamian

Describe the bug

When at-rules are nested inside another at-rule, they are not sorted.

This is unexpected and can cause significant production issues (especially when production apps are configured differently to development environments).

Example:

const styles = cssMap({
  root: {
    '@supports not (-moz-appearance: none)': {
      '@media (prefers-reduced-motion: no-preference) and (min-width: 64rem)': {
        color: 'blue',
      },
      '@media (prefers-reduced-motion: no-preference)': {
        color: 'green',
      },
    },
  },
});

In this example, we would expect color: 'blue' to "win" on large viewports (width than 64rem). However this doesn't consistently happen in apps.

This appears to be because the sortAtomicStyleSheet function only looks at the nodes at the first level.

There is a comment here also confirming this: https://github.com/atlassian-labs/compiled/blob/master/packages/css/src/plugins/sort-atomic-style-sheet.ts#L39

 * Only top level CSS rules will be sorted.

Nesting at-rules like this is valid CSS: https://css-tricks.com/can-you-nest-media-and-support-queries/

To Reproduce

You can test the current behaviour by adding the following tests to packages/css/src/plugins/tests/sort-at-rules.test.ts:

it.only('should support nested queries with multiple at-rules', () => {
  const actual = transform(`
    @supports not (height: 1lh) {
      color: red;
      @media (prefers-reduced-motion: no-preference) and (width > 1500px) {
        color: blue;
      }

      @media (prefers-reduced-motion: no-preference) {
        color: green;
      }
    }
  `);

  expect(actual).toMatchInlineSnapshot(`
    "
            @supports not (height: 1lh) {
              color: red;
              @media (prefers-reduced-motion: no-preference) and (width > 1500px) {
                color: blue;
              }

              @media (prefers-reduced-motion: no-preference) {
                color: green;
              }
            }
          "
  `);
});

it.only('should support nested queries with multiple at-rules', () => {
  const actual = transform(`
    color: red;
    @media (prefers-reduced-motion: no-preference) and (width > 1500px) {
      @supports not (height: 1lh) {
        color: blue;
      }
    }

    @media (prefers-reduced-motion: no-preference) {
      @supports not (height: 1lh) {
          color: green;
        }
      }
  `);

  expect(actual).toMatchInlineSnapshot(`
    "
            color: red;

            @media (prefers-reduced-motion: no-preference) {
              @supports not (height: 1lh) {
                  color: green;
                }
              }
            @media (prefers-reduced-motion: no-preference) and (width > 1500px) {
              @supports not (height: 1lh) {
                color: blue;
              }
            }
          "
  `);
});

Expected behavior
Nested media queries should be sorted.

At a minimum, some improved guidance and lint rules to make people aware would be really helpful.

Workaround
The current workaround we are using is just making sure all at-rules that require sorting (e.g. @media) are at the top level. For my specific example, flipping the order of the at-rules to place the @media at the top level and have @supports nested at the second level would make sure the media queries are sorted.

Additional context
Related issue about not merging duplicate styles in nested media queries: #1847

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug 🐛Something isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions