Skip to content

Instantly share code, notes, and snippets.

@tj
Created January 13, 2012 19:14
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tj/1608186 to your computer and use it in GitHub Desktop.
Save tj/1608186 to your computer and use it in GitHub Desktop.

Stylus

A dynamic, expressive, powerful language compiling to CSS.

SLIDE:

whoami

tj holowaychuk
  • github.com/visionmedia
  • @tjholowaychuk
  • express
  • connect
  • cluster
  • jade
  • stylus
  • node-canvas
  • soda
  • ...

SLIDE:

LearnBoost

SLIDE:

eh?

SLIDE:

Flexible Grammar

button,
a.button {
  padding: 5px 10px;
  color: #888;
}

or

button,
a.button
  padding: 5px 10px
  color: #888

or

button
a.button
  padding 5px 10px
  color #888

SLIDE:

Nesting

form
  padding 10px
  input[type='text']
    padding 5px
    border 1px solid

to:

  form {
    padding: 10px;
  }
  form input[type='text'] {
    padding: 5px;
    border: 1px solid;
  }

SLIDE:

Parent Reference "&"

a
  color: white
  background: black 
  &:hover
    color: black
    background: white

to:

a {
  color: #fff;
  background: #000;
}
a:hover {
  color: #000;
  background: #fff;
}

SLIDE:

Variables

gray = #888
$fonts = helvetica, arial, sans-serif

body
  font 14px/1.5 $fonts
  color gray

to:

body {
  font: 14px/1.5 helvetica, arial, sans-serif;
  color: #888;
}

SLIDE:

Mixins

