1 // This program is in the public domain within the United States. Additionally,
2 // we waive copyright and related rights in the work worldwide through the CC0
3 // 1.0 Universal public domain dedication, which can be found at
4 // http://creativecommons.org/publicdomain/zero/1.0/
6 // This file contains helpers for using stylus in your project.
8 // Put something like this in your styl.styl:
9 // @require 'inc/wfpl/stylus-helpers.styl'
12 // set units to px if it doesn't have a unit already
14 unit(i) == '' ? unit(i, px) : i
16 // transparently support vender-specific tags
18 -webkit-border-radius: arguments
19 border-radius: arguments
21 -webkit-box-shadow: arguments
24 -webkit-box-sizing: arguments
25 -moz-box-sizing: arguments
28 -webkit-user-select: arguments
29 -moz-user-select: arguments
30 -ms-user-select: arguments
31 user-select: arguments
33 // map "whitespace:" to "white-space:"
35 white-space: arguments
39 position: unquote(type)
40 for i in (0...length(args))
41 if not args[i] is a 'unit'
42 {args[i]}: args[i + 1] is a 'unit' ? args[i + 1] : 0
44 // you can pass directions and units, or just directions. Examples:
45 // absolute: top 20px left 0
49 _pos('absolute', arguments)
51 _pos('fixed', arguments)
53 _pos('relative', arguments)
55 // Specify width and height on the same line
60 // generate (return) a "calc(Fpx + R%)" to scale linearly with parent
62 // pb: pixel width of parent (when biggest)
63 // ps: pixel width of parent (when smallest)
64 // cb: pixel width of child (when biggest)
65 // cs: pixel width of child (when smallest)
66 linear_scale_calc(pb, ps, cb, cs)
67 // math explained: (figuring out F and R in calc(Fpx + R%))
68 // f + r * pb = cb // known_1: formula(pb) -> cb
69 // f = cb - r * pb // solve_for_f: subtract (r * pb) from both sides
70 // f + r * ps = cs // known_2: formula(pb) -> cb
71 // cb - r * pb + r * ps = cs // substitute solved_for_f into known_2
72 // -r * pb + r * ps = cs - cb // subtract cb from both sides
73 // r * (-pb + ps) = cs - cb // factor out r from left side
74 // r = (cs - cb) / (-pb + ps) // divide both sides by (-pb + ps)
75 // r = (cb - cs) / (pb - ps) // multiply left side by -1/-1
76 r = (cb - cs) / (pb - ps)
78 unquote("calc(" + unit(f, "px") + ' + ' + unit(100 * r, "%") + ")")
80 // Make children of this element inline-blocks that are spaced evenly accross.
82 // To create a minimum distance between: don't use word-spacing, it's broken in
83 // firefox. Instead, try padding on the children and negative margin on the
85 space_evenly(line_height = 1.2)
86 line_height = unit(line_height, em)
90 relative: top line_height
95 margin-bottom: -(line_height)
101 // image layout: left column has normal, right column hover versions
103 // arguments: width/height are pixel dimensions of a single sprite
105 // markup: put classes n1, n2, etc on the items.
112 // <li class="n0">home</li>
113 // <li class="n1">contact</li>
118 // sprites_rollover "images/nav.png" 150 35 2
119 sprites_rollover(image, width, height, count, v_offset = 0, h_offset = 0)
120 width: unit(width, px)
121 height: unit(height, px)
123 background: url(image[0])
124 background: url(image[1]), linear-gradient(transparent, transparent);
126 background-image: url(image)
127 background-position: top left
128 background-repeat: no-repeat;
131 y = - (@height * n) - unit(v_offset, px)
132 background-position: (0 - unit(h_offset, px)) y
134 background-position: (0 - unit(h_offset, px) - @width) y
135 sprite_rollover(image, width, height, v_offset = 0, h_offset = 0)
136 sprites_rollover(image, width, height, 1, v_offset, h_offset)
138 // see sprites_rollover
139 sprites(image, height, count, v_offset = 0, h_offset = 0)
140 height: unit(height, px)
142 background: url(image[0])
143 background: url(image[1]), linear-gradient(transparent, transparent);
145 background-image: url(image)
146 background-position: top left
147 background-repeat: no-repeat;
150 y = - (@height * n) - unit(v_offset, px)
151 background-position: (0 - unit(h_offset, px)) y
152 sprite(image, height, v_offset = 0, h_offset = 0)
153 height: unit(height, px)
155 background: url(image[0])
156 background: url(image[1]), linear-gradient(transparent, transparent);
158 background-image: url(image)
159 background-repeat: no-repeat;
160 background-position: (0 - unit(h_offset, px)) (0 - unit(v_offset, px))
162 // Styling for a variable height element with an image background where the
163 // middle repeats vertically. You must split your image into three images, and
164 // name them with "-top", "-mid" and "-bot" suffixes (right before the file
165 // extension.) then pass a filepath as the first argument to this function
166 // which does not have a suffix.
171 // i/foo-top.png (900x20)
172 // i/foo-mid.png (900xwhatever)
173 // i/foo-bot.png (900x30)
175 // dom layout: <div id="expando"><div><div><div>...</div></div></div></div>
180 // mangin: 10px auto;
182 // background_vertical_stretch("i/foo.png", 20, 30)
186 // You can (optionally) pass 1-4 additional arguments which
187 // (effectively) add padding to the inner most div. The default is to
188 // have the height and width of the inner div match exactly the outer
189 // one. Parameters you specify are relative to that (not the section
190 // filled with the "-mid" image).
191 background_vertical_stretch(image, top_height, bottom_height, top_pad = 0, right_pad = top_pad, bottom_pad = top_pad, left_pad = right_pad)
192 top_height = _px(top_height)
193 bottom_height = _px(bottom_height)
194 top_pad = _px(top_pad)
195 right_pad = _px(right_pad)
196 bottom_pad = _px(bottom_pad)
197 left_pad = _px(left_pad)
199 path = pathjoin(dirname(image), basename(image, ext))
201 // bottom image (outermost block)
202 background: transparent url(path + '-bot' + ext)
203 padding-bottom: bottom_height + 1 // +1 to prevent margin collapsing
205 background: transparent url(path + '-top' + ext) 0 0 no-repeat
206 padding-top: top_height + 1 // +1 to prevent margin collapsing
208 background: transparent url(path + '-mid' + ext) 0 0 repeat-y;
209 margin-top: -1px; // correct for above +1 from top
210 margin-bottom: -1px; // correct for above +1 from bottom
211 padding: 1px; // to prevent margin collapsing
213 // all "2px" below are to correct for 1px above padding and below
215 margin-top: 2px - top_height + top_pad
216 margin-left: left_pad - 2px
217 margin-right: right_pad - 2px
218 margin-bottom: 2px - bottom_height + bottom_pad
219 background: transparent;
231 &::-webkit-input-placeholder
233 &:-moz-placeholder // firefox 4-18
235 &::-moz-placeholder // firefox 19+
237 &:-ms-input-placeholder // ie
244 -webkit-touch-callout: none
245 -webkit-user-select: none
246 -khtml-user-select: none
247 -moz-user-select: none
248 -ms-user-select: none
251 // Do all the crazy math to get columns to fit properly
252 // and (optionally) scale for responsive designs.
256 // columns = wfpl_columns({
264 // border-width: 1px,
265 // border-style: solid,
266 // border-color: black,
267 // padding-left: 30px,
268 // padding-right: 18px,
270 // type: 'alternatives',
280 // margin-left: 48px,
294 // margin-left: 48px,
299 // type: 'alternatives',
302 // margin-left: 30px,
308 // margin-left: 30px,
309 // border-width: 1px,
310 // border-style: solid
321 // output css from column calculations
322 // for selector, css in columns.css
325 // @media screen and (max-width: (columns.width))
326 // // output responsive css from column calculations
327 // for selector, css in columns.responsive_css
331 // // as big as it can be
335 // // make sure that borders (which won't scale) and rounding errors don't
336 // // break the layout
337 // body > .centerer.full > .main,
338 // body > .centerer.with_sidebar > .sidebar.plain,
339 // body > .centerer.with_sidebar > .sidebar.bordered
340 // margin-right: -10px
342 // // switch to 1-column layout
343 // @media screen and (max-width: (single_column_max))
344 // for selector, css in columns.css
346 // if selector == '.centerer'
348 // else if selector in hide_in_one_column_mode
356 // margin-top: columns['responsive_css']['.centerer']['margin']
357 wfpl_columns_helper(top, node, selector, parent_width, expected_width)
359 responsive_css_rules = {}
360 if node.type == 'node'
363 width = null // inner
367 selector = '.' + node.name
369 selector += ' > .' + node.name
370 // callculate width and outer_width
372 if match('^(margin|padding|border-width)$', k)
375 if match('^((margin|padding)-left)|(border-left-width)$', k)
377 if match('^((margin|padding)-right)|(border-right-width)$', k)
379 if k != 'type' && k != 'child' && k != 'name' && k != 'outer_width'
384 if k == 'outer_width'
386 css_rules['width'] = v - left_width - right_width
387 if (!width) && (!outer_width)
389 outer_width = parent_width
391 error("Couldn't figure out width of " + selector);
392 if outer_width && !width
393 width = outer_width - left_width - right_width
394 if width && !outer_width
395 outer_width = width + left_width + right_width
396 unless parent_width // should only happen at top level
397 parent_width = outer_width
398 top['css'][selector] = css_rules
399 for k, v in css_rules
400 if k != 'border-width' && k != 'border-left-width' && k != 'border-right-width'
401 if typeof(v) == 'unit' && unit(v) == 'px'
402 responsive_css_rules[k] = floor(unit((v / parent_width) * 100, '%'), 4)
403 top['responsive_css'][selector] = responsive_css_rules
405 child_width = wfpl_columns_helper(top, node.child, selector, width, width)
406 _columns_recurser(rargs)
407 if child_width != width
408 error(selector + " is the is wrong width. Expected: " + width + ", got: " + rargs.ret.w)
409 top['widths'][selector] = width
411 if node.type == 'alternatives'
412 for name, child in node
414 child_selector = selector + '.' + name
415 child_width = wfpl_columns_helper(top, child, child_selector, parent_width, expected_width)
417 if child_width != expected_width
418 error(child_selector + " is wrong width. Expected: " + expected_width + ", got: " + child_width)
420 expected_width = child_width
422 parent_width = child_width
423 return expected_width
424 if node.type == 'series'
426 for name, child in node
429 child_selector = '.' + name
431 child_selector = selector + ' > .' + name
432 child_width = wfpl_columns_helper(top, child, child_selector, parent_width, null)
433 series_width += child_width
435 if series_width != expected_width
436 error(selector + " series total wrong. expected: " + expected_width + ", got: " + series_width);
438 return // never reached with valid data
441 top = { // contains settings, and is returned from porcelain
442 responsive: responsive
448 top.width = wfpl_columns_helper(top, hash, '', null, null)