@hellocosmin hellocosmin released this Nov 9, 2018

Assets 2
  • lock NPM dependencies in package.json to tested releases.

@hellocosmin hellocosmin released this Oct 29, 2018 · 3 commits to master since this release

Assets 2
  • fixed Screenshots on OSX setups - Puppeteer was not loading the correct path for local files

@hellocosmin hellocosmin released this Oct 27, 2018 · 6 commits to master since this release

Assets 2

This release updates Tailwind CSS, fixes a few bugs and adds support for using hover- utilities without a @media query.

Pseudo selectors now preserved

Until now, you couldn't have used a utility like hover-text-green, because Juice would have removed the CSS selector as part of the removeStyleTags feature. The common solution was to 'hide' the selector inside a @media query, so Juice would leave it alone - i.e. all-hover-text-green.

This release updates Juice to version 5.0.0, which fixes the inconvenience.

Fixed googleFonts key format

Due to a recently introduced type check for the googleFonts Front Matter variable, using a simple string was not working anymore. Updated the example templates and docs to use an array.

Font family reset removed

<td> and <th> were being reset to use the font-sans utility class. This meant that, if you wanted to use a different font family on an element, you needed to override this reset either globally, or by adding a font-... utility on every table cell.

This release removes the global font family reset, and adds it on the wrapper table instead.

lang="" for webmail

Webmail clients, such as Gmail, completely remove your <html> and <body> tags. Since the lang="" attribute is being set on the <html> tag, this resulted in a situation where a screenreader could not use the language we specified, for correct pronunciation.

This release adds the lang="" attribute on elements in the included templates, too. When creating a new template, it is highly advisable that you add this, just like in the examples.

Tailwind CSS 0.6.6

Updated to Tailwind CSS 0.6.6, which fixes an issue where units were stripped from zero value properties, and promotes shadowLookup from experiment to official feature.

There is also a borderCollapse: [], module in the Tailwind config, which you can use if you ever need variants for border-collapse on tables.

@hellocosmin hellocosmin released this Oct 25, 2018 · 15 commits to master since this release

Assets 2

#outlook a removed

Testing in Outlook 2013/2016 shows that #outlook a {padding: 0;} isn't needed for the 'view in browser' bar to show up, so it has been removed.

Custom MSO styles per template

outlook-conditional.blade.php now checks if there's a Blade section named mso-css in your template. If it finds it (and it's not empty), it will use its contents in the <style> tag from the <!--[if mso]> conditional in a layout's <head>.

For example, say we wanted a different font stack for this email instead of the default one (that is using Segoe UI), as well as some extra MSO-specific table styling:

// custom-mso-css.blade.md

---
title: Custom MSO styling example
---

@section('mso-css')
  table {border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;}
  td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: Helvetica, Arial, sans-serif; mso-line-height-rule: exactly;}
@endsection

<table ...

Note: whatever you have in this section will replace the default CSS for Outlook that Maizzle comes with.

@hellocosmin hellocosmin released this Oct 23, 2018 · 18 commits to master since this release

Assets 2

Removed the custom Parsedown parser container binding, so that Parsedown Extra can actually work.

Jigsaw uses Parsedown Extra since v1.2.0, so this was unnecessary.

@hellocosmin hellocosmin released this Oct 20, 2018 · 20 commits to master since this release

Assets 2

Improved CSS class cleanup

Maizzle now exposes email-remove-unused-css's backend option in your config, which you can use to define heads/tails pairs of characters that wrap your CSS class names, so the cleanup library doesn't remove those classes.

A {{ }} pair is included - useful if you're using Blade to further process an email after Maizzle compiles it:

'cleanup' => [
    'removeUnusedCss' => [
        // ...
        'backend' => [
            [
            'heads' => "{{",
            'tails' => "}}",
            ],
        ],
        // ... 
    ],
],

Imagine foo = 'odd' somewhere in your application. Previously, if you wanted to output Blade from Maizzle:

<tr class="@{{ foo }}">

... you would have ended up with this in the compiled HTML:

<!-- foo is removed 😩 -->
<tr class="{{ }}">

Having {{ and }} defined as heads/tails in the backend option, the compiled HTML will now look like this:

<!-- we can now use `foo` 👌 -->
<tr class="{{ foo }}">

Don't load extraBorderUtilities plugin by default

The require for this Tailwind CSS plugin is now commented out by default, as it's not really needed in most cases. Simply uncomment this line in tailwind.js, if you need it:

// require('./tailwind/plugins/extraBorderUtilities'),

@hellocosmin hellocosmin released this Oct 7, 2018 · 25 commits to master since this release

Assets 2

This release adds some cool new features, and fixes some minor issues.

Specifically, it:

Switch to PostCSS

Maizzle now uses PostCSS instead of Sass.

It's faster, avoids preprocessor gotchas (like a Python dependency for node-sass in Windows, or not being able to use Tailwind's @apply with !important), and makes more sense considering the tooling being used.

Plaintext Versions

Maizzle can now automatically generate plaintext versions of your emails, with the help of string-strip-html.

A new 'plaintext' => false, option has been added to the configs. When you set it to true, Maizzle will generate a plaintext version for every template. The .txt file will be placed in the same directory as the HTML it's based on, and it will also have the same name.

Plaintext is enabled by default for production builds, in config.production.php.

Shortand CSS

Maizzle now uses postcss-merge-longhand to rewrite your CSS padding, margin, and border in shorthand-form, where possible. Because Tailwind classes mostly map one-to-one with CSS properties, this won't have any effect on them. Instead, it's very useful when you extract components with Tailwind's @apply.

For example, considering this template:

---
title: Confirm your email
preheader: Please verify your email address with us
bodyClasses: bg-grey-light
---

<div class="col">test</div>

... let's extract a .col class in an imaginary source/_styles/components.css:

.col {
  @apply py-8 px-4;
}

Previously, that would have given us this:

<div style="padding-top: 8px; padding-bottom: 8px; padding-left: 4px; padding-right: 4px;">test</div>

Now, thanks to postcss-merge-longhand, we get this:

<div style="padding: 8px 4px;">test</div>

As mentioned, this works for padding, margin, and border. Using shorthand CSS for these is well supported in email clients and will make your HTML lighter, but the shorthand border is particularly useful because it's the only way Outlook will render it properly.

Note: the library won't assume any missing values. For padding and margin, the class needs to specify properties for all four sides. For borders, see the example below.

Shorthand borders

To get the PostCSS plugin to rewrite your CSS borders in shorthand-form, you need to specify all these:

  • border-width
  • border-style
  • border-color

When extracting a component class in Tailwind, that means you can do something like this:

.my-border {
  @apply border border-solid border-blue;
}

... which, following the example above, will result in this shorthand form:

<div style="border: 1px solid #3490dc;">test</div>

DOCTYPE option

You can now set a doctype for your emails, either globally or per-template.

There is a new doctype option in config.php:

'doctype' => 'html',

This is used in the default layout(s) that Maizzle comes with:

<!DOCTYPE {!! $page->doctype ?? 'html' !!}>

Note: the {!! !!} Blade statement is used instead of {{ }}. This is to prevent the value from being escaped - helps if you use an older doctype, like in the front matter example below.

The global variable set in config.php can be overridden at a template level, just like any other option:

---
doctype: html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
title: Confirm your email
preheader: Please verify your email address with us
---

Stacks

Maizzle's default layouts now include a @stack('head') directive (see Blade docs).

This allows you to use @push('head') and @prepend('head') in your Blade email files, to add anything you'd like right before the closing </head> tag.

You could use this to add custom <style> blocks on a per-template basis. Email client-specific CSS resets make for a good example:

source/emails/confirm-email.blade.md

---
// front matter needs to be first!
---

@push('head')
<style data-embed>
  a[x-apple-data-detectors] {color: inherit; text-decoration: none;}
</style>
@endpush

<table ...

You can push multiple stacks, too:

---
...
---

@push('head')
<style data-embed>
  a[x-apple-data-detectors] {color: inherit; text-decoration: none;}
</style>
@endpush

@push('head')
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="date=no">
<meta name="format-detection" content="address=no">
<meta name="format-detection" content="email=no">
@endpush

Of course, you can use it for anything you'd like to have right before </head>, for this template only: additional meta tags, Outlook conditionals, custom ESP code - you name it!

