2 min read

Keep height consistent without setting a height

A good example is add-ons to form inputs. Sometimes they might be buttons and sometimes just a simple symbol in a container.

One answer might be to simply set a height on the element. This is bad for two reasons:

  1. The element might not wrap text correctly
  2. Padding and line-height can cause cross browsers issues when mixed with heights.

Demo

Code

// Round $n to the nearest $m
@function roundUp($n, $m) {
@return round(($n/$m))*$m;
}
// Get unitless values
@function parseInt($n) {
@return $n / ($n * 0 + 1);
}
@mixin height($font-size, $desired-height, $rem: true, $font-base: 16, $baseline: 4){
$font-unit: unit($font-size);
$font-value: parseInt($font-size) !default;
$height-unit: unit($desired-height);
$height-value: parseInt($desired-height) !default;
$font-base: parseInt($font-base);
$baseline: parseInt($baseline) !default;
// If rems are enforced
@if $rem == true {
// Convert the baseline to unitless rems
$baseline: $baseline/$font-base;
// If the font size is in rems
@if $font-unit != 'rem' {
$font-value: $font-value/$font-base;
}
// If the height is in rems
@if $height-unit != 'rem' {
$height-value: $height-value/$font-base;
}
}
// If rems are not enforced
@else if $rem == false {
// If the font size is in rems
@if $font-unit == 'rem' {
$font-value: $font-value * $font-base;
}
// If the height is in rems
@if $height-unit == 'rem' {
$height-value: $height-value * $font-base;
}
}
@if $height-unit != 'rem' and $height-unit != 'px' {
@error '#{$height-unit} is not supported!'
}
@if $font-unit != 'rem' and $font-unit != 'px' {
@error '#{$font-unit} is not supported!'
}
// Warn if the font-size is greater than the height
@if $font-value > $height-value {
@warn 'The font size should not exceed the desired height due to negative padding.';
}
// Calulate the relative, valueless line-height
$lh: ( roundUp($font-value, $baseline) + $baseline ) / $font-value;
// Calculate the actual px dimensions
$lh-realised: $font-value * $lh;
// Calculate the remaining space available and halve it
$offset: ($height-value - $lh-realised)/2;
// Debug
// rem: $rem;
// font-value: $font-value;
// font-unit: $font-unit;
// height-value: $height-value;
// height-unit: $height-unit;
// lh: $lh;
// lh-realised: $lh-realised;
// offset: $offset;
line-height: $lh;
font-size: if($rem, $font-value + rem, $font-size);
padding-bottom: if($rem, $offset + rem, $offset + px);
padding-top: if($rem, $offset + rem, $offset + px);
}
// Check out http://codepen.io/craigmdennis/pen/VYqJqq
// to see some examples
.rems-from-pixels {
@include height(20px, 32px);
}
.rems-from-rems {
@include height(1.25rem, 2rem);
}
.pixels-from-pixels {
@include height(20px, 32px, false);
}
.pixels-from-rems {
@include height(1.25rem, 2rem, false);
}
.mix-and-match {
@include height(20px, 2rem);
}

Why not set the line-height to 1 to make the calculation easier?

When the line-height is set to one, the descenders of the type actually protrude and can get cut off if overflow: hidden; is applied. The text will also be squashed together if the element does wrap.

The mixin finds the next multiple of the baseline (4 by default) and uses that as the desired line-height.

So if you specify 13px as your font size, the mixin will use 16px as the line-height and convert it into a unit-less value.

It then takes the remaining space available from the height specified, halves it, and applies that to the top and bottom padding.

Rems and px

Not only does the mixin do the heavy lifting calculations for you but it also doesn’t care whether you use rems or px for the input values. You can also specify whether you want either to be the output (rems are default).

SASS precision

There is only an issue with SASS’ decimal point precision.

By default SASS uses 5 decimal points but when using values that don’t divide easily by each other you can end up with a recurring number.

The precision needs to be at least 7 decimal points for browsers to correctly round to the correct pixel display.

Unfortunately CodePen doesn’t support this so some values have been overridden manually

Fixing SASS precision:

Use it in your project

  • Add this to your project using bower npm install sass-height --save
  • Download the project folder
  • Fork the project on GitHub
Posted