csstools / postcss-plugins

PostCSS Tools and Plugins
https://preset-env.cssdb.org/
MIT No Attribution
904 stars 71 forks source link

Postcss-light-dark-function Breaks With Cascade Layers #1348

Closed Jothsa closed 7 months ago

Jothsa commented 7 months ago

Bug description

Postcss-light-dark-function does not work with css cascade layers. I have variables and the rules to switch the color-scheme based on an attribute on the :root element defined in a css cascade layer. However, postcss-light-dark-function adds media queries to set the values of --csstools-color-scheme--light and --csstools-color-scheme--dark based on the value of prefers-color-scheme outside of any cascade layer. This causes those rules to have a higher priority than the rules which change the variables and color schemes based on an html attribute. This conflict results in the color-scheme attribute changing to reflect the attribute on the :root element, but the values of --csstools-color-scheme--light and --csstools-color-scheme--dark remaining set based on the value of prefers-color-scheme.

Source CSS

@layer theme {
     :root {
         color-scheme: light;
         --bg: light-dark(white, black);

         &[data-theme-mode="auto"], &:not([data-theme-mode="light"], [data-theme-mode="dark"]) {
             @media (prefers-color-scheme: dark) {
                 color-scheme: dark;
            }
             @media (prefers-color-scheme: light) {
                 color-scheme: light;
            }
        }
         &[data-theme-mode="light"] {
             color-scheme: light;
        }
        &[data-theme-mode="dark"] {
            color-scheme: dark;
        }
    }
}

Expected CSS

@layer theme {
  :root {
    --csstools-color-scheme--light: initial;
    --csstools-color-scheme--dark:  ;
    color-scheme: light;
    --bg: var(--csstools-color-scheme--light, white)
      var(--csstools-color-scheme--dark, black);

    &[data-theme-mode='auto'],
    &:not([data-theme-mode='light'], [data-theme-mode='dark']) {
      @media (prefers-color-scheme: dark) {
        --csstools-color-scheme--light:  ;
        --csstools-color-scheme--dark: initial;
        color-scheme: dark;
      }
      @media (prefers-color-scheme: light) {
        --csstools-color-scheme--light: initial;
        --csstools-color-scheme--dark:  ;
        color-scheme: light;
      }
    }
    &[data-theme-mode='light'] {
      --csstools-color-scheme--light: initial;
      --csstools-color-scheme--dark:  ;
      color-scheme: light;
    }
    &[data-theme-mode='dark'] {
      --csstools-color-scheme--light:  ;
      --csstools-color-scheme--dark: initial;
      color-scheme: dark;
    }
    & * {
      --bg: var(--csstools-color-scheme--light, white)
        var(--csstools-color-scheme--dark, black);
    }
  }

  @supports (color: light-dark(red, red)) {
    :root {
      --bg: light-dark(white, black);
    }
  }
}

Actual CSS

@layer theme {
  :root {
    --csstools-color-scheme--light: initial;
    --csstools-color-scheme--dark:  ;
    color-scheme: light;
    --bg: var(--csstools-color-scheme--light, white)
      var(--csstools-color-scheme--dark, black);

    &[data-theme-mode='auto'],
    &:not([data-theme-mode='light'], [data-theme-mode='dark']) {
      @media (prefers-color-scheme: dark) {
        --csstools-color-scheme--light:  ;
        --csstools-color-scheme--dark: initial;
        color-scheme: dark;
      }
      @media (prefers-color-scheme: light) {
        --csstools-color-scheme--light: initial;
        --csstools-color-scheme--dark:  ;
        color-scheme: light;
      }
    }
    &[data-theme-mode='light'] {
      --csstools-color-scheme--light: initial;
      --csstools-color-scheme--dark:  ;
      color-scheme: light;
    }
    &[data-theme-mode='dark'] {
      --csstools-color-scheme--light:  ;
      --csstools-color-scheme--dark: initial;
      color-scheme: dark;
    }
    & * {
      --bg: var(--csstools-color-scheme--light, white)
        var(--csstools-color-scheme--dark, black);
    }
  }

  @supports (color: light-dark(red, red)) {
    :root {
      --bg: light-dark(white, black);
    }
  }
}
:root {
  --csstools-color-scheme--light: initial;
  --csstools-color-scheme--dark:  ;
}
@media (prefers-color-scheme: dark) {
  :root {
    --csstools-color-scheme--light:  ;
    --csstools-color-scheme--dark: initial;
  }
}