Notes:

  1. When @pushing a <style>, you need to add a data-embed attribute on it, so that the inliner leaves it alone.

  2. You cannot use Tailwind CSS @-rules here. Tailwind is processed before this file is, so using something like @apply will have no effect.

More Juice options

styleToAttribute

Maizzle now exposes juice.styleToAttribute to your configs, so you can define which CSS properties should be also added as HTML attributes to table elements that Juice knows about.

This is opt-out by design, and will only have effect if inlining is enabled.

You can customise the styleToAttribute key under inlineCSS, mapping CSS properties to HTML attributes.

For example this:

// config.production.php

'transformers' => [
    'inlineCSS' => [
        'enabled' => true,
        'styleToAttribute' => [
            'background-color' => 'bgcolor',
        ],
    ],
    ...
],

... will transform this:

<!-- using Tailwind class here... -->
<table class="bg-white">
  ...
</table>

... to this:

<table style="background-color: #ffffff;" bgcolor="#ffffff">
  ...
</table>

Opt-out by design

By default, Maizzle configs include the styleToAttribute key, and populate it with Juice's defaults.

This is on purpose, for better email client compatibility.

However, if you specify a single mapping, like we did in the example above, Juice will only convert that to an attribute, keeping any other property as inline CSS-only.

codeBlocks

Maizzle now exposes juice.codeBlocks to your config, so that you can define additional fenced code blocks that you need Juice to ignore when inlining.

codeBlocks must be an array of items matching juice.codeBlocks dictionary format.

For example:

// config.production.php

'inlineCSS' => [
    'enabled' => true,
    'codeBlocks' => [
        'ASP' => ['start' => '<%@', 'end' => '%>'],
    ],
],

The above will add the ASP key to Juice's juice.codeBlocks dictionary, so that Juice doesn't treat something like <%@ page language="C#" %> as an HTML tag that needs to have CSS inlined.

applySizeAttribute

There is a new applySizeAttribute key under the inlineCSS options:

'inlineCSS' => [
    'enabled' => true,
    // ...
    'applySizeAttribute' => [
        'width' => ['TABLE', 'TD', 'TH', 'IMG', 'VIDEO'],
        'height' => ['TABLE', 'TD', 'TH', 'IMG', 'VIDEO'],
    ],
],

Under the width and height keys, you can specify an array of elements that should receive width="" and height="" attributes. These elements will be passed to the Juice inliner, which will duplicate any inline width and height CSS rules it finds as HTML attributes, but only for those elements.

excludedProperties

The new excludedProperties option allows you to define an array of CSS properties that should be excluded from the CSS inlining process by Juice:

'inlineCSS' => [
    'enabled' => true,
    // ...
    'excludedProperties' => [],
],

Maizzle sets this as an empty array in the production environment configs.

Property names are considered unique, so you need to specify each one you'd like to exclude. For example:

'excludedProperties' => ['padding', 'padding-left'],

Note: corresponding classes that they're derived from (i.e. pl-2) will still be removed from your HTML if removeUnusedCss is enabled. To prevent this, you could add 'removeStyleTags' => false, in the inlineCSS options array.

removeStyleTags

removeStyleTags has been removed from config.*.php. It was set to true in all three config files, and the option it's for also falls back to true in the post-processing script, so it was redundant.

However, you can still add it to your config, to prevent Juice from removing possibly-inlined CSS from your <style></style> tags:

//config.staging.php

'transformers' => [
    // [...]
    'inlineCSS' => [
        'enabled' => true,
        'removeStyleTags' => false,
    ],
],

Refactored and renamed preferAttributeWidth

The preferAttributeWidth option has been renamed and refactored to apply to both widths and heights:

'cleanup' => [
    // [...]
    'keepOnlyAttributeSizes' => [
        'width' => ['TABLE', 'TD', 'TH', 'IMG', 'VIDEO'],
        'height' => ['TABLE', 'TD', 'TH', 'IMG', 'VIDEO'],
    ],
],

You can now specify for which elements should the inline CSS width and height be removed. As you can see, Maizzle defaults to the same elements as in the applySizeAttribute option.

Removing the entire keepOnlyAttributeSizes key will leave any inline CSS width/height untouched.

Providing an empty array to one of its keys will not remove the corresponding CSS property for that key on any element. So if you do this:

