Skip to content

Instantly share code, notes, and snippets.

@bhalash
Last active August 29, 2015 14:27
Show Gist options
  • Save bhalash/0a2e6cbfba5fd920d1f7 to your computer and use it in GitHub Desktop.
Save bhalash/0a2e6cbfba5fd920d1f7 to your computer and use it in GitHub Desktop.
Arbitrary Scss columns
/**
* Asymmetrical Floated Elements
* -----------------------------------------------------------------------------
* float-of() provides regularly-sized floated columns side by side. This allows
* for an asymmetrical structure. SIZES ARE INCLUSIVE OF MARGIN, EXCEPT IN
* PERCENTAGE COLUMNS.
*
* Seriously -- do NOT use this mixin for regularly-spaced columns. Use
* float-of() if you need regular columns, because the right margin will look
* all fucked. This is *only* for asymmetric columns.
*
* @param string $element Target child element.
* @param list $columns List of columns to produce.
* @param int $percentage Percentage space betwen columns.
* @param string $side Float side.
*/
@mixin asym-float($element: div, $columns: 25% auto, $margin: 2, $side: left) {
@each $col in $columns {
@if not is-percent($col) and not is-keyword($col) {
// Check for appropraite units.
@error 'Supplied units should be either a percentage or \'auto!\'';
}
}
@if list-greater-than($columns, 100) {
// Check for appropriate size.
@error 'Column percentages should total < 100%!';
}
// Determine width of 'auto' columns using <rainbow>mathematics</rainbow>.
$auto-width: asym-col-width($columns, $margin);
$margin: percentage($margin * 0.01);
@for $i from 1 through length($columns) {
@if is-keyword(nth($columns, $i), auto) {
$columns: set-nth($columns, $i, $auto-width);
}
}
// Output CSS begins.
@include clearfix();
> #{$element} {
float: $side;
min-height: 1px;
@for $i from 1 through length($columns) {
&:nth-child(#{$i}) {
@if $i < length($columns) and $margin != 0 {
margin-#{opposite-side($side)}: $margin;
}
width: nth($columns, $i);
/* Ware ye! This can lead to *very* verbose CSS, while making it
* easier to add responsive rules. */
@content;
}
}
}
}
/**
* Asymmetric Float Column Width
* -----------------------------------------------------------------------------
* Return the width of all "auto" size columns in asym-float().
*
* @param list $columns List of columns.
* @param int $margin Column margin.
* @return int $width Percentage width of "auto" columns.
*/
@function asym-col-width($columns, $margin) {
$percent: 0;
$width: 0;
$autos: 0;
@each $col in $columns {
$percent: $percent + value-percent($col);
@if is-keyword($col) {
$autos: $autos + 1;
}
}
@if $autos == 0 {
// Division by zero is bad.
$autos: 1;
}
/* Width for margin is subtracted from auto columns. In short:
* % column total = % width + margin;
* auto column total = % width - margin; */
$margin: $margin * (length($columns) - 1);
$width: ((100 - $percent) / $autos) - ($margin / $autos);
@return $width;
}
/**
* Flexbox Parent
* -----------------------------------------------------------------------------
* If you have come this far you should have an understanding of how, why and
* where you would use flexbox. These mixins generate unprefixed CSS flexbox
* rules, with a fallback generated for IE8 for use through Flexie.
*
* Two safe defaults are assumes for flex-direction and flex, but otherwise I
* leave browser defaults be.
*
* @param string $direction Direction of flexbox. Column or row.
* @param string $justify-content Alignment of content along main axis.
* @param string $wrap Whether or not to wrap content.
* @param string $align-items Alignment of content along secondary axis.
* @link https://css-tricks.com/snippets/css/a-guide-to-flexbox/
* @link http://zomigi.com/blog/flexbox-syntax-for-ie-10/
* @link http://flexiejs.com/
*/
@mixin flex-parent($direction: row, $justify: null, $wrap: null, $align: null) {
display: flex;
// No 2009 equivalent.
flex-direction: $direction;
@if $justify {
justify-content: $justify;
}
@if $wrap {
flex-wrap: $wrap;
}
@if $align {
align-items: $align;
}
}
/**
* Flexbox Child
* -----------------------------------------------------------------------------
* @param list $flex Default flex arguments.
* @param string $align-self Self-alignment position.
* @param int $order Order for self in flex parent.
*/
@mixin flex-child($flex: 1, $align: null, $order: null) {
flex: $flex;
@if $align {
align-self: $align;
}
@if $order {
order: $order;
}
}
/**
* Flexbox Column Width
* -----------------------------------------------------------------------------
* Separated because I use it for fallback rules.
*
* @param int $columns Number of columns.
* @param int $margin Percentage space between columns.
* @return int Calculated column width.
*/
@function column-width($columns, $margin) {
@return (100 - ($columns - 1) * $margin) / $columns;
}
/**
* Asymmetric Float Column Width
* -----------------------------------------------------------------------------
* Return the width of all "auto" size columns in asym-float().
*
* @param list $columns List of columns.
* @param int $margin Column margin.
* @return int $width Percentage width of "auto" columns.
*/
@function asym-col-width($columns, $margin) {
$percent: 0;
$width: 0;
$autos: 0;
@each $col in $columns {
$percent: $percent + value-percent($col);
@if is-keyword($col) {
$autos: $autos + 1;
}
}
@if $autos == 0 {
// Division by zero is bad.
$autos: 1;
}
/* Width for margin is subtracted from auto columns. In short:
* % column total = % width + margin;
* auto column total = % width - margin; */
$margin: $margin * (length($columns) - 1);
$width: ((100 - $percent) / $autos) - ($margin / $autos);
@return $width;
}
/**
* Get Opposite Float Side
* -----------------------------------------------------------------------------
* Used in several places and surprisingly useful.
*
* @param string $side Float side;
* @return string $opposite Inverse of floated side;
*/
@function opposite-side($side: left) {
$opposite: right;
@if $side == right {
$opposite: left;
}
@return $opposite;
}
/**
* Flexbox Columns
* -----------------------------------------------------------------------------
* @param string $element Target element element.
* @param int $count Number of columns to output.
* @param int $percentage Percentage space betwen columns.
*/
@mixin column-of($element: div, $columns: 3, $margin: 2) {
$width: percentage(column-width($columns, $margin) * 0.01);
@include flex-parent(row, space-between, wrap);
> #{$element} {
min-height: 1px;
@include flex-child(0 1 $width);
@content;
}
}
/**
* Float Columns
* -----------------------------------------------------------------------------
* @param string $element Target element element.
* @param int $count Number of columns to output.
* @param int $percentage Percentage space betwen columns.
* @param string $side Float side.
*/
@mixin float-of($element: div, $columns: 3, $margin: 2, $side: left) {
$width: percentage(column-width($columns, $margin) * 0.01);
@include clearfix();
> #{$element} {
float: $side;
min-height: 1px;
width: $width;
&:not(:nth-child(#{$columns + 'n'})) {
margin-#{opposite-side($side)}: percentage($margin * 0.01);
}
@content;
}
}
/**
* Evaluate Whether Number is Column
* -----------------------------------------------------------------------------
* If number is a percentage, return number, else return number 0.
*
* @param int $value Number to test.
* @return int Number/0.
*/
@function value-percent($value) {
@return if(is-percent($value), $value, 0);
}
/**
* Total Percentages in List
* -----------------------------------------------------------------------------
* The list for asymmetric float columns has a mix of percentage and "auto"
* sized columns. This tallies the percentage columns and returns whether they
* exceed 100%.
*/
@function list-greater-than($columns, $target: 100) {
$percent: 0;
@each $col in $columns {
$percent: $percent + value-percent($col);
}
@return ($percent > $target);
}
/**
* Asymmetrical Floated Elements
* -----------------------------------------------------------------------------
* float-of() provides regularly-sized floated columns side by side. This allows
* for an asymmetrical structure. SIZES ARE INCLUSIVE OF MARGIN, EXCEPT IN
* PERCENTAGE COLUMNS.
*
* Seriously -- do NOT use this mixin for regularly-spaced columns. Use
* float-of() if you need regular columns, because the right margin will look
* all fucked. This is *only* for asymmetric columns.
*
* @param string $element Target child element.
* @param list $columns List of columns to produce.
* @param int $percentage Percentage space betwen columns.
* @param string $side Float side.
*/
@mixin asym-float($element: div, $columns: 25% auto, $margin: 2, $side: left) {
@each $col in $columns {
@if not is-percent($col) and not is-keyword($col) {
// Check for appropraite units.
@error 'Supplied units should be either a percentage or \'auto!\'';
}
}
@if list-greater-than($columns, 100) {
// Check for appropriate size.
@error 'Column percentages should total < 100%!';
}
// Determine width of 'auto' columns using <rainbow>mathematics</rainbow>.
$auto-width: asym-col-width($columns, $margin);
$margin: percentage($margin * 0.01);
@for $i from 1 through length($columns) {
@if is-keyword(nth($columns, $i), auto) {
$columns: set-nth($columns, $i, $auto-width);
}
}
// Output CSS begins.
@include clearfix();
> #{$element} {
float: $side;
min-height: 1px;
@for $i from 1 through length($columns) {
&:nth-child(#{$i}) {
@if $i < length($columns) and $margin != 0 {
margin-#{opposite-side($side)}: $margin;
}
width: nth($columns, $i);
/* Ware ye! This can lead to *very* verbose CSS, while making it
* easier to add responsive rules. */
@content;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment