Style
Each post is uniquely styled within a grid. ↓
Sinatra
Generates static pages and graphics. Styles code samples. ↓
No SQL
Not even a database! File-based metadata. ↓
Haml
Powers article templates and markup. ↓
SASS/CSS
Comprehensive and reusable styling. ↓
JavaScript
Taking it further…custom behavior & graphics. ↓
Style
Greg Storey, Airbag Industries1Before there were blogs we had websites. Beautiful, random websites that felt more like a zine — one page looking nothing like the one before or after it.

Many developments in computer science have nothing to do with the capabilities of computers, but with the way we use them.
A few years ago, an article on Airbag Industries1 inspired me to think about writing a blog with a unique design per post.
There are many benefits:
- It encourages creativity both at the computer and away from it
- It’s like a code kata for design
- You can easily experiment with cutting-edge CSS3 features or just learn CSS2
- You’ll learn how to build a style foundation for other designs in other applications
On the downside, it takes more time than simply writing prose.
Building an art-directed blog may seem as easy as dropping an
extra CSS link tag into every blog post, but a little extra
work upfront makes it a lot easier.
First, I started with a layout grid in CSS. Grids have been used extensively in print design but have only recently become popular in website design. I like Tyler Tate’s 1kb grid2 because it’s the simplest thing necessary to accomplish the task.
In standard website design, elements can be placed anywhere on a page. In contrast, a grid defines boundaries in regular increments, limiting the possibilities. Instead of stifling creativity, it encourages experimentation by making it easier to think about a finite number of widths for text or images.
I use a 12 column grid (double-click anywhere on this page to see it). Twelve columns are easily divided into other increments: 12 · 6+6 · 2+5+5 · 4+4+4 · 3+3+3+3
Sinatra
The Sinatra web framework has a beautiful REST API, can be extremely powerful, and is super fast.
It’s easily deployed with Phusion Passenger alongside my other Rack-based web applications.
I started with the Nesta4 blog engine and enhanced it with the additional CSS and graphic generation features I needed. All pages are cached statically with Sinatra::Cache5 for even greater speed. Although this blog could be generated with a static site generator, I find it more natural to write it in a dynamic style.
One example is the generation of text headline images with the Textorize6 renderer from Thomas Fuchs. I reference the images by URL in my CSS files. Sinatra generates the graphic on the fly when that URL is accesed (then caches it). Sinatra keeps URLs at the forefront of the web developer’s mind — where they should be!
Other resources are generated at the view level when needed. Code snippets are formatted by the Pygments7 command-line script via a special RedCloth tag implemented as a Haml filter.
Textorize (right half) renders text edges smoothly.
No SQL
heading: About this Blog
heading-font: Klavika-BoldCondensed
heading-color: #000
heading-background: transparent
published: December 30 2019 at 6pm
categories: meta design
summary: An article about...
format: haml
.column.article
%h2 Bacon
:custom
Haml and textile together...It’s much easier to debug a visual design in development on the local machine, check the whole thing into Git, and deploy when ready. No need to write an authentication system or an article preview mechanism.
Article files are stored on disk with a bit of YAML-esque metadata at the top. Each article can be in Textile format, or can use the full power of Haml for more complicated markup.
Early on I needed to store information about the title’s font size and color. Later I needed to add other custom fields for additional tagging. With an ORM this would have meant writing a migration and installing a plugin to manage tags.
There was no such problem here! I just added a few key-value pairs to the article’s metadata and started using them in the application immediately.
Haml
Not only does each post get its own CSS, it can also specify its own HTML, via Haml8.
When I’m designing visually in the browser, I’m thinking primarily about blocks of text and images, and how they will be styled. I don’t want to be thinking about HTML boilerplate or need to hunt to find a matching end tag.
Haml decreases the mental distance between HTML and CSS. I work directly with CSS selectors no matter what file I happen to be in.
But Haml isn’t well suited to working with inline text. Fortunately, there’s Textile9 for that!
The differences between the basic syntaxes of Textile and Markdown are negligible. Where it pulls away from Markdown is when you add new tags of your own. You can write view helpers that take a simple string and turn it into a group of HTML tags. It’s not only faster to type, but I can build reusable elements for movie links, syntax-highlighted code snippets, pull quotes, and product boxes.
Textile enhancements are easily used inline within any Haml document. In RedCloth 4, the steps are:
- Write a module with methods for each of the tags you want to use (such as
movieat right). - In each method, examine the
:textkey in the options, then build and return an HTML string. I build links for themovandm4vversions of each movie. - Write a Haml filter. The name of your module will be used as the name of the filter.
- Extend RedCloth with your custom module.
- Add more methods to your tags module as needed.
The code samples at right were built this way. I specify a path
to a source file and it runs it through pygmentize. It also
prepends a link to the source file itself.
The end result is a text system customized for writing about code, but it looks great, too! I can’t imagine using a CMS that was any less powerful than this.
.column.article
%h2 Custom Filter Sample
:custom_red_cloth
movie. http://example.com/movie.mov
source. /blog/code.rb
buy. http://peepcode.com/productsmodule CustomTags
def movie(opts)
my_text = opts[:text]
# Build an HTML string around my_text
# ...
return my_text
end
endmodule Haml
module Filters
# Renders custom textile.
module CustomRedCloth
include ::Haml::Filters::Base
lazy_require 'custom_tags'
def render(text)
red_cloth = ::RedCloth.new(text)
red_cloth.hard_breaks = false
red_cloth.extend CustomTags
red_cloth.to_html
end
end
end
endSASS/CSS
@import page_util
@import pygments_pastie
@include masthead-reset
@include dark-logo
@include dark-masthead-menu
$base_color: #d1e3ff
.prose
@include grid_6
float: left
margin:
left: $grid_gutter_width
top: $line_height * 5
Archives page with screenshots
In a recent copy of Print Magazine11, someone talked about designing a “style sheet” for the magazine back in the 70’s. For some reason, the extra space jolted my brain into thinking differently. Style. Sheet.
A stylesheet isn’t supposed to be a receptacle into which one tosses a bunch of unrelated visual directives. It’s a coherent guideline for how things should look. It should be planned and reusable. And like backend code, it may take several iterations of use in the real world to get it right.
SASS is a huge part of achieving that concept in this blog.
People get emotional when they hear about SASS. Either they see it as a threat to traditional CSS (used only by hacks who haven’t taken the time to learn it) or they welcome it as the best thing to happen to the Internet.
After working with this blog for a few months, I’m in the second camp.
As an example, any page can be designed with a light or a dark background. A few mixins at the top automatically adjust the page background, logo color (implemented as an image sprite), and menu colors. All these are contained in the stylesheet and don’t require any modification of the page’s HTML.
The technical parts of the grid and vertical rhythm are also
wired into my SASS stylesheets. I can give elements meaningful
names in HTML, then specify +grid_6 in SASS to make the
element six columns wide (alternately, I could have used
arguments like grid(6)).
Variables like grid_gutter_width and line_height are
available to the whole application. I rarely type a number;
instead I use meaningful variables to add margins or manipulate
elements into position.
And if I’m going to spend that much time in the powder room, I want to show it off. The archives page uses webkit2png.py10 to take a screenshot of each page and display it in a grid.
JavaScript
A unique design for each post is great, but how about unique JavaScript for each post?
For my Rails 3 upgrade screencast, I wanted to show a timeline of Rails release dates. I’ve recently become enamored with RaphaelJS12 and it took only a few lines of code to import a JavaScript file for posts that want one.
I wrote a custom Timeline function that plots dates as large
dots.
Conclusion
As with many features of this blog, it will take a while to figure out how to use them all fully. In the meantime, it’s a great learning experience and a fun output for creativity.
I haven’t published the source yet and may never. But now you have the ideas…implement them on your own blog!

