Inlining CSS Files

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Now that the styles are being inlined, we can go a step further. I don't love having all my email styles inside a style tag. It works... but will be a problem once our app sends multiple emails: we don't want to duplicate this in every template.

Nope, in the real world, we put CSS into CSS files. Let's do that. Copy all of the styles and delete them. Inside the assets/css directory, let's create a new email.css file. Paste!

body {
margin: 0;
padding: 0;
background-color: #f3f3f3;
font-family: Helvetica, Arial, sans-serif;
}
h1 {
background-color: #264459;
color: #ffffff;
padding: 30px 0 50px 0;
font-weight: normal;
}
hr {
border: none;
border-top: 3px solid #264459;
margin: 20px;
}
.container {
background-color: #fefefe;
width: 580px;
margin: 0 auto;
}
.bottom {
background-color: #efefee;
}
.block {
margin: 0;
padding: 10px 20px 20px 20px;
}
.logo {
width: 100%;
}
.text-center {
text-align: center;
}
.btn {
display: inline-block;
padding: 10px 20px;
background-color: #264459;
color: #fefefe;
border: 1px solid #fff;
border-radius: 3px;
font-size: 20px;
font-weight: bold;
text-decoration: none;
}

So far, we've seen that the inline_css filter is smart enough to notice any style tags in the template and use that CSS to style the HTML tags. But you can also point the filter to an external CSS file.

Go back to config/packages/twig.yaml. To point to the CSS file, we need to add another Twig path: let's set the assets/css directory to styles. So, @styles will point here.

twig:
... line 2
paths:
... line 4
'assets/css': styles
... lines 6 - 10

Back in welcome.html.twig, we can pass an argument to inline_css(): a string of styles that it should use. To get that, use the source() function, @styles/ and then the name of our file email.css.

{% apply inline_css(source('@styles/email.css')) %}
... lines 2 - 50
{% endapply %}

The source() function is a standard Twig function... that you don't see very often. It tells Twig to go find the file - which could be a CSS file or another Twig template - and return its contents. It's basically a file_get_contents() for Twig. That's perfect, because inline_css() doesn't want the path to a CSS file, it wants the string styles it should use.

Let's try this! Hit back once again in your browser, bump the email, type a password, submit and... it looks good! And this time in the HTML source, the style tag is not there... but the inline styles are. That's another benefit of the CSS file: it got rid of the extra style tag, which makes our email a little bit smaller.

Using Sass or Encore for Email CSS?

By the way, if you prefer to use Sass or LESS for your CSS and are using Webpack Encore to compile all of that into your final CSS file, then... you have a problem. You must pass CSS to inline_css - you can't pass it Sass and expect it to know how to process that. Instead, you need to point inline_css at the final, built version of your CSS - the file that lives in public/build/.

Doing that seems easy enough: you could add another Twig path - maybe called encore - that refers to the public/build directory. Except... if you're using versioned filenames... then how do you know exactly what the built filename will be? And if you're using splitEntryChunks(), your one CSS file may be split into multiple!

This is a long way of saying that pointing to a CSS file with inline_css is easy... but pointing to a Sass file is... trickier. Later, we'll walk you through how to do it.

But first! The two rules of making an email look good in every email client are, one, use a table-based layout instead of floats or flex-box. And two, inline your styles. We've done the second, now its time to do the first. Does this mean we need to rewrite our HTML to use ugly, annoying tables? Actually... no!

Leave a comment!

  • 2019-11-06 Victor Bocharsky

    Hey Stephane,

    Good catch! And nice debugging with putting it inside the apply() filter! Sorry, I totally missed that duplicated doctype in the source code :/ Basically, "twig/cssinliner-extra" requires "tijsverkoyen/css-to-inline-styles" that will add a doctype if won't find it, here's the code responsible for this: https://github.com/tijsverk...

    So yes, you're right! We should move that doctype line inside of apply() Twig filter to avoid duplication. Well, it's not too important for email rendering and displaying, but anyway better do not have 2 doctypes :)

    Cheers!

  • 2019-11-02 Stéphane

    Hi Ryan,

    You put {% apply %} to the second line of the template then you have 2 doctypes declaration into your html for email. This is normal ? I try to put the {% apply %} to the first line of the email template and then there is only one doctype declaration .