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

Can an @apply rule be used inside a custom property bag declaration by the same name? #43

Open
philipwalton opened this issue Oct 19, 2015 · 2 comments

Comments

@philipwalton
Copy link

For example, imagine inside <my-element> you have the following style declaration:

:host {
  /* Sets some defaults */
  --my-element-styles: {
    color: red;

    /* But also applies any inherited declarations. */
    @apply --my-element-styles;
  }
}

I've been experimenting with the @apply feature in Polymer and, when composing elements, I quickly found myself wanting to have the composing element set some custom property values yet still allow for parent elements to override them. As far as I can tell, that's not supported (at least not explicitly) in this proposal (Polymer fails when trying this).

Conceptually related, it's not clear to me whether custom property bags operate as a merge (like they do on elements) or as an override.

For example, consider the following HTML structure and CSS rules:

<div class="parent">
  <div class="child">
    <div class="grandchild">
      text...
    </div>
  </div>
</div>
.parent {
  --styles: {
    font-family: monospace;
  }
}
.child {
  --styles: {
    color: red;
  }
}
.grandchild {
  @apply --styles;
}

Is the text red and monospaced, or is it just red because --styles was overriden in .child?

What I suspect is that the declarion is overridden, and if so, that could be an argument in favor of custom pseudo-elements as they could more gracefully (and predictably) handle merging of properties via the cascade.

@tabatkins
Copy link
Owner

Within a custom property's value, only var() values are expanded. Everything is kept literal, waiting for processing by something else (substitution into another property via var(), processing via JS, etc).

So using @apply in a custom property bag does nothing at declaration time. Upon use (via an @apply in another rule), the value of the custom property is subbed into the style rule and processed. At that point, the inner @apply is processed, triggering another substitution. Both the outer and inner @applys use the value of the given custom property on the invoking element.

If you want to build a custom property bag out of multiple other custom property bags at declaration time, use var():

.parent {
  --foo: { 
    color: red; 
    var(--bar); 
  };
  --bar: { 
    background-color: white; 
  };
}
.child {
  @apply --foo;
  /* gets "color: red; background-color: white;" */
}

Merging in a child (like your last example) can be accomplished, but it's a little tricky until we get the ability to refer to the parent's variable value. You'd just write something like:

.parent {
  --styles: { font-family: monospace; };
}
.child {
  --styles: { var(parent --styles); color: red; };
}
.grandchild {
  @apply --styles;
  /* gets "font-family: monospace; color: red; } */
}

This is the same problem as building up an "accumulating" variable for normal use - you need something like --indent: calc(20px + var(parent --indent)); to make it clean.

To do it today, you instead need to change the variable name as you go down the tree, such as appending a generation counter to the var name, so you're not self-referencing. This is tricky and error-prone, obviously. I'm waiting until variables finish getting implemented and used before doing Variables Level 2 with this ability.

@tabatkins
Copy link
Owner

THAT SAID, I might change the @apply behavior so that it gets substituted in at declaration time, like var() does. The behavioral difference isn't intentional; there's just as much call for something like --shadow: var(--shadow-color-at-use-time) 2px 2px;, so Variables level 2 will have some ability to declare that a var() is substituted "late", at use-time, rather than at declaration time. Having @apply work the same way - substituted at declaration time by default, and at use-time by request - is probably best for authors. And it means you don't have to remember the weirdness of using a var() in a declaration context, like my examples above do - they'd instead both be written with @apply, which I think feels much more natural, as it keeps the "var() is for values, @apply is for declarations" concept consistent.

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

2 participants