In an earlier post, RaphaelJS was used to chart Rails release dates.
jQuery(function () {
var paper = Raphael("graph", 940, 81),
timeline;
timeline = new Timeline(paper, [
{date:"06/25/2004", version:"0.5"},
{date:"12/13/2005", version:"1.0.0"},
{date:"12/07/2007", version:"2.0.0"},
{date:"6/10/2010", version:"3.0 ?"}
]);
});1 Airbag Industries wrote on the old days when blogs were designed like zines.
2 The 1kb grid is the simplest thing necessary to get the job done. I convert it to SASS with the included css2sass command.
3 With a hack to ruby-mode in Emacs, I can easily jump to any Sinatra URL handler in a file.
4 Nesta is a minimal blog engine by Graham Ashton (but easy to extend). It’s built on the Sinatra web framework.
5 Sinatra::Cache source code
7 Pygments is the best syntax highlighter out there. When you view diffs and code at GitHub, you’re viewing pygments. Pipe code to it on the command line.
8 The Haml template engine is available for Ruby and other languages.
9 Textile is a syntax for formatting prose.
10 webkit2png.py is a capable screenshot capturing script for Mac OS X. There’s a Ruby port, but the original works well enough that I don’t see the need. I’ve wrapped it in the osxscreenshot gem.
11 Print Magazine is a classic (over 70 years in publication). Baseline Magazine is a more academic European counterpart. I recently bought a subscription to both.
12 RaphaelJS is a JavaScript-based SVG drawing library.
13 The Plainview web browser is a full-screen browser, useful not only for HTML-based presentations.