'cleanup' => [
    // [...]
    'keepOnlyAttributeSizes' => [
        'width' => [],
        'height' => ['TABLE', 'TD', 'TH', 'IMG', 'VIDEO'],
    ],
],

... then inline CSS width will be left untouched, while inline CSS height will be removed for all those elements in the array.

@env Blade directive

There is a new @env($environment) Blade directive (inspired by the one in the Laravel docs), which you can use to output content only when building for a specific environment.

For example, to output some text only for production emails, you can do this:

source/emails/example.blade.md

---
front matter always first!
---

<table class="w-full" cellpadding="0" cellspacing="0" role="presentation">
  <tr>
      <td align="center" class="px-8">
        @env('production')
            This text will be visible only when running `npm run production`
        @endenv
      </td>
  </tr>
</table>

Of course, you can do an if/else statement:

...

<table class="w-full" cellpadding="0" cellspacing="0" role="presentation">
  <tr>
      <td align="center" class="px-8">
        @env('development')
            Show this if we do `npm run dev` or `npm run watch`
        @elseenv('production')
            But when we do `npm run prod`, show this instead
        @endenv
      </td>
  </tr>
</table>

Note: $environment must match one of the NODE_ENV environment variables set in package.json.

Custom utilities

There is a new custom-utilities.css inside the source/_styles directory. Use this file to register any utility classes that Tailwind doesn't provide yet. This release includes extra table-display utilities, useful for example if you need to do reverse column stacking, plus a .mso-leading-exactly utility:

.mso-leading-exactly {
  mso-line-height-rule: exactly;
}

@responsive {
  .table-row-group {
    display: table-row-group;
  }
  .table-header-group {
    display: table-header-group;
  }
  .table-footer-group {
    display: table-footer-group;
  }
  .table-column-group {
    display: table-column-group;
  }
  .table-column {
    display: table-column;
  }
  .table-caption {
    display: table-caption;
  }
}

Tailwind plugins

There is a new tailwind/plugins directory, where you can add your Tailwind CSS plugins.

Border side colors and styles plugin

Maizzle now comes with a custom plugin that allows for defining individual border side colors and styles, thanks to @spiltcoffee.

Background gradients plugin

Also included is a plugin that generates CSS background gradients, inspired by @benface's plugin.

You can define the gradients in your tailwind.js config, just like with any other module:

let gradients = {
  'grey-dark': ['#b8c2cc', '#8795a1'],
  'red-dark': ['#e3342f', '#cc1f1a'],
  'orange-dark': ['#f6993f', '#de751f'],
  'yellow-dark': ['#ffed4a', '#f2d024'],
  'green-dark': ['#38c172', '#1f9d55'],
  'teal-dark': ['#4dc0b5', '#38a89d'],
  'blue-dark': ['#3490dc', '#2779bd'],
  'indigo-dark': ['#6574cd', '#5661b3'],
  'purple-dark': ['#9561e2', '#794acf'],
  'pink-dark': ['#f66d9b', '#eb5286'],
}

Those are the default ones Maizzle comes with, and they're based on Tailwind's default color palette.

You can add your own gradients, with as many color stops as you need:

let gradients = {
  // ...

  'hydrogen': ['#667db6', '#0082c8', '0082c8', '667db6'],
}

If you define a single color instead of an array, the resulting gradient will start from transparent and move towards the color you defined:

let gradients = {
  // ...

  'black': '#22292f',
}

... will generate classes like:

.bg-gradient-to-top-black {
  background-image: linear-gradient(to top, transparent, #22292f);
}

Of course, the generated classes cover all four directions. With the example above, you'd get:

.bg-gradient-to-top-black {
  background-image: linear-gradient(to top, transparent, #22292f);
}
.bg-gradient-to-right-black {
  background-image: linear-gradient(to right, transparent, #22292f);
}
.bg-gradient-to-bottom-black {
  background-image: linear-gradient(to bottom, transparent, #22292f);
}
.bg-gradient-to-left-black {
  background-image: linear-gradient(to left, transparent, #22292f);
}

Just like with any module, you can control which variants are generated. Maizzle only enables the responsive and hover variants by default, but you can use any of the variants, or set it to false to not generate any background gradients:

modules: {
  // ...
  gradients: ['responsive', 'hover'],
 // ...
},

Miscellaneous

  • updated source/_styles directory structure:
    • _styles
      • partials
        • custom-utilities.css
        • reset.css
      • extra.css
      • main.css
  • extra.css is now inserted when Jigsaw builds the files, instead of when post-processing happens. This speeds up the post-processing a bit by not relying on Cheerio
  • you can now use Tailwind's @apply inside extra.css
  • improved Google Fonts config check in layouts
  • updated config files:
    • stub transformers in config.php
    • default googleFonts to empty array
    • improved comments
  • removed merriweather font stack definition from tailwind.js
  • fixed body font reset + removed typography.css
  • fixed Gmail iOS full width reset + removed responsive.css
  • uglify option has been renamed to uglifyClassNames, for clarity

@hellocosmin hellocosmin released this Sep 23, 2018 · 76 commits to master since this release

Assets 2

This release exposes new CSS cleanup options in config.php, thanks to v4.2.0 of email-remove-unused-css.

You now have two new options in removeUnusedCss:

1. removeHTMLComments

'enabled' => true, will remove any HTML comments in your compiled email. Set to false to not remove any comments.

preserve can be set to an array of strings. If any comment's opening tag contains any of these strings, that comment will not be removed.

Warning: if using removeHTMLComments but want to preserve certain comments such as Outlook conditionals, you need to explicit about both the starting and ending comment in the preserve strings, because they are treated as two separate comments.

Basically, don't do this:

// will remove your closing <![endif]--> comment
'preserve' => ['if', 'mso', 'ie'],

Instead, do this:

// will preserve Outlook conditional comments as expected
'preserve' => ['if', 'endif', 'mso', 'ie'],

Maizzle defaults to the email-remove-unused-css library defaults (['if', 'endif', 'mso', 'ie']) both in the config.*.php, and as a fallback: if you enable removeHTMLComments but don't specify a list of strings (or specify an empty array for preserve), it will still properly handle Outlook conditionals.

2. uglify

This is a very cool option introduced in email-remove-unused-css v4.2.0: it renames all your classes, in both HTML and CSS, to be very short (starting from a single character). It's a small but very useful feature, especially for complex layouts where you need to shave off as many KB as possible to avoid Gmail clipping.

For example, this:

<style>
    .text-purple-lightest {color: #f3ebff;}
    .bg-grey-darkest {color: #3d4852;}
</style>

<table>
    <tr>
        <td class="bg-grey-darkest">
            <p class="text-purple-lightest">Lorem ipsum</p>
        </td>
    </tr>
</table>

gets transformed to something like this:

<style>
    .a {color: #f3ebff;}
    .b {color: #3d4852;}
</style>

<table>
    <tr>
        <td class="b">
            <p class="a">Lorem ipsum</p>
        </td>
    </tr>
</table>

Maizzle enables uglify by default only in config.production.php.

@hellocosmin hellocosmin released this Sep 13, 2018 · 78 commits to master since this release

Assets 2

This release introduces the ability to control how width should be specified for certain elements.

preferAttributeWidth

The preferAttributeWidth config key allows you to specify which elements should have the inline CSS width removed, so that they only use the HTML width="" attribute.

For example, with these settings:

'preferAttributeWidth' => [
    'table' => true,
    'td' => true,
    'th' => true,
    'img' => false,
],

the following HTML:

<table class="w-full" cellpadding="0" cellspacing="0" role="presentation">
  <tr>
    <td class="w-full">
      <img src="img/maizzle.png" alt="Maizzle" class="w-64">
    </td>
  <tr>
</table>

will be transformed into this:

<table cellpadding="0" cellspacing="0" role="presentation" width="100%">
  <tr>
    <td width="100%">
      <img src="img/maizzle.png" alt="Maizzle" style="width: 64px;" width="64">
    </td>
  <tr>
</table>

As you can see, because we set 'img' => false, the <img> tag still contains the inline CSS declaring the width.

Files changed

  • config.php
  • config.staging.php
  • config.production.php
  • tasks/js/after-jigsaw.js

For a complete diff, please see 2c8427c

@hellocosmin hellocosmin released this Aug 1, 2018 · 80 commits to master since this release

Assets 2

First release 🎉