stripe(even = #eee, odd = #fff)
  background-color: odd
  &.even
  &:nth-child(2n)
    background-color: even

table
  tr
    stripe(#888, #eee)

to:

table tr {
  background-color: #eee;
}
table tr.even,
table tr:nth-child(2n) {
  background-color: #888;
}

SLIDE:

Transparent Mixins

stripe(even = #eee, odd = #fff)
  background-color: odd
  &.even
  &:nth-child(2n)
    background-color: even

table tr
  stripe #888 #eee

to:

table tr {
  background-color: #eee;
}
table tr.even,
table tr:nth-child(2n) {
  background-color: #888;
}

SLIDE:

Arguments

border-radius()     
  -webkit-border-radius arguments
  -moz-border-radius arguments
  border-radius arguments

.dialog
  border-radius 5px 10px

to:

.dialog {
  -webkit-border-radius: 5px 10px;
  -moz-border-radius: 5px 10px;
  border-radius: 5px 10px;
}

SLIDE:

Functions

add(a, b = a)
  return a + b

button
  padding: add(5px)
  padding: add(5px, 10px)

to:

button {
  padding: 10px;
  padding: 15px;
}

SLIDE:

Implicit Returns

add(a, b = a)
  return a + b


add(a, b = a)
  a + b


add(a, b = a){ a + b }


add(a, b = a) {
  return a + b;
}

SLIDE:

Conditionals (if)

box(n, props = margin padding)
  if margin in props
    margin n
  if padding in props
    padding n

.dialog
  box(5px)

.dialog
  box(5px, margin)

to:

.dialog {
  margin: 5px;
  padding: 5px;
}
.dialog {
  margin: 5px;
}

SLIDE:

Conditionals (unless)

prevent-reset = true

reset-table() {
  border-collapse: collapse;
  border-spacing: 0;
}

table
  margin: 15px
  unless prevent-reset
    reset-table()

SLIDE:

Postfix if / unless

box(n, props = margin padding)
  margin n if margin in props
  padding n if padding in props

SLIDE:

Iteration / Interpolation

box(n, props = margin padding)
  for prop in props
    {prop} n

.dialog
  box(5px)

.dialog
  box(5px, foo bar baz)

to:

.dialog {
  margin: 5px;
  padding: 5px;
}
.dialog {
  foo: 5px;
  bar: 5px;
  baz: 5px;
}

SLIDE:

Postfix for

box(n, props = margin padding)
  {prop} n for prop in props


body
  nums = 1 2 3 
  foo: n for n in nums if length(nums) > 2

to:

body {
  foo: 1;
  foo: 2;
  foo: 3;
}

SLIDE:

Keyword Arguments

button(text = #eee, background = #888)
  // implementation ...

button
a.button,
input[type=submit]
  button(white, black)
  button(background: black, text: white)
  button(background: black, white)
  button(black, text: white)

p(button)
// => button(text, background)

SLIDE:

Rest Params

 sum(nums...)
   sum = 0
   sum += n for n in nums

 sum(1,2,3,4)
 // => 10

SLIDE:

Rest Params #2

join(delim, vals...)
  str = ''
  for val, i in vals
    if i
      str += delim + val
    else
      str += val

join(', ', 1, 2, 3)
// => '1, 2, 3'

SLIDE:

Interpolation

vendor(prop, args)
  -webkit-{prop} args
  -moz-{prop} args
  {prop} args

border-radius()
  vendor('border-radius', arguments)

.dialog
  border-radius 5px

to:

.dialog {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
}

SLIDE:

Complex Functions

opposite-position(pos)
  return bottom if pos == top
  return top if pos == bottom  
  return right if pos == left
  return left if pos = right
  error('Invalid position ' + pos)

opposite(positions)
  for pos in positions
    pos = opposite-position(pos)
    ret = ret is defined ? ret pos : pos

opposite(top)
// => bottom

opposite(left)
// => right

opposite(top left)
// => bottom right

SLIDE:

Reflection

reset()
  if mixin == 'root'
    // mixin some selectors
  else if mixin
    // mixin some properties
  else
    // return a value

SLIDE:

Custom Properties (implementation next)

.page-number
  fixed: top right

to:

.page-number {
  position: fixed;
  top: 0;
  right: 0;
}

or

.page-number
  absolute: top 5px right 5px

to:

.page-number {
  absolute: fixed;
  top: 5px;
  right: 5px;
}

SLIDE:

Implementation

fixed()
  pos('fixed', arguments)

absolute()
  pos('absolute', arguments)

pos(pos, args)
  position: pos
  if length(args) == 2
    {args[0]}: 0
    {args[1]}: 0
  else if length(args) == 4
    {args[0]}: args[1]
    {args[2]}: args[3]
  else
    error('invalid arguments. ' + type + ': <pos> [n] <pos> [n];')

SLIDE:

Operators

  []
  ! ~ + -
  is defined
  ** * / %
  + -
  ... ..
  <= >= < >
  in
  == is != is not isnt
  is a
  && and || or
  ?:
  = ?= += -= *= /= %=
  not
  if unless

SLIDE:

Conditional Assignment

size ?= 5px
size ?= 10px

size
// => 5px

size = 5px unless size is defined
size = 10px unless size is defined

size
// => 5px

SLIDE:

Type Checking

#fff is a 'color'
// => true

15px is a 'color'
// => false

'something' is a 'string'
// => true

SLIDE:

Color Operations

 #eee + #f0e
 // => #fef

 #eee - 5
 // => #e9e9e9

 #eee - rgba(200,0,0,0.5)
 // => rgba(38,238,238,0.5)

 #eee - 50%
 //=> #777

SLIDE:

Subscript

dimensions = 50px 100px

dimensions[0]
// => 50px

dimensions[1]
// => 100px

SLIDE:

Subscript Assignment

list = foo bar baz

list[1] = 'bar'
p(list)
// => foo 'bar' baz

list[1..4] = 1
p(list)
// => foo 1 1 1 1

SLIDE:

Is Defined

size is defined
// => false

size = 15px
size is defined
// => true

SLIDE:

Inclusive / Exclusive Range

nums = 1..5
// => 1 2 3 4 5

list = a b c d e f

list[0..2]
// => a b c

list[0...2]
// => a b

SLIDE:

In

props = foo bar baz

bar in props
// => true

rawr in props
// => false

SLIDE:

Type Coercion

#fff / 2
// => #808080

'value: ' + 5px
// => 'number: 5px'


large-font = 30px
small-font = 10px
size = large

size + '-font'
// => 30px

SLIDE:

Unit Conversion

1s + 500ms
// => 1.5s

500ms + 1s
// => 1500ms

10cm - 1in
=> 7.46cm

SLIDE:

Built-in Functions

Over 45 built-in functions.

  • color manipulation
  • image dimensions
  • utilities
  • math

SLIDE:

RGB Components

red(#c00)
// => 204

alpha(#fff)
// => 1

alpha(rgba(0,0,0,0.3))
// => 0.3

SLIDE:

HSL Components

hue(hsla(50deg, 100%, 80%))
// => 50deg

saturation(hsla(50deg, 100%, 80%))
// => 100%

lightness(hsla(50deg, 100%, 80%))
// => 80%

SLIDE:

Lightness Conditions

dark(black)
// => true

dark(#005716)
// => true

dark(white)
// => false


light(black)
// => false

light(white)
// => true

light(#00FF40)
// => true

SLIDE:

Type Checking

  type(12)
  // => 'unit'

  typeof(12)
  // => 'unit'
  
  typeof(#fff)
  // => 'rgba'

  type-of(#fff)
  // => 'rgba'

SLIDE:

Math

abs(-5px)
// => 5px

ceil(5.5in)
// => 6in

floor(5.6px)
// => 5px

round(5.5px)
// => 6px

round(5.4px)
// => 5px

max(1, 5)
// => 5

even(6px)
// => true

sum(1 2 3)
// => 6

avg(1 2 3)
// => 2

SLIDE:

Joining Lists

  join(' ', 1 2 3)
  // => '1 2 3'
  
  join(',', 1 2 3)
  // => '1,2,3'
  
  join(', ', foo bar baz)
  // => 'foo, bar, baz'

  join(', ', foo, bar, baz)
  // => 'foo, bar, baz'

SLIDE:

RGBA

  rgba(255,0,0,0.5)
  // => rgba(255,0,0,0.5)

  rgba(255,0,0,1)
  // => #ff0000

  rgba(#ffcc00, 0.5)
  // rgba(255,204,0,0.5)

SLIDE:

Lightening / Darkening Colors

  lighten(#2c2c2c, 30)
  // => #787878

  lighten(#2c2c2c, 30%)
  // => #393939


  darken(#D62828, 30)
  // => #551010

  darken(#D62828, 30%)
  // => #961c1c

SLIDE:

Saturate / Desaturate Colors

  desaturate(#f00, 40%)
  // => #c33

  saturate(#c33, 40%)
  // => #f00

SLIDE:

Literal Values

  unquote('sans-serif')
  // => sans-serif

  unquote(sans-serif)
  // => sans-serif

  unquote('1px / 2px')
  // => 1px / 2px

SLIDE:

Literal Sprintf

  s('bar()');
  // => bar()

  s('bar(%s)', 'baz');
  // => bar("baz")

  s('bar(%s)', baz);
  // => bar(baz)

  s('X-Crazy-Microsoft-Stuff(%s)', #ffcc00);
  // => X-Crazy-Microsoft-Stuff(#fc0)

  'foo(%s)' % bar
  // => foo(bar)

  'foo(%s, %s)' % (5 10)
  // => foo(5, 10)

SLIDE:

Dynamic Operations

  op = '+'
  operate(op, 15, 5)
  // => 20

SLIDE:

Expression Length

length(1 2 3 4)
// => 4

length(1 2)
// => 2

length(1)
// => 1

length(())
// => 0

length()
// => 0

SLIDE:

Notifications

warn('you shouldnt do that')

error('im broken!')

SLIDE:

Position Opposites

 opposite-position(right)
 // => left

 opposite-position(top left)
 // => bottom right

 opposite-position('top' 'left')
 // => bottom right

SLIDE:

Image Dimensions

width(img)
  return image-size(img)[0]

height(img)
  return image-size(img)[1]

image-size('tux.png')
// => 405px 250px

image-size('tux.png')[0] == width('tux.png')
// => true

SLIDE:

Property Replication

linear-gradient(pos, from, to)
  prop = current-property[0]
  moz = '-moz-linear-gradient(%s, %s 0%, %s 100%)' % (pos from to)
  add-property(prop, moz)
  'linear-gradient(%s, %s 0%, %s 100%)' % (pos from to)

body
  background: linear-gradient(bottom right, white, black)

body
  background-image: linear-gradient(bottom right, white, black)

to:

body {
  background: -moz-linear-gradient(bottom right, #fff 0%, #000 100%);
  background: linear-gradient(bottom right, #fff 0%, #000 100%);
}
body {
  background-image: -moz-linear-gradient(bottom right, #fff 0%, #000 100%);
  background-image: linear-gradient(bottom right, #fff 0%, #000 100%);
}

SLIDE:

REPL

$ stylus -i

> 100 + 50%
=> 150

> list = one two three four five
=> one two three four five

> list[1..2]
=> (two three)

SLIDE:

Connect Middleware

var connect = require('connect')
  , stylus = require('stylus')
  , pub = __dirname + '/public'

connect(
  stylus.middleware({ src: pub, compress: true }),
  connect.static(pub)
).listen(3000);

SLIDE:

stylus(1)

Usage: stylus [options] [command] [< in [> out]]
              [file|dir ...]

Commands:

  help <prop>     Opens help info for <prop> in
                  your default browser. (osx only)

Options:

  -i, --interactive       Start interactive REPL
  -w, --watch             Watch file(s) for changes and re-compile
  -o, --out <dir>         Output to <dir> when passing files
  -C, --css <src> [dest]  Convert css input to stylus
  -I, --include <path>    Add <path> to lookup paths
  -c, --compress          Compress css output
  -d, --compare           Display input along with output
  -V, --version           Display the version of stylus
  -h, --help              Display help information

SLIDE:

Utilizing External Libraries

var stylus = require('stylus')
  , nib = require('nib')
  , fs = require('fs');

fs.readFile('test.styl', 'utf8', function(err, str){
  stylus(str)
    .set('filename', 'test.styl')
    .include(nib.path)
    .render(function(err, css){
      console.log(css);
    });  
});

SLIDE:

Nib

$ npm install nib
  • customizable components (buttons etc)
  • transparent vendor functions (gradients etc)
  • transparent vendor properties (border-radius, opacity, etc)
  • custom properties (fixed, absolute, etc)
  • various mixins (hide-text(), resets, etc)

SLIDE:

components

SLIDE:

opacity

@import 'nib/vendor'

button {
  opacity: .5;
}

to:

button {
  opacity: 0.5;
  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);
}

SLIDE:

border-radius

@import 'nib'

button {
  border-radius: top 5px, bottom 10px;
}

to:

button {
  -moz-border-radius-topleft: 5px;
  -webkit-border-top-left-radius: 5px;
  border-top-left-radius: 5px;
  -moz-border-radius-topright: 5px;
  -webkit-border-top-right-radius: 5px;
  border-top-right-radius: 5px;
  -moz-border-radius-bottomleft: 10px;
  -webkit-border-bottom-left-radius: 10px;
  border-bottom-left-radius: 10px;
  -moz-border-radius-bottomright: 10px;
  -webkit-border-bottom-right-radius: 10px;
  border-bottom-right-radius: 10px;
}

SLIDE:

border-radius #2

@import 'nib'

button {
  border-radius: top left 5px, bottom right 10px;
}

to:

button {
  -moz-border-radius-topleft: 5px;
  -webkit-border-top-left-radius: 5px;
  border-top-left-radius: 5px;
  -moz-border-radius-bottomright: 10px;
  -webkit-border-bottom-right-radius: 10px;
  border-bottom-right-radius: 10px;
}

SLIDE:

fixed / absolute

@import 'nib'

#logo {
  fixed: top left;
}
#feedback {
  absolute: top 10px right 10px
}

to:

#logo {
  position: fixed;
  top: 0;
  left: 0;
}
#feedback {
  position: absolute;
  top: 10px;
  right: 10px;
}

SLIDE:

linear-gradient

@import 'nib/gradients'

button {
  background: linear-gradient(top, white, black);
}

to:

button {
  background: -webkit-gradient(linear, left top, left bottom,
    color-stop(0, #fff),
    color-stop(1, #000));
  background: -moz-linear-gradient(top, #fff 0%, #000 100%);
  background: linear-gradient(top, #fff 0%, #000 100%);
}

SLIDE:

linear-gradient #2

@import 'nib/gradients'

button {
  background: linear-gradient(top, 50% red, green, blue);
}

to:

button {
  background: -webkit-gradient(linear, left top,left bottom,
    color-stop(0.5, #f00),
    color-stop(0.33, #008000),
    color-stop(1, #00f));
  background: -moz-linear-gradient(top, #f00 50%, #008000 33.33%, #00f 100%);
  background: linear-gradient(top, #f00 50%, #008000 33.33%, #00f 100%);
}

SLIDE:

The End

@vesln
Copy link

vesln commented Jan 13, 2012

That's impressive!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment