Created

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist
View stylus.md

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

That's impressive!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.