blog

This changes everything! CSS ‘Fluid Properties’

by Stephen Rose
28 Jan 2016
Share this article:
This changes everything! CSS ‘Fluid Properties’

The term “Responsive Web Design” (RWD) has been around since 2010.
I think we are ready for a new level of responsive fluidity which I call "Fluid Properties".

The problem

The current way that responsive websites are built is through the media queries. CSS preprocessor languages (LESS and SCSS) have simplified our code – grouping the media queries with the element we’re styling.

The problem with this approach, is we only update sizes at arbitrary intervals, also known as "breakpoints". We create media queries from small to large screens. We sometimes target popular device sizes along the way like 768, 1024, and 1366.

What would be great if there is a way in pure CSS to say:
 

“Set the H1 font size to 50 pixels when viewed at 320 pixels.
Then fluidly increase the value up to 100 pixels at 1920 pixels”

 

A basic solution? – Viewport Units

One basic solution to this is problem is Viewport Units. With Viewport Units, a CSS property such as font-size or height can fluidly resize based on a percentage of the window width.

 

h1{
  font-size:15.625vw; //Will equal 30 pixels on a 320 screen
}


The h1 will always be 15.625% of the browser width, so would be 300 pixels at 1920. The element needs to be a fixed proportion to the viewport width – which dramatically limits creative direction.

We can do much better!

The best solution! – Fluid Properties

The solution involves a few pieces, that are all coming of age, all working together:

  The fp() “Fluid Property” SCSS mixin

@mixin fp($property, $min, $max, $start: 320, $end: 1920, $clip: true, $clipAtStart: true, $clipAtEnd: true) {
  $multiplier: ($max - $min) / ($end - $start) * 100;
  $adder: ($min * $end - $max * $start) / ($end - $start);
  $formula: calc(#{$multiplier + 0vw} + #{$adder + 0px});
  @if $clip and $clipAtStart {
    @media (max-width: #{$start + 0px}) {
      #{$property}: $min + 0px;
    }
  }
  @if $clip and $clipAtEnd {
    @media (min-width: #{$end + 0px}) {
      #{$property}: $max + 0px;
    }
  }
  #{$property}: $formula;
}
An example @include:

 

h1 {
  @include fp(font-size, 50, 100); //50px at 320, 100px at 1920;
}
The CSS output:

 

h1 {
  font-size: calc(3.125vw + 40px); //This is the magic!
}

@media (max-width:320px){ //Clips the start to the min value
  font-size:50px;
}

@media (min-width:1920px){ //Clips the end to the max value
  font-size:100px;
}
This will do exactly what we want to do! It will set the font size of h1 to 50 pixels at 320 pixels, and scale perfectly up to 100 pixels at 1920.

 

The fp() arguments

$property, $min, and $max

These are the only mandatory arguments. Property can be any css property that accepts calc(), viewport units and pixels, ie. Most properties… height/width/padding/font-size etc.

$start, $end

By default, these are set at 320, and 1920. Those values should be suitable for most responsive projects. If you need to, you can change the default inside the mixin to suite your project requirements. Or you can pass in new value per include. Remember SASS 3.1 you to pass in named arguments, so you can skip any argument you don't need to override.

$clip, $clipAtStart, and $clipAtEnd

By default, the mixin will prevent the value from decreasing or increasing past the $start or $end. To disable this, either pass $clip: false to disable both directions. To disable only one direction pass either $clipAtStart: false or $clipAtEnd: false.
 

Live demo

Simple demo

http://jsfiddle.net/pixelperfect/bjyfg7ws/embedded/result,css/

Eh, Whats up doc?

http://jsfiddle.net/pixelperfect/g5ncro4p/embedded/result,css/

Examples
Here are some examples to get you thinking about the possibilities (they are endless!)

h1 {
  //font-size of 100px at 320, scaling down to 50px at 1920
  @include fp(height, 100, 50);

  //width 100px at 320, scaling up to 50px at 1024
  @include fp(width, 100, 10, $end:1024);

  //border-top-width of 5px at 320, scaling up to 20px at 768
  //scaling onwards infinitely past 768
  @include fp(border-width, 100, 10, $max:768, $clipAtEnd:false);
}

More thoughts

  • Inverted scaling of properties is possible with mixin!

    • Think – a large “Tappable” button on mobile, small “clickable” button on desktop

  • Calc and Viewport Units are no worse performing than other values – in my testing.

  • Don't over do it. While I don't see any harm in applying the mixin to any property that changes value responsively. I would still consider if the same change can be applied via cascading. eg. Using fluid properties to set the font size of an element and scaling the bottom margins with relative em’s.

  • As viewport units and calc() will generate a fraction, I wouldn't advice this technique to be used for layout purposes if rounding issues will break the layout, as rounding is handled differently in different browsers.

  • Part of my Gulp CSS build process is “Combine Media Queries” which is great for responsive projects in general, but also for this technique, hitch will minimize the need to disable clip, as all of the start/end clip media queries will be combined into one media query.

 

Contact and feedback

Found this interesting? Have any feedback or questions? Please leave a comment below, or contact me on twitter – I am Steve Rose – @pixelpp.

Share this article: