Inlining CSS into the head of a page
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.