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,246 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
May 24, 2013 at 5:39 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam Oops! My mistake! I hadn't gotten that far in my testing - I'm still baby stepping my way through the process. ... read »
May 24, 2013 at 5:13 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
Hi Jason, Thanks for checking up on that, but I still stand firm on my position. :) There are actually two listLast()'s in use, and you're right that the one using a space as a delimiter is fine. ... read »
May 24, 2013 at 4:45 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Ben I have been lurking your site for quite some time, and haven't stepped up to comment until today. Thanks for all the great info - keep it up! @Adam I believe you are mistaken... as the commen ... read »
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools