Using The RequireJS Build / Optimizer To Concatenate Modularized CSS Files
A little while back, I started looking at RequireJS as away to organize and modularize my JavaScript code. And, while I am still getting my feet wet in modular JavaScript web application development, I can tell you that I have really enjoyed using RequireJS - and, that I plan to continue to integrate it into my development process. The asynchronous loading and the on-demand loading of JavaScript modules are nice feature of RequireJS; but the real "ah ha!" moment for me came with the ability to organize my JavaScript code into small(er) cohesive files. In my reading, I noticed that the RequireJS optimizer - r.js - also works with CSS (Cascading Style Sheets) files. As such, I wanted to see if RequireJS could afford the same kind of benefits in the world of CSS that it provided in the world of JavaScript.
The functionality of the RequireJS optimizer as it applies to CSS is rather simple. As stated on the RequireJS website, the optimizer works as such:
Optimizes CSS by inlining CSS files referenced by @import and removing comments.
It doesn't do any syntax-based optimization or minifacation - it simply removes comments and inlines files that have been linked using the @import command. This may not seem like much; but, given the fact that my "ah ha!" moment in the JavaScript domain came with the ability to organize my files, it would seem that this is exactly the kind of functionality that I would need for my CSS.
To experiment with the RequireJS optimizer for CSS, I created a trivial HTML page that linked to a CSS file that, itself, linked to two other CSS files:
<!DOCTYPE html>
<html>
<head>
<title>Using RequireJS To Optimize CSS Files</title>
<!-- Link the main stylesheet. -->
<link rel="stylesheet" type="text/css" href="./main.css"></link>
</head>
<body>
<!-- BEGIN: Site Container. -->
<div class="l-container">
<div class="l-header">
<div class="logo">
The Blog Of Ben Nadel
</div>
</div>
<div class="l-body">
<h1>
Using RequireJS To Optimize CSS Files
</h1>
<p>
This is an exploration of the use of the RequireJS
compiler / optimizer (r.js) as a means to concatenate
and optimize CSS files.
</p>
<p>
So far, I really like RequireJS as a way to organize
and then package JavaScript modules. It might be a
nice, simply way to package CSS as well.
</p>
</div>
<div class="l-footer">
<p class="copyright">
Copyright © 2012. Ben Nadel.
</p>
</div>
</div>
<!-- END: Site Container. -->
</body>
</html>
Notice that some of the class names are prefixed with, "l-". As part of the exploration, I thought I might start trying to use some SMACSS (Scalable and Modular Architecture for CSS) methodologies (which use "l-" to define layout definitions); as such, I created a "Base" CSS file and a "Layout" CSS file. The "Modules" CSS for the page is defined in the main CSS that is loaded:
main.css - Our Page Modules
/* Base styles. */
@import url( "./base.css" );
/* Layout styles. */
@import url( "./layout.css" );
/* Modules for this section. */
div.logo {
background-color: #333333 ;
color: #FFFFFF ;
font-size: 11px ;
padding: 5px 0px 5px 0px ;
text-align: center ;
text-transform: uppercase ;
width: 155px ;
}
p.copyright {
color: #999999 ;
font-size: 11px ;
margin: 0px 0px 0px 0px ;
}
As you can see, our modules CSS file links to the Base and Layout files using the @import command. The RequireJS optimizer will inline these two additional files so that they won't incur the cost of two additional HTTP requests.
You have to invoke the node.js-based RequireJS optimizer from the command line. But, rather than doing that manually, I figured I would create a Bash script that would encapsulate the "build" of my application:
build - Bash Script For Invoking RequireJS Optimizer
## Optimize the CSS file. Creates "main-built.css" file.
node ../r.js -o cssIn=main.css out=main-built.css
When I run the above build script in my CSS directory, the RequireJS optimizer creates "main-built.css" which is a concatenated version of all the required CSS files:
main-built.css - Our Concatenated CSS File
body {
background-color: #FFFFFF ;
font-family: "lucida grande", helvetica, arial, verdana ;
font-size: 14px ;
margin: 30px 20px 30px 20px ;
padding: 0px 0px 0px 0px ;
}
h1 {
font-size: 150% ;
}
p {
line-height: 1.5em ;
}
div.l-container {
background-color: #F0F0F0 ;
-moz-border-radius: 5px 5px 5px 5px ;
border-radius: 5px 5px 5px 5px ;
margin: 0px auto 0px auto ;
padding: 20px 20px 20px 20px ;
width: 450px ;
}
div.l-header {
height: 50px ;
position: relative ;
}
div.l-body {
padding: 0px 0px 20px 0px ;
}
div.l-footer {
border-top: 1px dotted #CCCCCC ;
height: 30px ;
padding: 20px 0px 0px 0px ;
position: relative ;
}
div.logo {
background-color: #333333 ;
color: #FFFFFF ;
font-size: 11px ;
padding: 5px 0px 5px 0px ;
text-align: center ;
text-transform: uppercase ;
width: 155px ;
}
p.copyright {
color: #999999 ;
font-size: 11px ;
margin: 0px 0px 0px 0px ;
}
As you can see, the RequireJS optimizer doesn't do anything fancy with CSS (the way it obfuscates and compacts JavaScript code); rather, it simply inlines the CSS of the linked files.
As new as I am to the concept of Modular JavaScript architecture, I'm even less experienced when it comes to good CSS organization. That said, I think the RequireJS optimizer for CSS gives me some really fun stuff to play with. Going from one monolithic CSS file to many smaller, cohesive files, however, is going to be a long journey of exploration and understanding.
Want to use code from this post? Check out the license.
Reader Comments
Now that you are on your way to becoming a bash expert it would look like a good exercice to a build a script that does exactly that.
(tips : cat filename1 >> filename2 appends the content of filename1 into filename2,
grep and egrep are used to find lines that match a regex. You can use -o to capture only the part that matches the regex instead of the whole line.)
@Guillaume,
Trying to write this in a Bash script would definitely be awesome! I'll put that on my list of things to do. I know that people talk about grep like it's the holy grail :) I do love me some regular expressions, of course!
You should look into less css if you haven't already. it rocks big time.
Ben, for a CF approach to modularising JS and CSS check out Dominic Watson's superb CfStatic: http://cfsimplicity.com/43/the-simplicity-of-cfstatic
As well as concatenation it will also minify and compile LESS CSS syntax, as recommended by Nelle.
Ben, for an example of how to organize CSS by using LESS take a look at how it's done in Bootstrap, from twitter.
This links to the githup repo and their less files:
https://github.com/twitter/bootstrap/tree/master/lib
I used coldfusion to join my CSS and Javascript for example:
<cfapplication name="style_values" sessionmanagement="Yes" setclientcookies="Yes"><cfinclude template="/include/session.cfm" /><cfprocessingdirective suppresswhitespace="yes" pageEncoding="utf-8"><cfsetting enablecfoutputonly="true"><cfsetting showdebugoutput=false><cfcontent type="text/css; charset=UTF-8" reset="true"><cfsavecontent variable="variables.pagecontent"><cfoutput>
html
{
min-width:#style_values.pagewidth#px;
}
<cfinclude template="head.cfm" />
<cfinclude template="navigation.cfm" />
<cfinclude template="content.cfm" />
<cfinclude template="footer.cfm" />
<cfinclude template="classes.cfm" />
h2
{
font-family:#style_values.font.primary.face#;
color:###style_values.textcolor#;
font-size:#style_values.font.h2.size#px;
}
<cfcontent type="text/html" variable="#tobinary(tobase64(RemoveBlankLines(variables.pagecontent)))#" />
Woops the last line should be <cfcontent type="text/css" variable="#tobinary(tobase64(RemoveBlankLines(variables.pagecontent)))#" />
And I didn't mention that I call this as a CSS file.
@Ben For larger applications, having CSS directly correspond to small templates allows for modular CSS management. Getting this to work practically with builds can be tricky.
I've put together one solution along these lines supporting CSS loading and builds through the r.js optimizer as another dependency. Thought you may be interested to check it out.
https://github.com/guybedford/require-css