// 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. // // Put something like this in your styl.styl: // @require 'inc/wfpl/stylus-helpers.styl' // set units to px if it doesn't have a unit already _px(i) unit(i) == '' ? unit(i, px) : i // transparently support vender-specific tags border-radius() -webkit-border-radius: arguments border-radius: arguments box-shadow() -webkit-box-shadow: arguments box-shadow: arguments box-sizing() -webkit-box-sizing: arguments -moz-box-sizing: arguments box-sizing: arguments user-select() -webkit-user-select: arguments -moz-user-select: arguments -ms-user-select: arguments user-select: arguments // map "whitespace:" to "white-space:" whitespace() white-space: arguments // helper function _pos(type, args) position: unquote(type) for i in (0...length(args)) if not args[i] is a 'unit' {args[i]}: args[i + 1] is a 'unit' ? args[i + 1] : 0 // you can pass directions and units, or just directions. Examples: // absolute: top 20px left 0 // fixed: top right // relative: top -4px absolute() _pos('absolute', arguments) fixed() _pos('fixed', arguments) relative() _pos('relative', arguments) // Specify width and height on the same line dimensions(w, h) 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.2) line_height = unit(line_height, em) text-align: justify & > * display: inline-block relative: top line_height &:before content: '' display: block width: 100% margin-bottom: -(line_height) &:after content: '' display: inline-block width: 100% // image layout: left column has normal, right column hover versions // // arguments: width/height are pixel dimensions of a single sprite // // markup: put classes n1, n2, etc on the items. // // Example // // html: // // styl: // nav li // sprites_rollover "images/nav.png" 150 35 2 sprites_rollover(image, width, height, count, v_offset = 0, h_offset = 0) width: unit(width, px) height: unit(height, px) 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) &.n{n} y = - (@height * n) - unit(v_offset, px) background-position: (0 - unit(h_offset, px)) y &:hover background-position: (0 - unit(h_offset, px) - @width) y sprite_rollover(image, width, height, v_offset = 0, h_offset = 0) sprites_rollover(image, width, height, 1, v_offset, h_offset) // see sprites_rollover sprites(image, height, count, v_offset = 0, h_offset = 0) height: unit(height, px) 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) &.n{n} y = - (@height * n) - unit(v_offset, px) background-position: (0 - unit(h_offset, px)) y sprite(image, height, v_offset = 0, h_offset = 0) height: unit(height, px) if image[1] background: url(image[0]) background: url(image[1]), linear-gradient(transparent, transparent); else background-image: url(image) background-repeat: no-repeat; background-position: (0 - unit(h_offset, px)) (0 - unit(v_offset, px)) // Styling for a variable height element with an image background where the // middle repeats vertically. You must split your image into three images, and // name them with "-top", "-mid" and "-bot" suffixes (right before the file // extension.) then pass a filepath as the first argument to this function // which does not have a suffix. // // Example: // // image files: // i/foo-top.png (900x20) // i/foo-mid.png (900xwhatever) // i/foo-bot.png (900x30) // // dom layout:
...
// // Stylus: // // #expando // mangin: 10px auto; // width: 900px; // background_vertical_stretch("i/foo.png", 20, 30) // // Notes: // // You can (optionally) pass 1-4 additional arguments which // (effectively) add padding to the inner most div. The default is to // have the height and width of the inner div match exactly the outer // one. Parameters you specify are relative to that (not the section // filled with the "-mid" image). background_vertical_stretch(image, top_height, bottom_height, top_pad = 0, right_pad = top_pad, bottom_pad = top_pad, left_pad = right_pad) top_height = _px(top_height) bottom_height = _px(bottom_height) top_pad = _px(top_pad) right_pad = _px(right_pad) bottom_pad = _px(bottom_pad) left_pad = _px(left_pad) ext = extname(image) path = pathjoin(dirname(image), basename(image, ext)) // bottom image (outermost block) background: transparent url(path + '-bot' + ext) padding-bottom: bottom_height + 1 // +1 to prevent margin collapsing > * background: transparent url(path + '-top' + ext) 0 0 no-repeat padding-top: top_height + 1 // +1 to prevent margin collapsing > * > * background: transparent url(path + '-mid' + ext) 0 0 repeat-y; margin-top: -1px; // correct for above +1 from top margin-bottom: -1px; // correct for above +1 from bottom padding: 1px; // to prevent margin collapsing > * > * > * // all "2px" below are to correct for 1px above padding and below padding: 1px; margin-top: 2px - top_height + top_pad margin-left: left_pad - 2px margin-right: right_pad - 2px margin-bottom: 2px - bottom_height + bottom_pad background: transparent; li_reset() margin: 0 padding: 0 list-style: none // Example: // input // +placeholder() // color: red placeholder() &::-webkit-input-placeholder {block} &:-moz-placeholder // firefox 4-18 {block} &::-moz-placeholder // firefox 19+ {block} &:-ms-input-placeholder // ie {block} // Example: // div.button // noselect() noselect() -webkit-touch-callout: none -webkit-user-select: none -khtml-user-select: none -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' css_rules[k] = v for k, v in node if k == 'width' width = v if k == 'outer_width' outer_width = v css_rules['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 css_rules if 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 // internal helper _pct_pos(offset, width, sheet_width) if width is sheet_width return 0 ratio = offset / (sheet_width - width) if ratio is 0 return 0 return unit(ratio * 100, '%') // wfpl_spritesheets documentation/example: // // wfpl_spritesheets({ // main: { // image: sha1['images/sprites.min.svg'] // y_origin: 'bottom' // invert y coordinates below (thx inkscape) // w: 200 // width of document (whatever units) // h: 400 // height of document (whatever units) // sprites: { // ".icon": { // y: 399 // top (regardless of y_origin) // w: 48 // width of sprite (in doc coords) // h: 48 // height of sprite (in doc coords) // gap: 2 // space _between_ rows/columns // hover: 'right' // can be 'right' or 'down', meaning: direction to // // move in spritesheet to find the ":hover" graphic // column: 'three' 'four' 'five' 'six' // key can be 'column' or 'row' // // values are postfix for class names // } // ".logo": { // y: 100 // w: 200 // h: 50 // } // } // } // }) // // HTML: //