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 »
8 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 »
10,743 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 »
26 Comments

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


Jan 12, 2012 at 4:36 AM // reply »
2 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 »
2 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 »
2 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.


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
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 21, 2012 at 1:58 AM
Updated: Converting A ColdFusion Query To CSV Using QueryToCSV()
Hi Ben, why do you need to have so many double quotes when adding the field and field name to the row data? ----------------------------------------- <cfset LOCAL.RowData[ LOCAL.ColumnIndex ] = ... read »
AXL
May 21, 2012 at 1:24 AM
URL Rewriting And ColdFusion's WriteToBrowser Image Functionality (CFFileServlet)
@Mounir, Open your lower case URL Rewrite rule and add the following condition. Condition input: {REQUEST_URI} Check if input string: Does Not Match the Pattern Pattern: ^/CFFileServlet/_cf_ca ... read »
May 20, 2012 at 4:28 AM
Understanding The Complex And Circular Relationships Between Objects In JavaScript
@Will Vaughn I tried your javascript example but got this error:- foo.print is not a function ... read »
May 19, 2012 at 5:37 AM
A Graphical Explanation Of Javascript Closures In A jQuery Context
Thanks for this article, but I fear you missed an important point. If variables in the outer context change, these changes affect the inner anonymous functions as well. That means: if you change the ... read »
May 18, 2012 at 3:39 PM
Parsing CSV Data With An Input Stream And A Finite State Machine
Can you use file upload button with this? and read live? or does the file have to already be on the server saved? ... read »
May 18, 2012 at 1:06 AM
VIRGO (Aug. 23-Sept. 22): Dead On The Money!
A friend of mine and I were arguing about astrology and she told me that he believes in astrology. She hasn't provided me with any evidence that the belief makes any sense to me. She she been telling ... read »
May 17, 2012 at 11:32 PM
Using ColdFusion to Handle 404 Errors (Page Not Found) On Development Server
Very easy the configuration. I read a lot pages and I can't find the solution. I open the administrator and change this Administrator/server settings/Error Handlers/Missing Template Handler and p ... read »
May 17, 2012 at 3:13 PM
LOCAL Variables Scope Conflicts With ColdFusion Query of Queries
I never cease to be amazed that almost EVERY random CF issue I come across lands me on your site. Thank you for documenting your findings for the world. ... read »