Using The RequireJS Build / Optimizer To Concatenate Modularized CSS Files

Posted January 10, 2012 at 10:19 AM by Ben Nadel

Tags: HTML / CSS

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 &copy; 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.




Reader Comments

Jan 10, 2012 at 12:20 PM // reply »
9 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.)


Jan 10, 2012 at 12:34 PM // reply »
11,314 Comments

@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!


Jan 11, 2012 at 2:58 AM // reply »
27 Comments

You should look into less css if you haven't already. it rocks big time.


Jan 12, 2012 at 4:36 AM // reply »
6 Comments

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.


Jan 19, 2012 at 1:16 AM // reply »
2 Comments

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


Jan 19, 2012 at 12:15 PM // reply »
3 Comments

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)))#" />


Jan 19, 2012 at 12:16 PM // reply »
3 Comments

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.


Dec 17, 2012 at 7:13 AM // reply »
1 Comments

@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


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Jun 20, 2013 at 3:15 AM
A Billion Wicked Thoughts By Ogi Ogas And Sai Gaddam
nice post i love it thanks 4 u :) ... read »
seb
Jun 20, 2013 at 2:32 AM
Working With Inherited Collections In AngularJS
@mike, @ben, The best article about scope and prototypal prototypical inheritance in angularjs is http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical- ... read »
Jun 20, 2013 at 2:17 AM
ColdFusion NumberFormat() Exploration
Nice read thanks Ben, Is there a way to mask a negative number? Long story short in the finance sector when you go 'short' on a stock you want the price to fall this is a good thing because you are ... read »
Jun 20, 2013 at 1:09 AM
The Beauty Of The jQuery Each() Method
my html code : <html> <head> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="nss.js"> ... read »
Jun 19, 2013 at 11:31 PM
Directive Link, $observe, And $watch Functions Execute Inside An AngularJS Context
@Ben, bunch to learn indeed, but thats fun part : ) ... read »
Jun 19, 2013 at 10:41 PM
Referencing ColdFusion Query Columns In A Loop Using Both Array And Dot Notation
Burdock-roots Are you going fat day by day? You need to be good for your family and make some money too. So we bring for you a best product that helps you to be more energetic every day. You will b ... read »
Jun 19, 2013 at 9:52 PM
Working With Inherited Collections In AngularJS
I recognize the applicability of your solution, and how easy it makes to share data across multiple views or even "submodules" of rather simple application. But it seems to me that it creat ... read »
Jun 19, 2013 at 9:38 PM
Directive Link, $observe, And $watch Functions Execute Inside An AngularJS Context
@Alesei, Glad you like it. Even after working with AngularJS for months, I still get a bunch of unexpected, "$digest is already in progress". So hard to debug sometimes! ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools