Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple defaults for CSS Custom Properties #80

Open
bennypowers opened this issue Jul 12, 2021 · 15 comments
Open

Multiple defaults for CSS Custom Properties #80

bennypowers opened this issue Jul 12, 2021 · 15 comments

Comments

@bennypowers
Copy link
Contributor

Consider this case

class MyElement extends HTMLElement {
  constructor() {
    super().attachShadow({ mode: 'open' }).innerHTML = `
      <style>
        :host { color: var(--my-color, red); }
        @media (prefers-color-scheme: dark) {
          :host { color: var(--my-color, blue); }
        }
      </style>
    `;
  }
}

Should I get

"cssProperties": [
  {
    "name": "--my-color",
    "default": "red"
  }
]

Or

"cssProperties": [
  {
    "name": "--my-color",
    "default": "blue"
  }
]

And if I get either, that's not really correct is it?

We should add a schema facility for declaring multiple defaults and their conditions

@liamdebeasi
Copy link

Should we consider fallback values to be "default"? I would expect a default value to be something that is explicitly set:

:host {
  --my-color: red;
}

That being said I suppose you could have the same problem here:

:host {
  --my-color: red;

  color: var(--my-color);
}

@media (prefers-color-scheme: dark) {
  :host {
    --my-color: blue;
  }
}

One might argue that red is the default here given that blue is only set conditionally, yet red is always set.

@bennypowers
Copy link
Contributor Author

the spec language refers to those as defaults

it's an interesting point you make, but I'm more concerned here with the 'multiple' part than the 'default' part

@daKmoR
Copy link

daKmoR commented Jul 13, 2021

:host {
  --my-color: red;

  color: var(--my-color);
}

that will prevent users to providing --my-color as the host values set in the web components will always take prio over a user provided value.

So it's actually not a "valid" example I would say

@liamdebeasi
Copy link

the spec language refers to those as defaults

it's an interesting point you make, but I'm more concerned here with the 'multiple' part than the 'default' part

Oh interesting! I did not know it considered those defaults. Good to know 👍 .

that will prevent users to providing --my-color as the host values set in the web components will always take prio over a user provided value

Not sure how the spec defines this, but in practice my example does work and users can override the --my-color var: https://codepen.io/liamdebeasi/pen/wvdgvJE


I can't think of a syntax that would elegantly express all of the possible defaults and what criteria makes each value become active.

It might be useful to wait and see how developers are using CEM first before making a decision on this. Right now I think this feature would requires us to make assumptions about how developers are structuring their CSS, and I am not sure we have all the required information to make an informed decision on this.

For additional context, Stencil does not list the default values for CSS Variables in its docs output: https://github.com/ionic-team/stencil/blob/45388e95edb46ef357eb9ae37cd32bbb5bc1ed23/src/declarations/stencil-public-docs.ts#L101-L105.

@bennypowers
Copy link
Contributor Author

The open-wc analyzer, for it's part, does accept a default value in @cssproperty [--my-prop=red]

@liamdebeasi
Copy link

I guess the thing that trips me up here is that I have always considered a "default" value to be a single value that is explicitly set (such as --my-color: red), though in something like Ionic Framework this is not always the case when it comes to theming.

Ionic Framework applies themes based upon light vs. dark mode as well as platform mode ('ios' vs 'md'), so there are different ways of determining the default value. We usually stay away from stating the default value for a CSS variable in the docs because the answer is typically "It depends!" 😄 .

I would be interested to see if others have successfully documented variables with multiple defaults.

@splitinfinities
Copy link

splitinfinities commented Jul 13, 2021

Edit: Included what it would look to take example code and include within the manifest.

Hey there! Long time listener, first time caller.

I do certainly see value in adding a "conditional" concept on certain marked up CSS variables. Something that could take in something to the effect of @prop {conditional} --background: Background of the alert and transforms it. The conditional could be an arbitrary string (Dark Mode), media query (@media (prefers-color-scheme: dark)), or a selector (html[mode="md"]).

With the example:

:host { 
 /**
  * @prop() --my-color: Change the color of the button
  */
  color: var(--my-color, red); 
}

@media (prefers-color-scheme: dark) {
    :host { 
       /**
        * @prop() {@media (prefers-color-scheme: dark)} --my-color: Change the color of the button
        */
        color: var(--my-color, blue); 
    }
}

If a processor wants to automatically infer based on the wrapped media query, they can do so.

After processing, the manifest could then look like:

"cssProperties": [
  {
    "name": "--my-color",
    "description": "Change the color of the button",
    "default": "blue",
  },
  {
    "name": "--my-color",
    "description": "Change the color of the button",
    "default": "red",
    "condition": "@media (prefers-color-scheme: dark)"
  }
]

Risk here is bloat to the file afaik. But I can see some helpful documentation coming out around this feature.

@bennypowers
Copy link
Contributor Author

But what would it look like in the manifest?

@splitinfinities
Copy link

Updated my comment above to be a little more cohesive

@daKmoR
Copy link

daKmoR commented Jul 14, 2021

Not sure how the spec defines this, but in practice my example does work and users can override the --my-color var: https://codepen.io/liamdebeasi/pen/wvdgvJE

interesting - it behaves differently when using constructible stylesheets 🙈

e.g. with inline styles a selector on the main document like

html {
  --my-color: blue;
}

still applies.

When using using constructible stylesheets then it won't 🤔

https://codepen.io/daKmoR/pen/xxdgapY

@castastrophe
Copy link

castastrophe commented Jul 16, 2021

I see these as variations or contexts. They're still defaults for the same CSS property but they are dependent upon certain conditions. What about defining these contexts in connection to the primary definition like this:

"cssProperties": [{
    "name": "--my-color",
    "description": "Change the color of the button",
    "default": "blue",
    "contexts": [{
        "description": "Dark mode button color",
        "default": "red",
        "condition": "@media (prefers-color-scheme: dark)"
    }]
}]

@bennypowers
Copy link
Contributor Author

bennypowers commented Jul 19, 2021

Something to consider: if condition or conditions is meant to be machine-readable, then the values should be strictly identical

i.e. a tool like storybook or <api-viewer> might check a css property's contexts like so:

propertyContext.condition === '@media(prefers-color-scheme: dark)'

Or, it might perform a regexp check that allows for whichever whitespace rules that condition's language allows.

@castastrophe
Copy link

@bennypowers Do you envision condition being an object with pre-defined types perhaps? Conditions can be component or browser-centric.

Some possibilities:

  • media query
  • container query
  • specific attribute present

@bennypowers
Copy link
Contributor Author

tricky

on the one hand we'd prefer to keep our own enum right? makes it easier for consumers
but on the other hand, we'd have to anticipate every case and keep up with browser specs

My 'gut reaction' is to keep it open as a string condition and have tools fight it out. I think there's precedent for that in Type in the schema, which doesn't assume anything

@jrunning
Copy link

Another complication here is that @media and @supports queries can be nested indefinitely (I don't know if container queries can or not and Google didn't give me a quick answer). Here's an example from CSS Tricks:

@media (min-width: 2px) {
  @media (min-width: 1px) {
    @supports (--a: b) {
      @supports (display: flex) {
        body {
          background: pink;
        }
      }
    }
  }
}

If a custom property was defined at some inner level I have no idea how you'd represent it in the manifest or in an e.g. @cssproperty tag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants