postcss-js icon indicating copy to clipboard operation
postcss-js copied to clipboard

Media query ordering changes when converting to object

Open AlanBreck opened this issue 2 years ago • 7 comments

I have a problem where a given rule's default definition (.leoButton.isSecondary) gets placed after the media query, which changes the behavior of the rendering. As such, the default overrides the media query, and the media query never takes effect.

Is this a known bug? Is there any known fix for this?

Input

.button.isPrimary {
  --bg: var(--color-secondary-light-mode-70);
  --bg-hover: var(--color-secondary-light-mode-80);
  --bg-active: var(--color-secondary-light-mode-90);
  --bg-focus: var(--color-secondary-light-mode-70);
  --bg-loading: var(--color-secondary-light-mode-50);
  --bg-disabled: var(--color-gray-70);
  --color: white;
}
@media (prefers-color-scheme: dark) {
  .button.isPrimary {
    --bg: var(--color-secondary-dark-mode-50);
    --bg-hover: var(--color-secondary-dark-mode-60);
    --bg-active: var(--color-secondary-dark-mode-70);
    --bg-loading: var(--color-secondary-dark-mode-60);
  }
}
.button.isSecondary {
  --bg: white;
  --bg-active: --color-gray-20;
  --color: var(--color-gray-90);
  --color-hover: var(--color-secondary-light-mode-70);
  --color-loading: var(--color-gray-70);
  --border-width: 1px;
  --border-color: var(--color-gray-30);
}
@media (prefers-color-scheme: dark) {
  .button.isSecondary {
    --bg: black;
    --color-hover: var(--color-secondary-dark-mode-80);
  }
}

Output

{
  ".button.isPrimary": {
    "--bg": "var(--color-secondary-light-mode-70)",
    "--bg-hover": "var(--color-secondary-light-mode-80)",
    "--bg-active": "var(--color-secondary-light-mode-90)",
    "--bg-focus": "var(--color-secondary-light-mode-70)",
    "--bg-loading": "var(--color-secondary-light-mode-50)",
    "--bg-disabled": "var(--color-gray-70)",
    "--color": "white"
  },
  "@media (prefers-color-scheme: dark)": [
    {
      ".button.isPrimary": {
        "--bg": "var(--color-secondary-dark-mode-50)",
        "--bg-hover": "var(--color-secondary-dark-mode-60)",
        "--bg-active": "var(--color-secondary-dark-mode-70)",
        "--bg-loading": "var(--color-secondary-dark-mode-60)"
      }
    },
    {
      ".button.isSecondary": {
        "--bg": "black",
        "--color-hover": "var(--color-secondary-dark-mode-80)"
      }
    }
  ],
  ".button.isSecondary": {
    "--bg": "white",
    "--bg-active": "--color-gray-20",
    "--color": "var(--color-gray-90)",
    "--color-hover": "var(--color-secondary-light-mode-70)",
    "--color-loading": "var(--color-gray-70)",
    "--border-width": "1px",
    "--border-color": "var(--color-gray-30)"
  }
}

AlanBreck avatar Aug 11 '22 20:08 AlanBreck

Can you create a smaller example to understand what is happening?

ai avatar Aug 11 '22 20:08 ai

Sure thing. I've edited my original issue to include the bare minimum to reproduce.

AlanBreck avatar Aug 11 '22 20:08 AlanBreck

I got the problem, it happens because you have 2 media queries. JS object doesn’t allow having 2 properties with the same key, so we try to collapse them.

There will not be a proper solution for this limit.

Here is what we can do:

  1. If you can, avoid object format for CSS. It is just not a production ready solution and a hack.
  2. You can use another PostCSS plugin to combine at-media in the end of the file. It still will be dangerous and could create another issue.

ai avatar Aug 11 '22 20:08 ai

The third option is to change objectifier.js to keep merged at-rule at the position of the latest merging at-rule. Again, it is not a solution, but a hack, since we can create another conflict.

But maybe this strategy will create less issue.

If you want to you try to help me creating a PR.

ai avatar Aug 11 '22 20:08 ai

Could a possible fourth option be to output an array of objects instead of an object? I know that Tailwind (which is my use case) accepts this syntax (see very end of that page for example). I'm not sure how widely acceptable that would be, though.

AlanBreck avatar Aug 11 '22 20:08 AlanBreck

Could a possible fourth option be to output an array of objects instead of an object?

We can generate array in case when we have different media with the same params.

ai avatar Aug 11 '22 20:08 ai

  1. You can use another PostCSS plugin to combine at-media in the end of the file. It still will be dangerous and could create another issue.

The sort-media-queries worked quite nicely.

AlanBreck avatar Aug 11 '22 21:08 AlanBreck