Using Compass with Blueprint in Rails

For a new programming project I decided to see what the whole CSS framework and grid design craze is about for myself, and to refresh my Ruby on Rails programming skills.

CompassA little research turned up Chris Eppstein's Compass gem. This is a very nice meta-framework, which can be used with Rails, Merb, Sinatra or other Ruby frameworks as well as pure Ruby, that combines Hampton Caitlin's SASS gem with any of several CSS frameworks, including (at this writing) Blueprint, Yahoo! UI Grids and 960 Grid System. Blueprint seemed sufficient for my needs and the easiest choice so I went with it.

"Compass is a real stylesheet framework — not just a collection of classes. With Compass, you still use the best of breed css frameworks; adapted to make them easier to configure and apply to your semantic markup."

Installing is simple enough (on Mac or Linux, Windows may be different and in this post I am going to only give the Mac info as that's what I use), just:

# To get the best version of HAML, clone its repo
git clone git://github.com/nex3/haml.git
cd haml
(sudo) rake install

# GitHub already configured as a gem source:
(sudo) gem install chriseppstein-compass
# Otherwise:
(sudo) gem install chriseppstein-compass --source=http://gems.github.com/

You can install Compass into an existing Rails project or a new one. Or, using the application template feature adding in Rails 2.3.0, you can add it to your base template for automatic inclusion. Either way the commands are essentially the same:

cd app_dir
haml --rails
compass --rails -f blueprint

For the last command you can substitute the name of another supported framework for Blueprint. The Compass wiki even includes basic documentation for adding your own CSS framework support.

Although not strictly necessary, current Rails best practice says you should open your app's environment.rb and add a config.gem statement for Compass:

config.gem "chriseppstein-compass", :lib => 'compass', :version => '>= 0.6.1'

One drawback to using these tools, especially HAML, is that Rails' native code generation for views and layouts will not work out of the box. Instead you'll have to convert what's generated to HAML or just write them from scratch. Catlin has provided command line tools to convert HTML and CSS files to HAML and SASS you might grab the script posted by by Michelangelo Altamore and included here to work on the ERB templates that Rails scaffolding generates.

#! /bin/bash
# erb2haml
# by Michelangelo Altamore
 
# Convert all files having a html.erb extension in the current dir
# to haml format with html.haml extension. HAML gem must be installed.

for erb_file in `find . -name *.html.erb`; do
if [ -f $erb_file ] ; then
name=${erb_file%.html.erb};
    html2haml  > .html.haml;
  fi;
done

Happily HAML uses a lot less code to create the same end result so starting from scratch is very easy and the template language seems natural enough that I was able to code at speed in the first few days.

Tip: If you use TextMate, and you ought to, check out the SASS bundle from SeaOfClouds. You may also want to install the Ruby HAML TextMate bundle to get HAML syntax highlighting.

HAML and SASS

These two tools, for me, bring the DRY principle to HTML and CSS, respectively. The default ERB builder that comes with Rails still requires a lot of typing that the runtime engine ought to be able to do (e.g., closing tags) and Rails does nothing special to help with CSS at all.

In his blog post Living With Haml & Sass Carsten Nielsen writes "So pretty much everyone agrees Haml is fantastic, but what about Haml's smokin' hot sister Sass? I have been using Sass exclusively on LearnHub since day one too, I find the benefits to be just as high as Haml if not higher, especially when it comes to nested selectors, less typing means less room for error and a document that is compact and easier to read. There are not as many people out there using Sass though, and I think that is a shame. Sass lets you do some pretty nifty things that in turn give you great power. You can easily scope sets of rules to certain selectors and change them with ease, Sass removes all of the syntactic dirt and mostly all of the repetition from CSS."

I agree with Nielsen; I actually got started first from SASS and only after buying in big time did I start looking at HAML.

HAML—XHTML Abstraction Markup Language—uses a variation of the convention over configuration to substantially reduce the amount of text in a view, by using indentation to demarcate blocks. And since div is the most commonly used element, you don't need to type it at all except when there's no ID or class assigned to it. A simple example:

%h1 This is the title

	.some_class Some free text
	
	.someother_class
		.userclass
			.username.label Name
			.userage.label Age
			
		- for user in users
			.username= user.username
			.userage= user.age

The above generates the following HTML:

This is the title

Some free text
Name
Age
Bill
35
Viv
32
Chris
28

SASS—Syntactically Awesome StyleSheets—offers a similar simplification for CSS and adds the use of multiple types of variables, directly as constants and through what Caitlin calls mixins. Mixins enable you to define groups of CSS attributes and then include them inline. As in HAML, indentation is used to represent content and inclusion.

By content I mean attributes and the corresponding value and inclusion that a selector is effective only when used as a child of the previously specified selector:

!heavy = bold // a simple variable
=heavy_label	// a mixin
	:font
		:weight !heavy
		:size 126%
	:text-decoration underline

.some_class
	+heavy_label
	:color #0000bb
	
.label
	+heavy_label

This SASS gets this CSS:

.some_class {
	font-weight: bold;
	font-size: 126%;
	text-decoration: underline
	color: #0000bb;}
	
	.label {
	font-weight: bold;
	font-size: 126%;
	text-decoration: underline}

Blueprint

Blueprint Grid DemoThis is AFAIK one of the more widely used CSS grid systems. While explaining grids in detail is beyond the scope of this post, you can dive into it at The Grid System. The key appeal for me is as a developer the resulting design is more visually appealing then I get on my own and at a certain level decisions are made for me which, not being a graphic designer, is a good thing.

Essentially grids embody the design principal The Rule of Thirds as applied to layouts. Khoi Vinh, design designer for the New York Times online presence, is probably most responsible for bringing the grid concept into widespread use in web design. For me the most important benefit is that using a grid produces better horizontal alignment of blocks as well as strong vertical rhythm.

Blueprint, developed by Joshua Clayton, "is a CSS framework, which aims to cut down on your development time. It gives you a solid foundation to build your project on top of, with an easy-to-use grid, sensible typography, useful plugins, and even a stylesheet for printing."

Grid CSS divides the sreen into a fixed number of equal width columns, Clayton chose 24 for Blueprint, and by assigning a certain number of columns to a given block level element you control the element's width; the Blueprint grid demo page illustrates a good set of examples. From left to right the total number of columns either adds up to 24 or the rightmost element is assigned the special class last.

The framework includes several handy mixins and a large number of plugins that, combined with SASS, greatly ease use of its features. For instance, to add a width to a selector add the column(N) mixin and to get an unordered list with no bullet image use the no-bullet mixin:

ul.navigation
	+column(24)
	+no-bullet

Compass

Compass takes the CSS supplied Blueprint and the other supported frameworks and transforms them into SASS files; HAML comes with utilities to convert SASS to CSS, HAML to HTML and vice versa. Chris has also provided some command line utilities, all invoked through the Compass executable, that can update your project files, add new patterns to the project and more. For Rails projects, Compass automatically updates the compiled CSS files whenever you change the SASS.

The big value here is in the integration; other than the utilities it doesn't have much of its own. Still, check out the Compass Sample Rails Application for a really detailed example of how it fits in.

Now that you have all the pieces let's look at a real albeit simple example. Many applications these days try to find ways to link in social applications, so consider a user profile page with name, address, one or more phone numbers and links to the user's pages on Facebook, LinkedIn, Twitter and GitHub (hey, this is a Rails app, right?).

Implied Data Models and Routes

  • User: first name, last name, email, crypted_password, salt
  • Phone: phone type (home, cell, work, fax), number, is primary, user id
  • Social Link: network (FB, LI, etc.), network name, user id
  • Address: street, city, state or province, postal code, user id

The example here expects you are using Rails 2.3.2 or later as certain functionality added then is used, e.g. nested models, fields_for and accepts_nested_attributes_for, and the corresponding routes, models and controllers are left as an exercise for the reader as outside the scope of this post.

Create/Edit User Profile View (_user_form.html.haml)

%body

	#container
	
		%h1 Your User Profile
		
		- form_for @user do |f|
		
			.form_row
			
				.label= f.label :first_name
				= f.text_field :first_name
				
				.label= f.label :last_name
				= f.text_field :last_name
				
			.form_row
			
				.label= f.label :email
				= f.text_field :email
		
			.form_row
			
				.label= f.label :password
				= f.password_field :password
		
				.label= f.label :password_confirmation, 'Confirm Password'
				= f.password_field :password_confirmation
		
			.form_row
		
				- f.fields_for :address do |af|
		
					.label= af.label :street
					= af.text_field :street
					
					%br
					
					.label= af.label :city
					= af.text_field :city
					
					.label= af.label :state_or_province, 'State/Province'
					= af.text_field :state_or_province
					
					.label= af.label :postal_code, 'Zip/Postal Code'
					= af.text_field :postal_code
				
			
			.form_row
			
				.child_block
				
					%h3
						Phones
						.adder= link_to 'Add', new_user_phone_path
					
					.label= Type
					.label.last= Number

					- for phone in @user.phones
					
						= phone.phone_type
						= phone.phone_number
							
					- f.fields_for :phones do |pf|
					
						.label= pf.label :phone_type
						= pf.select :phone_type, %w[home cell work fax], :include_blank => 'Select one'
						
						%br
					
						.label= pf.label :phone_number
						= pf.text_field :phone_number
						
						%br
					
						.label= pf.label :is_primary, 'Primary contact?'
						= pf.check_box :is_primary
						
						%br
					
						= pf.submit
						
				.child_block
		
					%h3
						Social Links
						.adder= link_to 'Add', new_user_sociallink_path
						
					.label= Network
					.label.last= Network ID

					- for sociallink in @user.sociallinks
					
						= sociallink.network
						= sociallink.network_name
							
					- f.fields_for :sociallinks do |sf|
					
						.label= sf.label :network
						= sf.select :network, %w[Facebook LinkedIn Twitter GitHub], :include_blank => 'Select one'
						
						%br
					
						.label= sf.label :network_name
						= sf.text_field :network_name
						
						%br
					
						= sf.submit, 'Add Link'
						
			.form_row
			
				= f.submit, 'Save Your Profile'

The SASS

#container
	+container
	
	.form_row
		+column(24)
		
		.label
			+column(3)
			:font-weight bold
		
		.child_block
			+column(12)
			
			.label
				+clearfix
		
		.adder
			+column(4)
			
			a
				:background-color #0000bb
				:color #fff