Inlining CSS into the head of a page

Orde Saunders' avatarPublished: by Orde Saunders

Including CSS inlined into the <head> of a web page is a performance and fault tolerance best practice. As this technique requires the CSS to be included as part of the markup delivered by the server, integrating it with the output of build systems using CSS pre-processors designed to produce externally included files requires extra work.

Generate the CSS

A typical build process these days includes a CSS pre-processor to generate a single CSS file that is included into the page using <link rel="stylesheet" href="/path/to/main.css"/>. To inline styles you will need to add an extra build target that will generate the CSS to be included in the <head> - I imaginatively call mine head.css.

At this point you have two CSS files being built into the public directory of the webserver: head.css and main.css.

Include the CSS

Once the CSS is generated you now have to include it in a <style> element and how you do this will depend on you are generating your markup. Outlined below are three methods I have used.

Django

For more complex sites that benefit from using a framework I use Django. Whilst the details of how to integrate it with your framework of choice will vary, this should give you an idea of some of the considerations.

If the public directory of your webserver isn't accessible from your server side code (for example if you're serving it from a CDN) you'll have to make sure a copy of the head.css is available to the server side code as part of your deployment.

The template now needs to include the contents of the head.css file into the page. This could be as simple as using a template include tag but, as this is going to be referenced from every page, I cache the contents in memory to save a file system read.

As source maps don't work with inline stylesheets I also have a setting in my dev environment that allows me to include head.css as an external file using a <link> which means that I can use source maps.

Abstracting away a lot of the implementation details, essentially what we are left with is some code to load the contents of the stylesheet to be inlined:

head_css = False
if settings.INLINE_CSS:
    head_css = cache.get('head.css')
    if head_css is None:
        with file('%s/css/head.css' % (settings.STATIC_ROOT)) as f:
            head_css = f.read()
            cache.set('head.css', head_css)

And conditionally include it in the desired manner in the template:

{% if head_css %}
  <style>{{ head_css }}</style>
{% else %}
  <link href="{% static 'css/head.css' %}" rel="stylesheet"/>
{% endif %}

Assemble

If you are doing a static build of a site - such as with Assemble - then inlining the CSS can be done during the build stage. The sequence is important here: you need to compile the CSS before the build step to make sure you include the right version of the CSS.

The handlebars layout for the page then references head.css and it gets inlined into the built output.

<style>{{glob "path/to/head.css"}}</style>

PHP

These days I mostly use PHP for small or proof of concept projects requiring simple server side functionality where a full framework is overkill. In these situations I just include the CSS file.

<style><?php include("path/to/head.css"); ?></style>

Conclusion

There are different ways to inline a CSS file into a page and the method you chose will depend on how you are generating your markup.

As well as the details of how the inclusion is accomplished you should also consider the performance and debugging implications.