var declared in media query should pull in properties that use/reference that var
MadLittleMods opened this issue · 10 comments
Custom properties are ordinary properties, so they can be declared on any element, are resolved with the normal inheritance and cascade rules, can be made conditional with @media and other conditional rules, can be used in HTML’s style attribute, can be read or set using the CSSOM, etc.
from W3C Working Draft: CSS Custom Properties for Cascading Variables Module Level 1
:root {
--width: 100px;
}
.box {
width: var(--width);
}
@media (max-width: 1000px) {
:root {
--width: 200px;
}
}
I expect or should result in:
.box {
width: 100px;
}
@media (max-width: 1000px) {
.box {
width: 200px;
}
}
Is this a correct understanding?
Playing around in the cssnext
playground, I am getting different results.
I understand this could get a little tricky in a situation like described here.
https://github.com/postcss/postcss-custom-properties/blob/master/README.md
The transformation is not complete. It currently just aims to provide a future-proof way of using a limited subset (to top-level :root
selector) of the features provided by native CSS custom properties.
Read #1 & #9 to know why this limitation exists.
Put it behind a flag
I think the limitation is valid for being implemented as a default. I think if this feature would be best suited behind a flag. It would cover/solve the actual use cases that I have seen throughout the issues you linked, issues on the Sass repo, etc.
Unknown DOM Structure
For starters, the flag would only enable :root
level variable declarations to avoid the "magic" needed to cover the whole spectrum of DOM structure.
Cascade
specificity/cascade issues - moving/generating rules is always going to hit the problem where earlier styles are unintentionally overridden.
...
These kinds of divergences are unavoidable when you inject rules or move them around. All the media query packer plugins have the same caveat.from #9
Don't try to move around pack/group media queries. Some simple option presets could solve the functionality.
- Move everything to where the media query re-declaration is
- Create a specific media query below each rule
Example
Input:
:root {
--foo-width: 15vw;
--bar-width: 150px;
}
.box-foo {
width: var(--foo-width);
}
.box-bar {
width: var(--bar-width);
}
.box-qux {
width: calc(400px - var(--bar-width));
}
@media (max-width: 800px) {
:root {
--foo-width: 25vw;
--bar-width: 300px;
}
}
Output with preset #1
(move everything to where the media query re-declaration is):
.box-foo {
width: 15vw;
}
.box-bar {
width: 150px;
}
.box-qux {
width: calc(400px - 150px);
}
@media (max-width: 800px) {
.box-foo {
width: 25vw;
}
.box-bar {
width: 300px;
}
.box-qux {
width: calc(400px - 300px);
}
}
Output with preset #2
(create a specific media query below each rule):
.box-foo {
width: 15vw;
}
@media (max-width: 800px) {
.box-foo {
width: 25vw;
}
}
.box-bar {
width: 150px;
}
@media (max-width: 800px) {
.box-bar {
width: 300px;
}
}
.box-qux {
width: calc(400px - 150px);
}
@media (max-width: 800px) {
.box-qux {
width: calc(400px - 300px);
}
}
There is no better solution/alternative
This use case could be accomplished with a robust enough mixin system.
But right now, postcss-mixins doesn't support @content
style mixins or passing arguments from a mixin to a content block. See: postcss/postcss-mixins#8 for progress on this feature.
Combined with postcss-nested, we could accomplish the same as the snippet below in with PostCSS.
Note: non-valid Sass. until this issue is resolved
$media-variable-map: (
default: (
foo-width: 15vw,
bar-width: 150px
),
800px: (
foo-width: 25vw,
bar-wdith: 300px
)
);
@mixin access-variables {
@each $width, $variable-map in $media-variable-map {
@if($width != default) {
@media (max-width: #{$width}) {
@content($variable-map)
}
}
@else {
@content($variable-map)
}
}
}
.box-foo {
@include access-variables using($variable-map) {
width: map-get($variable-map, foo-width);
}
}
.box-bar {
@include access-variables using($variable-map) {
width: map-get($variable-map, bar-width);
}
}
.box-qux {
@include access-variables using($variable-map) {
width: calc(400px - map-get($variable-map, bar-width));
}
}
I think the example in #9 is pretty clear about the huge issue with the first solution (inject after each :root definition).
<div class="One Two">Text</div>
Input:
:root { --fontSize: 2em; }
.One { font-size: var(--fontSize); }
.Two { font-size: 6em; }
@media (min-width: 64em) {
:root { --font-size: 3em; }
}
Output (notice One
now overrides Two
, which it would not with a native solution):
.One { font-size: 2em; }
.Two { font-size: 6em; }
@media (min-width: 64em) {
.One { font-size: 3em; }
}
But I've to admit, with my brains that is just waking up, the second solution seems to be not that bad. I've added a comment here about it postcss/postcss-custom-properties#9 (comment)
@MoOx I think your example/use-case is outside of the scope of this feature in terms of a reason to shoot down this feature as a whole(not saying you are, but others have in previous issues) but a completely valid case for why it may be best to put it behind a flag. The cascade solution #2
proposed earlier does solve this problem as you mentioned though.
While #2
clearly seems better, I recommend implementing both just in case someone is inclined to use it and doesn't have a situation like you describe. The default should be #2
to avoid failure as much as possible. Your kind of example/situation pops up frequently with BEM but not everyone uses that methodology and may want their CSS output to be like #1
for some unforeseen reason.
I might start to work on solution #2
soon.
Any idea on the name of the option to enable this ?
handleRootDeclarationsInMediaQueries
(lol)?
Note: I suppose this should probably work with @support
directives as well.
Please make this flag available to the Node.js API.
Flag name suggestions
Since it is a bool, maybe prepend a should
in front of some of these.
handleRootVariables
handleRootVariableChanges
handleRootVariableDeclarations
handleAnyRootVariable
enhanceRootVariables
enhanceRootVariableSupport
enhanceRootVariableTransformation
completeRootVariableTransformation
rootVariableCompleteSubset
handleAllTopLevelVariables
Good point for @support
and some others @ directives (@page
?).
What about enableUnsafeTransformation
(enableDangerousTransformation
?). Or maybe just unsafe
with a clear doc on what it will enable.
Just released the postcss-css-variables
plugin to cover the use case this issue brings up.
- Removes
:root
level variable declaration limitation - Usage in At-rule
@media
,@support
- Nesting support with descendant selector or physical position along with postcss-nested
As said on gitter we should keep the effort on postcss-custom-properties plugin by incorporating safe transformation.
I just take a deeper look at your plugin and opened too many issues that you won't be able to resolve to decide (I know because i tried to in the past) to not use it sorry.
I will open another issue for :root in media queries.