stylis icon indicating copy to clipboard operation
stylis copied to clipboard

minification does not happen for gradients

Open layershifter opened this issue 3 years ago • 1 comments

Versions

[email protected]

Problem

Stylis does minification and it works great:

.foo { color: red; }
.bar {
  color: blue;
  background: orange;
}
/* ⬇️⬇️⬇️ */
.foo{color:red;}.bar{color:blue;background:orange;}

The problem happens with radial-gradient& repeating-linear-gradient:

.foo {
  background: repeating-linear-gradient(
    -45deg,
    red,
    red 5px,
    blue 5px,
    blue 10px
  );
}
.bar {
  color: blue;
  background: orange;
}
.baz {
  background-image:radial-gradient(
    ellipse 50rem 50rem,
    #8e91e6 0%, 
    #403f5a 25%,
    transparent 46%
  )
}
/* ⬇️⬇️⬇️ */
 .foo{background:repeating-linear-gradient(
    -45deg,
    red,
    red 5px,
    blue 5px,
    blue 10px
  );}.bar{color:blue;background:orange;}.baz{background-image:radial-gradient(
    ellipse 50rem 50rem,
    #8e91e6 0%, 
    #403f5a 25%,
    transparent 46%
  );}

👆 notice that .foo & .baz are not minified.

Reproduction

https://codesandbox.io/s/awesome-rgb-wf0j3e

layershifter avatar Oct 31 '22 13:10 layershifter

This does not only happen on gradients. Any style with Parentheses like var() calc() suffer from the same issue.

You can see this in test case, the parser ignores anything between parentheses

https://github.com/thysultan/stylis/blob/b8c0878307ce600c7ec5082f5f940febf663a722/test/Parser.js#L19-L27

You can workaround this by providing your own middleware.

import { compile, middleware, serialize, stringify } from 'stylis'

const parenthesesStartRegex = /\(\s+/g;
const parenthesesEndRegex = /\s+\)/g;
const commaSpaces = /,\s+/g;
const parenthesesRegex=/^[a-z-]+\(\s+/ig;

const minifyParentheses = element => {
  if (
    element.type === 'decl' &&
    typeof element.props === 'string' &&
    typeof element.children === 'string' &&
    parenthesesRegex.test(element.children)
  ) {
    element.return = `${element.props}:${element.children
      .replaceAll(parenthesesStartRegex, '(')
      .replaceAll(parenthesesEndRegex, ')')
      .replaceAll(commaSpaces, ',')};`;
  }
};

const css = `
.foo {
  background: repeating-linear-gradient(
    -45deg,
    red,
    red 5px,
    blue 5px,
    blue 10px
  );
  color: green;
}
.bar {
  color: blue;
  background: orange;
}
.baz {
  background-image:radial-gradient(
    ellipse 50rem 50rem,
    #8e91e6 0%, 
    #403f5a 25%,
    transparent 46%
  )
}
`;

const processedCss = serialize(compile(css), middleware([minifyParentheses, stringify]));
console.log(processedCss)

output:

.foo{background:repeating-linear-gradient(-45deg,red,red 5px,blue 5px,blue 10px);color:green;}.bar{color:blue;background:orange;}.baz{background-image:radial-gradient(ellipse 50rem 50rem,#8e91e6 0%,#403f5a 25%,transparent 46%);}

Caveats

Note that this example doesn't consider nested ( ) or content in "". It may cause some issues if you just use this workaround.

Fonger avatar Mar 16 '24 18:03 Fonger