Playgound example

https://preset-env.cssdb.org/playground/#JTdCJTIyc291cmNlJTIyJTNBJTIyJTQwbGF5ZXIlMjB0aGVtZSUyMCU3QiU1Q24lMjAlMjAlMjAlMjAlMjAlM0Fyb290JTIwJTdCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMGNvbG9yLXNjaGVtZSUzQSUyMGxpZ2h0JTNCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMC0tYmclM0ElMjBsaWdodC1kYXJrKHdoaXRlJTJDJTIwYmxhY2spJTNCJTVDbiU1Q24lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjYlNUJkYXRhLXRoZW1lLW1vZGUlM0QlNUMlMjJhdXRvJTVDJTIyJTVEJTJDJTIwJTI2JTNBbm90KCU1QmRhdGEtdGhlbWUtbW9kZSUzRCU1QyUyMmxpZ2h0JTVDJTIyJTVEJTJDJTIwJTVCZGF0YS10aGVtZS1tb2RlJTNEJTVDJTIyZGFyayU1QyUyMiU1RCklMjAlN0IlNUNuJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTQwbWVkaWElMjAocHJlZmVycy1jb2xvci1zY2hlbWUlM0ElMjBkYXJrKSUyMCU3QiU1Q24lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjBjb2xvci1zY2hlbWUlM0ElMjBkYXJrJTNCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCU3RCU1Q24lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlNDBtZWRpYSUyMChwcmVmZXJzLWNvbG9yLXNjaGVtZSUzQSUyMGxpZ2h0KSUyMCU3QiU1Q24lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjBjb2xvci1zY2hlbWUlM0ElMjBsaWdodCUzQiU1Q24lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlN0QlNUNuJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTdEJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyNiU1QmRhdGEtdGhlbWUtbW9kZSUzRCU1QyUyMmxpZ2h0JTVDJTIyJTVEJTIwJTdCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMGNvbG9yLXNjaGVtZSUzQSUyMGxpZ2h0JTNCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCU3RCU1Q24lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjYlNUJkYXRhLXRoZW1lLW1vZGUlM0QlNUMlMjJkYXJrJTVDJTIyJTVEJTIwJTdCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMGNvbG9yLXNjaGVtZSUzQSUyMGRhcmslM0IlNUNuJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTdEJTVDbiUyMCUyMCUyMCUyMCU3RCU1Q24lN0QlMjIlMkMlMjJjb25maWclMjIlM0ElN0IlMjJicm93c2VycyUyMiUzQSU1QiUyMiUzRSUyMDElMjUlMjIlNUQlMkMlMjJtaW5pbXVtVmVuZG9ySW1wbGVtZW50YXRpb25zJTIyJTNBMCUyQyUyMnN0YWdlJTIyJTNBMiUyQyUyMmxvZ2ljYWwlMjIlM0ElN0IlMjJpbmxpbmVEaXJlY3Rpb24lMjIlM0ElMjJsZWZ0LXRvLXJpZ2h0JTIyJTJDJTIyYmxvY2tEaXJlY3Rpb24lMjIlM0ElMjJ0b3AtdG8tYm90dG9tJTIyJTdEJTJDJTIyZmVhdHVyZXMlMjIlM0ElN0IlMjJuZXN0aW5nLXJ1bGVzJTIyJTNBJTVCJTIyYXV0byUyMiUyQyU3QiUyMmVkaXRpb24lMjIlM0ElMjIyMDI0LTAyJTIyJTdEJTVEJTdEJTdEJTdE

Does it happen with npx @csstools/csstools-cli <plugin-name> minimal-example.css?

Yes

Debug output

No response

Extra config

No response

What plugin are you experiencing this issue on?

PostCSS Light Dark Function

Plugin version

1.0.2

What OS are you experiencing this on?

macOS

Node Version

20.10.0

Validations

Would you like to open a PR for this bug?

romainmenke commented 7 months ago

@Jothsa Thank you for the bug report.

I think this is a much smaller example of the issue :

@layer theme {
  :root {
    color-scheme: dark;
    --bg: light-dark(white, black);
  }
}

https://preset-env.cssdb.org/playground/#JTdCJTIyc291cmNlJTIyJTNBJTIyJTQwbGF5ZXIlMjB0aGVtZSUyMCU3QiU1Q24lMjAlMjAlM0Fyb290JTIwJTdCJTVDbiUyMCUyMCUyMCUyMGNvbG9yLXNjaGVtZSUzQSUyMGRhcmslM0IlNUNuJTIwJTIwJTIwJTIwLS1iZyUzQSUyMGxpZ2h0LWRhcmsod2hpdGUlMkMlMjBibGFjayklM0IlNUNuJTIwJTIwJTdEJTVDbiU3RCUyMiUyQyUyMmNvbmZpZyUyMiUzQSU3QiUyMmJyb3dzZXJzJTIyJTNBJTVCJTIyY2hyb21lJTIwMTIwJTIyJTVEJTJDJTIybWluaW11bVZlbmRvckltcGxlbWVudGF0aW9ucyUyMiUzQTAlMkMlMjJzdGFnZSUyMiUzQTIlMkMlMjJsb2dpY2FsJTIyJTNBJTdCJTIyaW5saW5lRGlyZWN0aW9uJTIyJTNBJTIybGVmdC10by1yaWdodCUyMiUyQyUyMmJsb2NrRGlyZWN0aW9uJTIyJTNBJTIydG9wLXRvLWJvdHRvbSUyMiU3RCUyQyUyMmZlYXR1cmVzJTIyJTNBJTdCJTIybmVzdGluZy1ydWxlcyUyMiUzQSU1QiUyMmF1dG8lMjIlMkMlN0IlMjJlZGl0aW9uJTIyJTNBJTIyMjAyNC0wMiUyMiU3RCU1RCU3RCU3RCU3RA==

romainmenke commented 7 months ago

@Jothsa we've just rolled out an update for this plugin that will likely fix your issue. We use a slightly different approach and no longer add extra styles on :root {}.

We haven't updated postcss-preset-env yet, so make sure to update transitive dependencies with something like npm update if you use postcss-preset-env.

Would be awesome if you could let us know if everything is now working as expected. Thank you again for reporting this.

romainmenke commented 7 months ago
Totally unrelated Why aren't you using something like this? ```css @layer theme { :root { color-scheme: light dark; --bg: light-dark(white, black); &[data-theme-mode="light"] { color-scheme: light; } &[data-theme-mode="dark"] { color-scheme: dark; } } } ``` `color-scheme: light dark` is intended to say: "This page supports dark and light color schemes". So setting this is an anti-pattern: ```css &[data-theme-mode="auto"], &:not([data-theme-mode="light"], [data-theme-mode="dark"]) { @media (prefers-color-scheme: dark) { color-scheme: dark; } @media (prefers-color-scheme: light) { color-scheme: light; } } ``` But I obviously do not know your full code base, so maybe there is complexity you are trying to work around that isn't visible here :) Maybe also this : ```css @layer theme { :root { color-scheme: light; --bg: light-dark(white, black); &[data-theme-mode~="light"][data-theme-mode~="dark"] { color-scheme: light dark; } &[data-theme-mode="light"] { color-scheme: light; } &[data-theme-mode="dark"] { color-scheme: dark; } } } ``` ------ Even better version: ```css @layer theme { :root { color-scheme: light dark; --bg: light-dark(white, black); } [data-theme-mode="light"] { color-scheme: light; } [data-theme-mode="dark"] { color-scheme: dark; } } ``` This allows you to dynamically determine the theme mode even on elements that aren't the `:root`. Using nesting here is unnecessary complexity.
Jothsa commented 7 months ago

It's working fine now. Thanks so much!

Yeah, tbh, I'm not sure what happened with the css I was using in the example πŸ˜… I was messing around with it to see different combinations of selectors could get the plugin to work. I was using this css:

@layer theme {
  :root {
    color-scheme: light;
    --bg: white;

    @media (prefers-color-scheme: dark) {
      &:not[data-theme-mode='light'] {
        color-scheme: dark;
        --bg: black;
      }
    }

    &[data-theme-mode='dark'] {
      color-scheme: dark;
      --bg: black;
    }
  }
}

I wanted to make sure that if the user specifically chooses a theme the color-scheme property will be set to whatever that theme is and if the data-theme-mode attribute is set to auto the variables will update correctly. Now that light-dark function removes the need to redefine the variables, I will switch to one of your examples. I'll probably use the last one.

Thanks again for the quick response and the advice on my css. I really appreciate it! On my way to go redefine a bunch of variables now πŸ˜†

romainmenke commented 7 months ago

Thank you for confirming πŸ™‡ Always happy to help!