Automattic / juice

Juice inlines CSS stylesheets into your HTML source.
MIT License
3.1k stars 220 forks source link

[FEAT] Syntax for ignoring CSS code blocks #387

Open cossssmin opened 3 years ago

cossssmin commented 3 years ago

@jrit I remember discussing this way back, but can't find the issue...

What do you think of introducing CSS comments as delimiters for CSS code blocks that Juice should simply skip over?

This could be used in setups where using a <style data-embed></style> is not an option.

For example, using this with removeStyleTags: true:

body {
  margin: 0;
  padding: 0;
}

a[x-apple-data-detectors] {
  color: inherit!important;
  text-decoration: none!important;
  font-size: inherit!important;
  font-family: inherit!important;
  font-weight: inherit!important;
  line-height: inherit!important;
}

... results in a[x-apple-data-detectors] being removed from the <style> tag.

I can't use data-embed in this setup, and I need to keep removeStyleTags: true, so that I don't leave unnecessary code in my email, thus increasing its weight and the risk of Gmail clipping.

What do you think of a PurgeCSS-like CSS comment delimiter, that would mark the start and end of the lines that Juice should not inline and not remove when removeStyleTags: true?

Something like:

body {
  margin: 0;
  padding: 0;
}

/* juice start ignore */
a[x-apple-data-detectors] {
  color: inherit!important;
  text-decoration: none!important;
  font-size: inherit!important;
  font-family: inherit!important;
  font-weight: inherit!important;
  line-height: inherit!important;
}
/* juice end ignore */
cossssmin commented 3 years ago

Ah, found the initial discussion: https://github.com/Automattic/juice/pull/231, it was a PR actually, I only looked at issues 😂

cossssmin commented 3 years ago

🤔 I think we could do it just like PurgeCSS, which has the added benefit of familiarity for web developers.

Ignore the entire file - add comment at the very top:

/* juice ignore */

Ignore current rule:

h1 {
    /* juice ignore current */
    color: blue;
}

Ignore blocks of code:

h1 {
  color: black;
}

/* juice start ignore */
h2 {
  color: pink;
}

h3 {
  color: lightcoral;
}
/* juice end ignore */
cossssmin commented 3 years ago

Hmm, looking at this:

https://github.com/Automattic/juice/blob/f1ecb950b761377e3b77d8e978873699b3c310de/lib/utils.js#L60-L77

... parseCSS returns an array like this:

[
  'strong', // selector
  [
    // array of objects representing css rules for this selector
    {
      type: 'property',
      name: 'color',
      value: 'blue',
      position: [Object]
    }
  ]
]

However, this code restricts parsing only to actual CSS rules, so comments are never returned by parseCSS:

https://github.com/Automattic/juice/blob/f1ecb950b761377e3b77d8e978873699b3c310de/lib/utils.js#L66-L73

I'm not sure what the syntax for a 'comment' case should be, would this work?

[
  'comment',
  [
    {
      type: 'comment',
      text: ' juice ignore current ',
      position: [Object]
    }
  ]
]

That might fail if the user needs to inline on a <comment> tag.

Maybe setting it to null or false, then checking the type/value in inline.js?


Basically, in order to try and support inlining ignoring through CSS comments, I think we need them available in the object returned by parseCSS, so that we can abort early or skip inlining rules in handleRule from inline.js.

What do you think, @jrit?

jrit commented 3 years ago

mensch returns the comments, so it'd be looking for if (rules[i].type == 'comment') { I think. And then I think within juice's parseCSS function there you'd want to interpret the comments, based on something like you suggested, and then juice's parseCSS would only return the rules with comments having been processed. Does that make sense? Happy to take a PR for something like that