[css-variables?] Higher level custom properties that control multiple declarations · Issue #5624 · w3c/csswg-drafts (original) (raw)
Currently, custom properties can be used to hold small pieces of data to be used as parts of larger values. Despite being called custom properties, they are mainly used as variables. High-level custom properties that control a number of other CSS properties cannot be implemented.
Besides limiting regular CSS authors, this makes it impossible for custom element authors to follow the TAG guideline to avoid presentational attributes and to use a custom property instead, except for very simple bits of data like fonts, colors, and lengths. For anything more complex, web component authors use attributes instead.
Examples from a variety of custom element libraries:
- placement attribute
size
attribute in Shoelace and in Spectrum- pill attribute
- orientation attribute
- left attribute
- placement, offset, tip attributes
- appearance attribute
I can collect more if needed, examples abound in nearly all component libraries. Currently, these are impossible to implement as CSS custom properties, for a number of reasons:
- Each attribute controls multiple declarations, in different ways than just simple value substitution
- The actual value of the attribute may not be used used anywhere directly.
- Even in cases where the attribute controls one value only, it is more high level than the corresponding declarations (e.g.
pill=on
vsborder-radius: 999px
).
Essentially, component authors need more high-level custom properties that encapsulate the corresponding declarations better instead of just containing fragments of values.
Some proposals to address this problem focus on a JS-based way to monitor property changes [WICG/webcomponents#856], but that appears to be hard to implement. So, I'm wondering if we can address this from CSS instead, especially since it would also address a number of other use cases too that are unrelated to components, a big one being mixins, without the problems that we had with @apply
.
There are discussions in the group about inline conditionals [#4731, #5009]. If we were to have such conditionals, these would be possible to implement, but very painful (each declaration value would need to be one or more if()
). I was wondering if we could simplify this.
A pseudo-class such as :if-var(<dashed-ident> <comparison-operator> <value>)
would solve this ideally, but would likely not be implementable due to cycles. OTOH we already do cycle detection for variables, so perhaps it is? If so, I can flesh out a proposal.
Otherwise, perhaps a nested @rule
?
my-input { @if-var(--pill = on) { border-radius: 999px; } }
With nesting, that would even allow multiple rules, so it would cater for use cases such as e.g. the tabs placement without too much repetition.
One way to implement this would be as sugar for multiple if()
s, but that would have the undesirable side effect of all containing declarations being set to initial
when the conditional doesn't match and there's no @else
, which is suboptimal.
Whatever solution we come up with, some things to consider:
- The applied declarations may span multiple rules
- The actual value of the property may only be used indirectly, in conditionals (e.g.
--size: [small | medium | large]
) or it may be used directly in some values, and also in conditionals.
Current status (updated April 5th, 2021)
This is a long discussion, this is the current status:
- The initial idea of desugaring to inline
if()
has several drawbacks and would end up being very confusing for authors. - Another idea is to introduce a new type of custom property (custom constant?) that is limited to keywords and numbers, and can be used in a
:const()
selector. This requires two cascade passes. Constants cannot be set based on:const()
selectors. - Another idea, proposed by an actual implementer is to use regular custom properties, and have a pseudo that is invalid on the subject compound. This so far seems to address the most use cases.