ruk·si

🗿 Jade Guide

Updated at 2012-12-18 11:54

Jade is HTML templating with JavaScript, usually used with Node.js. Related to HTML guide.

TODO: Add Jade syntax highlight.

You use tag name and jQuery style selector to specify HTML elements.

div
// <div></div>

div#container
// <div id="container"></div>

div.user-details
// <div class="user-details"></div>

div#foo.bar.baz
// <div id="foo" class="bar baz"></div>

#foo
.bar
// <div id="foo"></div><div class="bar"></div>
// Default tag is <div> but it is good to always include it for consistency.

Nesting is done by indention. Use 2 space indention like in JavaScript.

!!!5
html(lang="en")
  head
  body
    div
// <!DOCTYPE html><html lang="en"><head></head><body><div></div></body></html>

You can add content to elements by one line of text, by dotting or by piping.

// One liner
p wahoo!
// <p>wahoo!</p>

// Dotting
p.
  foo asdf
  asdf
  asdfasdfaf
   asdf
    asd
// <p>foo asdf
// asdf
// asdfasdfaf
// asdf
// asd
// </p>

// Piping
p
  | foo bar baz
  | rawr raw rawr
  | super cool
  | go jade go
// <p>
// foo bar baz rawr raw rawr super cool go jade go
// </p>

// Piping is not required on elements that only accepts text e.g. script.
script
  if (foo) {
    bar();
  }
  else {
    baz();
  }

Unescaped text is prefixed by ! while unbuffered JavaScript is prefixed by -.

- var html = "<script></script>"
!{html}
// <script></script>

Interpolation is done with #{}.

- var teejii = { name: 'tj', email: 'tj@vision-media.ca' };
div#user #{teejii.name} &lt;#{teejii.email}&gt;
// <div id="user">
//   tj &lt;tj@vision-media.ca&gt;
// </div>

Escaping is done with \.

p \#{escaped_string}

Element attributes are specified in parenthesis.

a(href='/login', title='View login page') Login

// Boolean attributes
input(type="checkbox", checked)

// Outputs only if someVariable is true.
input(type="checkbox", checked=someVariable)
input(
  type='checkbox'
  name='agreement'
  checked
)

- var user = { id: 12, name: 'tobi' }
a(href='/user/' + user.id)= user.name
a(href='/user/#{user.id}')= user.name

- var bodyClasses = ['user', 'authenticated']
body(class=bodyClasses)

Nested tags

// Following two generate same HTML.
label Username:
  input(name='user[name]')
label
  | Username:
  input(name='user[name]')

ul
  li.first
    a(href='#') foo
  li
    a(href='#') bar
  li.last
    a(href='#') baz
// <ul>
//   <li class="first">
//     <a href="#">foo</a>
//   </li>
//   <li>
//     <a href="#">bar</a>
//   </li>
//   <li class="last">
//     <a href="#">baz</a>
//   </li>
// </ul>

// Block expansion with same output
ul
  li.first: a(href='#') foo
  li: a(href='#') bar
  li.last: a(href='#') baz

Commenting

Normal HTML comments are //. Silent comments are //- They work to comment out whole nested branch.

body
  //
    div#content
    h1 Example
// <body>
//   <!--
//     <div id="content">
//       <h1>Example</h1>
//     </div>
//   -->
// </body>

Conditional comments are supported

head
  //if lt IE 8
    script(src='/ie-sucks.js')

// <head>
//   <!--[if lt IE 8]>
//     <script src="/ie-sucks.js"></script>
//   <![endif]-->
// </head>

There are case statements.

html
  body
    friends = 10
    case friends
      when 0
        p you have no friends
      when 1
        p you have a friend
      default
        p you have #{friends} friends

// Or with block expansion
friends = 5
html
  body
    case friends
      when 0: p you have no friends
      when 1: p you have a friend
      default: p you have #{friends} friends

You can use filters to generate content from other precompilers.

// :stylus, must have stylus installed
// :less, must have less.js installed
// :coffeescript, must have coffee-script installed
// :markdown, must have markdown-js, node-discount, or marked installed

body
  :markdown
    Woah! jade _and_ markdown, very __cool__.
    we can even link to [stuff](http://google.com)

Code

Unbuffered JavaScript row is prefixed by -.

- var foo = 'bar';

- var smilies = {lol: ':D', sad: ':('}
- for (var name in smilies)
  p= smilies[name]

- var foo = false;
- if (foo)
  ul
    li yay
    li foo
    li worked
- else
  p oh no! didnt work

- var items = [1,2,3,4];
- if (items.length)
  ul
    - items.forEach( function(item) {
      li= item
    - })

Buffered and escaped code is prefixed by =.

- var foo = 'bar'
= foo
h1= foo
//  bar<h1>bar</h1>

Buffered and unescaped code is prefixed by !=.

- var aVariableContainingMoreHTML = '<h1>:DSL</h1>';
p!= aVariableContainingMoreHTML

// Following are equal, designer-friendly variants.
- var foo = 'foo ' + 'bar'
foo = 'foo ' + 'bar'

// Thus, previous could be written like.
aVariableContainingMoreHTML = '<h1>:DSL</h1>';
p!= aVariableContainingMoreHTML

Jade has also first-class if, else if, else, until, while, unless etc.

- var foo = 'bar';
if foo === 'bar'
  ul
    li yay
    li foo
    li worked
else
  p oh no! didnt work

Iteration

Basic each.

- var items = ['one', 'two', 'three']
ul
  each item in items
    li= item

// <ul>
//   <li>one</li>
//   <li>two</li>
//   <li>three</li>
// </ul>

Iterating with index.

items = ["one", "two", "three"]
each item, i in items
  li #{item}: #{i}

// <li>one: 0</li>
// <li>two: 1</li>
// <li>three: 2</li>

Iterating objects.

obj = { foo: 'bar', bear: 'four'}
each val, key in obj
  li #{key}: #{val}

// <li>foo: bar</li>
// <li>bear: four</li>

Nested iteration.

donald = {name: 'Donald', roles:['driver', 'duck']}
mickey = {name: 'Mickey', roles:['detective', 'mouse']}
users = [donald, mickey];
each user in users
  each role in user.roles
    li= role

donald = {name: 'Donald', roles:['driver', 'duck']}
mickey = {name: 'Mickey', roles:['detective', 'mouse']}
users = [donald, mickey];
for user in users
  for role in user.roles
    li= role

Conditionals

donald = {name: 'Donald', role:'admin'}
mickey = {name: 'Mickey', role:'detective'}
users = [donald, mickey];
for user in users
  if user.role == 'admin'
    p #{user.name} is an admin
  else
    p= user.name

// IS EQUIVALENT  TO

donald = {name: 'Donald', role:'admin'}
mickey = {name: 'Mickey', role:'detective'}
users = [donald, mickey];
for user in users
  - if (user.role == 'admin')
    p #{user.name} is an admin
  - else
    p= user.name

// Unless
donald = {name: 'Donald', role:'admin'}
mickey = {name: 'Mickey', role:'detective'}
users = [donald, mickey];
for user in users
  unless user.isAnonymous
    p
      | Click to view
      a(href='/users/' + user.id)= user.name

Template inheritance

Done with block and extends.

// Block is a request for child template.
// Here 2 of 3 blocks provide default content.

// layout.jade
html
  head
    h1 My Site - #{title}
    block scripts
      script(src='/jquery.js')
  body
    block content
    block footer
      #footer
        p some footer content

// my_page.jade
extends layout

block scripts
  script(src='/jquery.js')
  script(src='/pets.js')

block content
  h1= title
  each pet in pets
    include pet

Blocks can request more blocks.

// extended_layout.jade
extends layout

block content
  .sidebar
    block sidebar
      p nothing
  .primary
    block primary
      p nothing

Append and prepend.

// layout.jade
// Contains JS that is loaded on every page.
html
  head
    block head
      script(src='/vendor/jquery.js')
      script(src='/vendor/caustic.js')
    body
      block content

// flipflop.jade
extends layout

block append head
  script(src='/vendor/three.js')
  script(src='/game.js')

// flopflip.jade
extends layout

block prepend head
  script(src='/vendor/three.js')
  script(src='/game.js')

Includes

// Directory structure:
// ./layout.jade
// ./includes/head.jade
// ./includes/foot.jade

// layout.jade
html
  include includes/head
  body
    h1 My Site
    p Welcome to my super amazing site.
    include includes/foot

 // Path is relative to filename option passed to Jade.
 // Automatic in Express.

 // If you give include an extension like .html, it won't parse it as Jade.
html
  body
    include content.html

You can use yield to mark where block given to include is placed.

// layout.jade
html
  include head
    script(src='/foo.js')
    script(src='/bar.js')
  body
    h1 test

// head.jade
head
  yield
  script(src='/jquery.js')
  script(src='/jquery.ui.js')

Advanced yielding.

// people.jade
users = [{ name: 'Tobi', occupation: 'Ferret' }]
each user in users
  .user
    include user

// user.jade
h1= user.name
p= user.occupation

// <div class="user">
//   <h1>Tobi</h1>
//   <p>Ferret</p>
// </div>

Mixins

Mixins are like mini templates.

mixin list
  ul
    li foo
    li bar
    li baz

h2 Groceries
mixin list

// <h2>Groceries</h2>
// <ul>
//   <li>foo</li>
//   <li>bar</li>
//   <li>baz</li>
// </ul>

Mixins can have parameters.

mixin pets(pets)
  ul.pets
    - each pet in pets
      li= pet

mixin profile(user)
  .user
    h2= user.name
    mixin pets(user.pets)

jack = {name: 'Jack', pets: ['Don', 'Foo']}
mixin profile(jack)

// <div class="user">
//   <h2>Jack</h2>
//   <ul class="pets">
//     <li>Don</li>
//     <li>Foo</li>
//   </ul>
// </div>

Jade with make

Use to compile pages/*.jade into pages/*.html files by simply make.

JADE = $(shell find pages/*.jade)
HTML = $(JADE:.jade=.html)

all: $(HTML)

%.html: %.jade
    jade < $< --path $< > $@

clean:
    rm -f $(HTML)

.PHONY: clean

Optionally combine with file watchers.

watch make

Source