Golden ratio grid system in Sass

Posted by Razvan Spatariu

One of the most exciting and fun features that we implemented for our styling library was the grid system.

Our design team came up with the idea of using a golden ratio grid system, where they provide the proportions for the columns and we transform them into HTML/CSS columns.

What is the golden ratio?

For those who don’t know what the golden ratio is, the definition given by LiveScience is:

The Golden ratio is a special number found by dividing a line into two parts so that the longer part divided by the smaller part is also equal to the whole length divided by the longer part.

The result of that is a constant (represented by the greek letter phi — φ):

Golden Ratio

where φ = a+b/a = a/b = 1.6180…

The Greek mathematicians started investigating what we know today as the golden ratio about 2,400 years ago due to its frequent appearances in geometry. Since then, this magic number has fascinated many great minds throughout history (Euclid, Pythagoras, Fibonacci, etc).

One of the most interesting discoveries about the golden ratio is the fact that it can be found in the most diversified and surprising domains of the nature, beginning with the simple flower petals, shells, up to the human body and… galaxies. You can read more about the occurences of the golden ratio in nature by clicking this link

Although there isn’t a definitive proof that this ratio is perceived as “natural” by our brains, the usage of golden ratio emerged in various fields like:

Lately, we have started seeing this “magic ratio” emerging more and more in UI design, the most common use being the golden ratio font sizes, but also as a grid system, like the one we have built for Funding Circle’s website.

Golden ratio grid system

Requirements

Before implementing the new styling library we used a 3rd party solution for the grid system which served the purpose but also had some drawbacks:

  • you had to use an admin panel to generate the CSS/Sass so it interrupted the workflow
  • it exported a lot of CSS — every usage resulted in repeated code
  • it was class-name dependent

Therefore, we wanted to build a custom grid system for Funding Circle which would be:

  • easy to use
  • optimised for our problem
  • applicable to a parent element, so that the columns do not require specific classes
  • easy to maintain or change into a different grid system in the future

Geometry and formula

So, before diving into writing the Sass for this, we first needed to understand the math/geometry behind this.

This is an example of a golden spiral:

Golden Spiral

If we stretch that spiral into a line, the result can be translated into our grid system:

Columns Ratio

And the math would be:

c1 / c0 = φ c2 / c1 = φ …………………………… cn / cn-1 = φ

By using the pattern above and always replacing the cn with cn-1 × φ, we end up with the following formula:

cn = c0 × φn, where: c = column, n = column proportion relative to the first column φ = golden ratio (1,618)

Since we don’t have a defining scale for this and because everything will be turned into percentages anyway, we can arbitrarily set c0 to a convenient value, like 1, so we can simplify the formula further:

cn = φn

Design files / Practical example

The design team provides the column widths by giving us proportions that we then transform into percentages and apply to the columns.

Example: Columns Ratio

We know that the sum of the two columns must be 100% (the full width of the container).

c2 + c1 = 100 c2 = φ2 c1 = φ1 (keep in mind that the subscript indicates the proportion, not the column number)

Using a cross-multiplication, we can say that:

c2 + c1 ……… 100% c2 ………………………… x
x = c2 × 100 / c2 + c1 = φ2 × 100 / φ2 + φ1 = 1.61802 × 100 / 1.61802 + 1.6181 = 61.8029%

So, in this case the width for the column with the proportion 2 is 61.8029%.

As you can see, the width of one column is influenced by the others, so a column proportion will have different widths depending on the siblings’ proportions.

Sass implementation

After figuring out the math behind this, we can finally get to the implementation of the grid system.

Step 1

Calculate cn, where n = [1, 7] (the design team decided that the largest proportion for a column can be 7)

We store all the values of cn in an array that we can later access when we need those values.

// Calculate the general aspect ratio starting from an arbitrary number (1)
$ratio: 1.618;
$colRatio: 1;

@for $i from 1 through 7 {
  $colListLast: nth($colRatio, length($colRatio));
  $colRatio: append($colRatio, $colListLast * $ratio);
}

Step 2

We construct a function that can take the proportions for the columns as an array and returns another array with the percentages for each column, in the same order.

Example:

// input
@warn colsWidth(3 6 1);

// output
(17.80062 75.39987 6.79952) /* their sum is 100 */

Sass function:

@function colsWidth($cols: false) {
  $colsWidth: ();

  @if type-of($cols) == list {

    // read the list and calculate the percentages
    // retrieve the ratio corresponding to each column and add them together
    // this will give the third value (customized total) before applying the cross-multiplication to each value
    $colSum: 0;
    @each $colVal in $cols {
      $colSum: $colSum + nth($colRatio, $colVal);
    }

    // apply a cross-multiplication to find out the width for each column
    // so far we have:
    // - we know that all columns added together are 100%
    // - the corresponding value for 100% is $colSum
    // - we have the proportion for each column width
    // we only need to apply: columns width = proportion * 100 / $colSum
    $colsWidth: ();
    @each $colVal in $cols {
      $colsWidth: append($colsWidth, nth($colRatio, $colVal) * 100 / $colSum);
    }
  } @else {
    $colsWidth: false;
  }

  @return $colsWidth;
}

Step 3

After dealing with the math, all we need to do is to use this in a mixin.

The mixin will be applied to the wrapper of the columns and will return the width for each column using nth-child (we polyfilled this for IE8).

For the columns, we decided not to use the classical float (dealing with a lot of math can lead to trouble when rounding the widths) or flex (because of it’s weak browser support). Instead, we went for a more robust way of doing things although avoided by so many because of it’s quirky past: table layout.

What year is it

Before saying “Hey, hey, hey, tables, really?”, I should add that we don’t use the HTML <table> element, we just set display: table; to the wrapper and display: table-cell; to the siblings.

The pros of using this over float being:

  • you don’t get the bug where one columns falls below the others if their width exceeds 100%
  • you can use vertical-align
  • you can create equal columns without actually knowing the number of columns (by using the width: 1% hack on the columns)

That being said, we add the display: table on the wrapper and then we iterate through the result of the array outputed by the function in step 2 and apply those values to the corresponding direct children.

@mixin columnWrapper($cols: false) {

// table general stuff
width: 100%;
display: table;

// calculate the percentages
$cols: colsWidth($cols);

  @for $i from 1 through length($cols) {
    & *:nth-child(#{$i}) {
      display: table-cell;
      width: nth($cols, $i) * 1%;
    }
  }
}

We can then use this mixin like this:

.wrapper {
  @include columnWrapper(5 2 4);
}

and the output will be:

.wrapper { display: table; }

.wrapper > * { display: table-cell; }

.wrapper > *:nth-child(1) { width: 53.93366%; }
.wrapper > *:nth-child(2) { width: 12.73281%; }
.wrapper > *:nth-child(3) { width: 33.33353%; }

You can check the column mixin in action in this CodePen.

Production

The full mixin for our grid system is far more complex than just calculating the width of the columns.

It also has support for:

  • Responsive - between certain breakpoints it can:
    • reset the table layout to a full-width block layout
    • hide the columns
  • Rows — We can introduce another level of HTML and have support for display: table-row
  • Vertical-align
  • Custom paddings for the columns

If you want to check the full code that we use, you can find it in our styling library.