-// Copyright 2014 Jason Woofenden -- Public Domain (CC0)
+// This program is in the public domain within the United States. Additionally,
+// we waive copyright and related rights in the work worldwide through the CC0
+// 1.0 Universal public domain dedication, which can be found at
+// http://creativecommons.org/publicdomain/zero/1.0/
// This file contains helpers for using stylus in your project.
//
width: _px(w)
height: _px(h)
+// generate (return) a "calc(Fpx + R%)" to scale linearly with parent
+// args:
+// pb: pixel width of parent (when biggest)
+// ps: pixel width of parent (when smallest)
+// cb: pixel width of child (when biggest)
+// cs: pixel width of child (when smallest)
+linear_scale_calc(pb, ps, cb, cs)
+ // math explained: (figuring out F and R in calc(Fpx + R%))
+ // f + r * pb = cb // known_1: formula(pb) -> cb
+ // f = cb - r * pb // solve_for_f: subtract (r * pb) from both sides
+ // f + r * ps = cs // known_2: formula(pb) -> cb
+ // cb - r * pb + r * ps = cs // substitute solved_for_f into known_2
+ // -r * pb + r * ps = cs - cb // subtract cb from both sides
+ // r * (-pb + ps) = cs - cb // factor out r from left side
+ // r = (cs - cb) / (-pb + ps) // divide both sides by (-pb + ps)
+ // r = (cb - cs) / (pb - ps) // multiply left side by -1/-1
+ r = (cb - cs) / (pb - ps)
+ f = cb - r * pb
+ unquote("calc(" + unit(f, "px") + ' + ' + unit(100 * r, "%") + ")")
+
// Make children of this element inline-blocks that are spaced evenly accross.
//
// To create a minimum distance between: don't use word-spacing, it's broken in
// firefox. Instead, try padding on the children and negative margin on the
// parent.
-space_evenly(line_height = 1.2em)
+space_evenly(line_height = 1.2)
+ line_height = unit(line_height, em)
text-align: justify
& > *
display: inline-block
sprites_rollover(image, width, height, count, v_offset = 0, h_offset = 0)
width: unit(width, px)
height: unit(height, px)
- background-image: url(image)
+ if image[1]
+ background: url(image[0])
+ background: url(image[1]), linear-gradient(transparent, transparent);
+ else
+ background-image: url(image)
background-position: top left
background-repeat: no-repeat;
for n in (0...count)
// see sprites_rollover
sprites(image, height, count, v_offset = 0, h_offset = 0)
height: unit(height, px)
- background-image: url(image)
+ if image[1]
+ background: url(image[0])
+ background: url(image[1]), linear-gradient(transparent, transparent);
+ else
+ background-image: url(image)
background-position: top left
background-repeat: no-repeat;
for n in (0...count)
-moz-user-select: none
-ms-user-select: none
user-select: none
+
+// Do all the crazy math to get columns to fit properly
+// and (optionally) scale for responsive designs.
+//
+// example:
+//
+// columns = wfpl_columns({
+// type: 'node',
+// name: 'centerer',
+// margin: 15px,
+// width: 940px,
+// child: {
+// type: 'node',
+// name: 'middler',
+// border-width: 1px,
+// border-style: solid,
+// border-color: black,
+// padding-left: 30px,
+// padding-right: 18px,
+// child: {
+// type: 'alternatives',
+// full: {
+// type: 'series',
+// nav: {
+// type: 'node'
+// width: 219px
+// float: left
+// },
+// main: {
+// type: 'node'
+// margin-left: 48px,
+// width: 623px
+// float: left
+// }
+// },
+// with_sidebar: {
+// type: 'series',
+// nav: {
+// type: 'node',
+// width: 219px
+// float: left
+// },
+// main: {
+// type: 'node',
+// margin-left: 48px,
+// width: 343px
+// float: left
+// },
+// sidebar: {
+// type: 'alternatives',
+// plain: {
+// type: 'node'
+// margin-left: 30px,
+// width: 250px
+// float: left
+// },
+// bordered: {
+// type: 'node'
+// margin-left: 30px,
+// border-width: 1px,
+// border-style: solid
+// border-color: red
+// padding: 15px,
+// width: 218px
+// float: left
+// }
+// }
+// }
+// }
+// }
+// })
+// output css from column calculations
+// for selector, css in columns.css
+// body > {selector}
+// {css}
+// @media screen and (max-width: (columns.width))
+// // output responsive css from column calculations
+// for selector, css in columns.responsive_css
+// body > {selector}
+// {css}
+//
+// // as big as it can be
+// body > .centerer
+// width: auto
+//
+// // make sure that borders (which won't scale) and rounding errors don't
+// // break the layout
+// body > .centerer.full > .main,
+// body > .centerer.with_sidebar > .sidebar.plain,
+// body > .centerer.with_sidebar > .sidebar.bordered
+// margin-right: -10px
+//
+// // switch to 1-column layout
+// @media screen and (max-width: (single_column_max))
+// for selector, css in columns.css
+// body > {selector}
+// if selector == '.centerer'
+// margin-top: 0
+// else if selector in hide_in_one_column_mode
+// display: none
+// else
+// border: none
+// float: none
+// width: auto
+// margin: 0
+// padding: 0
+// margin-top: columns['responsive_css']['.centerer']['margin']
+wfpl_columns_helper(top, node, selector, parent_width, expected_width)
+ css_rules = {}
+ responsive_css_rules = {}
+ if node.type == 'node'
+ left_width = 0px
+ right_width = 0px
+ width = null // inner
+ outer_width = null
+ if node.name
+ if selector == ''
+ selector = '.' + node.name
+ else
+ selector += ' > .' + node.name
+ // callculate width and outer_width
+ for k, v in node
+ if match('^(margin|padding|border-width)$', k)
+ left_width += v
+ right_width += v
+ if match('^((margin|padding)-left)|(border-left-width)$', k)
+ left_width += v
+ if match('^((margin|padding)-right)|(border-right-width)$', k)
+ right_width += v
+ if k != 'type' && k != 'child' && k != 'name' && k != 'outer-width' && k != 'outer_width'
+ css_rules[k] = v
+ for k, v in node
+ if k == 'width'
+ width = v
+ outer_width = left_width + v + right_width
+ if k == 'outer-width' && k == 'outer_width'
+ outer_width = k
+ width = v - left_width - right_width
+ if (!width) && (!outer_width)
+ if parent_width
+ outer_width = parent_width
+ else
+ error("Couldn't figure out width of " + selector);
+ if outer_width && !width
+ width = outer_width - left_width - right_width
+ if width && !outer_width
+ outer_width = width + left_width + right_width
+ unless parent_width // should only happen at top level
+ parent_width = outer_width
+ top['css'][selector] = css_rules
+ for k, v in node
+ if k != 'outer-width' && k != 'outer_width' && k != 'border-width' && k != 'border-left-width' && k != 'border-right-width'
+ if typeof(v) == 'unit' && unit(v) == 'px'
+ responsive_css_rules[k] = floor(unit((v / parent_width) * 100, '%'), 4)
+ top['responsive_css'][selector] = responsive_css_rules
+ if 'child' in node
+ child_width = wfpl_columns_helper(top, node.child, selector, width, width)
+ _columns_recurser(rargs)
+ if child_width != width
+ error(selector + " is the is wrong width. Expected: " + width + ", got: " + rargs.ret.w)
+ top['widths'][selector] = width
+ return outer_width
+ if node.type == 'alternatives'
+ for name, child in node
+ if name != 'type'
+ child_selector = selector + '.' + name
+ child_width = wfpl_columns_helper(top, child, child_selector, parent_width, expected_width)
+ if expected_width
+ if child_width != expected_width
+ error(child_selector + " is wrong width. Expected: " + expected_width + ", got: " + child_width)
+ else
+ expected_width = child_width
+ unless parent_width
+ parent_width = child_width
+ return expected_width
+ if node.type == 'series'
+ series_width = 0px
+ for name, child in node
+ if name != 'type'
+ if selector == ''
+ child_selector = '.' + name
+ else
+ child_selector = selector + ' > .' + name
+ child_width = wfpl_columns_helper(top, child, child_selector, parent_width, null)
+ series_width += child_width
+ if expected_width
+ if series_width != expected_width
+ error(selector + " series total wrong. expected: " + expected_width + ", got: " + series_width);
+ return series_width
+ return // never reached with valid data
+
+wfpl_columns(hash)
+ top = { // contains settings, and is returned from porcelain
+ responsive: responsive
+ width: null
+ css: {}
+ responsive_css: {}
+ widths: {}
+ }
+ top.width = wfpl_columns_helper(top, hash, '', null, null)
+ return top