cssnano
cssnano copied to clipboard
mergeRules when combined with mergeLonghand can create the wrong rules
Apologies for not being able to reduce this to the absolute minimum reproduction, but given this CSS:
.has-errors .input {
background-color: #fcebea;
border-color: #cc1f1a;
}
.has-errors .checkbox-label {
background-color: #fcebea;
border-color: #cc1f1a;
}
.has-errors .checkbox-inline {
background-color: #fcebea;
border-color: #cc1f1a;
}
.error-banner .field-errors.filled {
width: 100%;
padding: 1.5rem 1.5rem 1rem;
background-color: #fcebea;
border-bottom-width: 1px;
border-style: solid;
border-color: #cc1f1a;
}
.error-banner .field-errors.filled .field-error {
width: 100%;
font-size: .875rem;
color: #22292f;
line-height: 1.5;
margin-bottom: .5rem;
}
When minified with mergeRules and mergeLonghand both enabled, the output is:
.error-banner .field-errors.filled,
.has-errors .checkbox-inline,
.has-errors .checkbox-label,
.has-errors .input {
background-color: #fcebea;
border-color: #cc1f1a
}
.error-banner .field-errors.filled {
width: 100%;
padding: 1.5rem 1.5rem 1rem;
border-bottom: 1px;
border-style: solid
}
.error-banner .field-errors.filled .field-error {
width: 100%;
font-size: .875rem;
color: #22292f;
line-height: 1.5;
margin-bottom: .5rem
}
The important changes here are that the border-color declarations were grouped and the border-color declaration from the .error-banner .field-errors.filled selector was removed, and that what was originally border-bottom-width: 1px is simplified to border-bottom: 1px.
But because the border-color declaration was hoisted to the group at the top, the border color no longer takes effect for the .error-banner .field-errors.filled selector because border-bottom: 1px sets a default color of black which overrides it.
This sort of hand-crafted diff tries to make the important changes clear:
+ .error-banner .field-errors.filled,
.has-errors .checkbox-inline,
.has-errors .checkbox-label,
.has-errors .input {
background-color: #fcebea;
border-color: #cc1f1a
}
.error-banner .field-errors.filled {
width: 100%;
padding: 1.5rem 1.5rem 1rem;
- border-bottom-width: 1px;
+ border-bottom: 1px;
border-style: solid
- border-color: #cc1f1a
}
.error-banner .field-errors.filled .field-error {
width: 100%;
font-size: .875rem;
color: #22292f;
line-height: 1.5;
margin-bottom: .5rem
}
In this case the expected behavior is that because border-color was hoisted out, border-bottom-width cannot be simplified to border-bottom, or alternatively, the border-color declaration should not be removed from that selector at all.
Tricky stuff to get right for sure, I don't envy anyone trying to build a tool like this but am extremely grateful for your hard work 👍
Oh, can we reduce example? I think problem in postcss-merge-longhand, he runs before postcss-merge-rules and create invalid border values so we have invalid merged rules
To be honestly, i don't have enough time and funds :disappointed: to works a lot of time on this so feel free to send a PR
Cool thanks I will see if I can get it working and PR something! 👍
For others, here's the minimum reproduction:
.foo {
border-color: #cc1f1a;
}
.bar {
border-bottom-width: 1px;
border-style: solid;
border-color: #cc1f1a;
}
Compiles to:
.bar,
.foo {
border-color: #cc1f1a
}
.bar {
border-bottom: 1px;
border-style: solid
}
Expected:
.foo {
border-color: #cc1f1a;
}
.bar {
border-bottom: 1px;
border-style: solid;
border-color: #cc1f1a;
}
This is related but possibly a different issue, but this CSS:
.foo {
border-color: #cc1f1a;
}
.bar {
border-bottom: 1px;
border-color: #cc1f1a;
}
...compiles to:
.bar,
.foo {
border-color: #cc1f1a
}
.bar {
border-bottom: 1px
}
...which is also wrong, since it's important that the border-color declaration comes after border-bottom for the color to take affect.
Expected result is that it just doesn't change:
.foo {
border-color: #cc1f1a;
}
.bar {
border-bottom: 1px;
border-color: #cc1f1a;
}
@adamwathan Can you provide what you expected too for other developers?
Added expected behavior to the comment 👍
@evilebottnawi I am seeing a similar issue (which may be related) when using the default preset:
The following CSS:
background-position: center right calc(0.375em + 0.1875rem);
is being converted to:
background-position: 100% right calc(0.375em + 0.1875rem)
which does not produce the same result visually.
But, by changing the order of the original css to the following:
background-position: right calc(0.375em + 0.1875rem) center;
cssnano leaves it alone and doesn't mangle it.
I've opened a new issue https://github.com/cssnano/cssnano/issues/712
@tmorehouse not related to this issue
Input
.foo {
border-color: #cc1f1a;
}
.bar {
border-bottom-width: 1px;
border-style: solid;
border-color: #cc1f1a;
}
With postcss-merge-longhand only
.foo {
border-color: #cc1f1a;
}
.bar {
border-bottom: 1px;
border-color: #cc1f1a;
border-style: solid;
}
With postcss-merge-rules only
.foo {
border-color: #cc1f1a;
}
.bar {
border-bottom-width: 1px;
border-style: solid;
border-color: #cc1f1a;
}
With postcss-merge-longhand before postcss-merge-rules
.foo,.bar {
border-color: #cc1f1a;
}
.bar {
border-bottom: 1px;
border-style: solid;
}
With postcss-merge-rules before postcss-merge-longhand
.foo {
border-color: #cc1f1a;
}
.bar {
border-bottom: 1px;
border-color: #cc1f1a;
border-style: solid;
}
Here, both plugins are working fine as independently but not together. The fix as expected is to have the merging of the rule before merging the longhand!
cc @evilebottnawi !
After running a quick test, I think #1450 might solve this