Automattic / juice

Juice inlines CSS stylesheets into your HTML source.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[FEAT] Syntax for ignoring CSS code blocks

cossssmin opened this issue · comments

@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 */

Ah, found the initial discussion: #231, it was a PR actually, I only looked at issues 😂

🤔 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 */

Hmm, looking at this:

juice/lib/utils.js

Lines 60 to 77 in f1ecb95

exports.parseCSS = function(css) {
var parsed = mensch.parse(css, {position: true, comments: true});
var rules = typeof parsed.stylesheet != 'undefined' && parsed.stylesheet.rules ? parsed.stylesheet.rules : [];
var ret = [];
for (var i = 0, l = rules.length; i < l; i++) {
if (rules[i].type == 'rule') {
var rule = rules[i];
var selectors = rule.selectors;
for (var ii = 0, ll = selectors.length; ii < ll; ii++) {
ret.push([selectors[ii], rule.declarations]);
}
}
}
return ret;
};

... 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:

juice/lib/utils.js

Lines 66 to 73 in f1ecb95

if (rules[i].type == 'rule') {
var rule = rules[i];
var selectors = rule.selectors;
for (var ii = 0, ll = selectors.length; ii < ll; ii++) {
ret.push([selectors[ii], rule.declarations]);
}
}

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?

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