The Most Beautiful Templating Engine in the Room

Posted in Insights

Developers, by profession, are lazy. They have to be. Ambitious codesmiths would take delight in writing unique functions for every dropdown menu. But lazy - and good - browser dwellers target all childbearing nav items, write a behavior, and call it a day.

On the FINE dev team, we like our code DRY and concise. With SCSS, Ruby, and the rare drop of Coffeescript, we’ve managed to conquer every frontier. Except HTML.

We went to find an engine to simplify our markup and came across Jade - a clean-shaven templating language. Bundled up on a cold winter morning, it's CSS-syntax-meets-HTML. But at its britches, it's a powerful precompiler that makes mundane, redundant tasks inexplicably rewarding.

We’ve put together a few examples to make a front-ender’s casual walk into a slow stroll; node.js carries the language, so a helper like Grunt or Gulp (or even Codekit) is required for straight-to-HTML output.

The Basics

Ah, indentation: every tag is offset by an indent. A space after the tag name renders content cuddled inside, although Jade is smart enough to end with /> for img, link, meta, and that ilk. Comma-separated attributes are wrapped in parentheses immediately after the tag name.

html
head
  title My title
  meta(name="keywords", content="new zealand,folk,duo,figwit")

As a coterie of Rubyists, we especially took to the syntax because of the yield-like action - before compiling, the page will “yield” for to-be-included content. Assuming the above snippet is in a base layout (layouts/application) file, child files (views/pages/seo101), would be brought in with extends.

//- ./layouts/application.jade
head
  ...
body
  block content

//- ./views/show.jade
extends ../layouts/application

block content
  h1 How to Cheer Up Your Bandmate
  p Hey there, Bret. I see you looking down.
//-...

How to...

Like yield, included files are possible too. Our picky programmers usually frown at renders, but in Jade, a partial is a pal™.

//- ./partials/_header.jade
.head
  img.logo(src="albi-jellybeans.jpg")

//- ./layouts/application.jade
body
  include ../partials/header
  block content

//- Renders on child compile
//-

The Good

In addition to the dot-hash syntax, Jade uses mixins, functions, and variables with a SASS-like flavor. Variables can be called as content with an equal sign immediately after the tag along with (bonus!) string interpolation.

//- ./modules/mixins.jade
mixin rapper(name)
  p= "I love it when you call me #{name}"

//- ./views/show.jade
include ../modules/mixins
block content
  .wrapper
    +rapper('the hiphopopotamus')

//-

I love it when you call me the hiphopopotamus

That vanilla scripting can also be used to declare global variables and later on, functions. Jade loop syntax includes while and each, but more often than not, for is a friend™.
//- ./modules/mixins.jade
-var filler = "Looking round the room, I can tell that you are the most beautiful girl in the...room"

mixin quote_list(quote_array)
for quote in quote_array
p= quote

//- ./views/show.jade
include ../modules/mixins
.wrapper
...
+quote_list([filler, 'Now I'm just down to my business socks', 'Thanks Mel'])

//-

Looking round the room, I can tell that you are the most beautiful girl in the...room

Now I'm just down to my business socks

Thanks Mel

**The Double Good**

Since slothful keyboard-slingers favor coding a reusable mixin just once, a source can be included in a universal template (like application.jade or _header.jade) and referenced in child files.

//- ./layouts/application.jade
include ../modules/mixins

body
  block content

//- ./views/show.jade
.wrapper
  +rapper('rhymenocerous')

//- It works, don't worry about it.

Since Jade is a node package, classic JavaScript is like a third-string benchwarmer, ready to jump into action at the first opportunity. Crazy functions or mixins that barely even use Jade, like a hack-tastic random ipsum generator, compile just fine.

-var ripsum = function(min, max){
  - var min = min || 0
  - var max = max || 300
  //- And now for something completely different, a monologue delivered generously by Charlie Chaplin in The Great Dictator without punctuation, save contractions.
  - var ipsum_array = "I am sorry but I don't want to be an emperor That's not my business I don't want to rule or conquer anyone I should like to help everyone if possible  Jew Gentile black man white We all want to help one another Human beings are like that We want to live by each other's happiness not by each other's misery We don't want to  hate and despise one another In this world there is room for everyone And the good earth is rich and can provide for everyone The way of life can be free and beautiful but  we have lost the way Greed has poisoned men's souls has barricaded the world with hate has goose-stepped us into misery and bloodshed We have developed speed but we have  shut ourselves in Machinery that gives abundance has left us in want Our knowledge has made us cynical Our cleverness hard and unkind We think too much and feel too little  More than machinery we need humanity More than cleverness we need kindness and gentleness Without these qualities life will be violent and all will be lost The aeroplane  and the radio have brought us closer together The very nature of these inventions cries out for the goodness in men cries out for universal brotherhood for the unity of us  all Even now my voice is reaching millions throughout the world millions of despairing men women and little children victims of a system that makes men torture and imprison  innocent people To those who can hear me I say do not despair The misery that is now upon us is but the passing of greed the bitterness of men who fear the way of human  progress The hate of men will pass and dictators die and the power they took from the people will return to the people And so long as men die liberty will never perish  Soldiers don't give yourselves to brutes men who despise you enslave you who regiment your lives tell you what to do what to think and what to feel Who drill you diet you  treat you like cattle use you as cannon fodder Don't give yourselves to these unnatural men machine men with machine minds and machine hearts You are not machines You are  not cattle You are men You have the love of humanity in your hearts You don't hate Only the unloved hate the unloved and the unnatural Soldiers Don't fight for slavery  Fight for liberty In the 17th Chapter of St Luke it is written the Kingdom of God is within man not one man nor a group of men but in all men In you You the people have the  power the power to create machines The power to create happiness You the people have the power to make this life free and beautiful to make this life a wonderful adventure  Then in the name of democracy let us use that power let us all unite Let us fight for a new world a decent world that will give men a chance to work that will give youth a  future and old age a security By the promise of these things brutes have risen to power But they lie They do not fulfil that promise They never will Dictators free  themselves but they enslave the people Now let us fight to fulfil that promise Let us fight to free the world to do away with national barriers to do away with greed with  hate and intolerance Let us fight for a world of reason a world where science and progress will lead to all men's happiness Soldiers in the name of democracy let us all  unite".toLowerCase().split(' ')
  - var randomMax = Math.round(Math.random() * (max - min)) + min;
  - var ipsum = []
  - var i = 0
  while i <= randomMax
    - var word = ipsum_array[Math.floor( Math.random() * ipsum_array.length)]
    if i == 0
      -word = word.charAt(0).toUpperCase() + word.slice(1);
    else
      if i % 11 == 0 || i % 28 == 0
        - word = '. '+word.charAt(0).toUpperCase() + word.slice(1);
      else
        - word = ' ' + word
    if i == randomMax
      - word += '.'
    - ipsum.push(word)
    - i++
  - return ipsum.join('')
-}

//- ./views/show.jade
.wrapper
  p= ripsum(10,20)

//-

Return your for now brutes life new there that people of. Promise machinery what fight unloved.

Or, the best features of both languages can join for a useful placeholder mixin.
mixin ph(dimensions, extraclass, placeholder_type, extrainfo, sizeopts)

  • var size = dimensions.split('x')

  • var placeholder_type = placeholder_type || 'image'
    if typeof size[1] === "undefined"

    • size[1] = size[0]
    • dimensions = size[0]+'x'+size[1]
      if size[0].indexOf('%') < 0
    • size[0] = size[0]+'px'
      if size[1].indexOf('%') < 0
    • size[1] = size[1]+'px'

    case placeholder_type
    when 'html'

    //- Pure HTML placeholder (requires applicable CSS)

    • var style = 'width:'+size[0]+'; height:'+size[1]+';background:#e9e9e9;color:#3d3d3d;text-align:center;display:table;'
      .placeholder(style=style, class=extraclass)
      .dimensions(data-dimensions=dimensions, data-info!=extrainfo style="display:table-cell;vertical-align:middle;")!= dimensions

    when 'image'

    //- Get placeholder image from placehold.it

    when 'kitten'

    //- Get placeholder image from placekitten.com

//- ./views/show.jade
.wrapper
+ph('200x300', '', 'kitten')

//- Placeholder


Jade, you're so beautiful, you could spend part of your time modeling HTML.

More Insights