Break(point) free: No more stepwise Styling in CSS

2018-01-01 · CSS

Most CSS Frameworks and many examples of custom CSS code have predefined breakpoints where the layout will radically change. This is often bound to a custom grid that influences container width, container stacking, font sizes, spacing, etc. As a result, those pages look OK on most screens. But especially on mid-sized screens, there is often wasted space because of strange choices of breakpoints and spacing. And as a developer, you have to deal with repetitive code like the example below.

@ media ( max-width : 540px ) { . container { padding : 10 px ; } } @ media ( max-width : 720px ) { . container { padding : 20 px ; } } @ media ( max-width : 960px ) { . container { padding : 30 px ; } } @ media ( max-width : 1140px ) { . container { padding : 40 px ; } }

You can also have a look at a—admittedly simpler—real-world example in the Bootstrap Jumbotron. Such definitions with fixed breakpoints are often used, however, they are mostly unnecessary. It is much easier to define relative dimensions instead.

Relative Spacing

Spacing is an important part of creating good layouts. Today, many landing or product pages even have more spacing than actual content (whether that results in good layouts is another question, though).

As a developer, you have different ways of adding spacing to your website. And in addition to that, there are many different length units you can choose from. But choosing pixel px (or other absolute units) is often a bad approach as you rarely want the same spacing on small mobile devices and huge desktop screens. The naive solution to that is to add multiple media queries to handle different screen sizes. But you can avoid that by using more suitable, relative units.

The CSS unit that is especially great for relative sizing depending on the screen size is vmin . It stands for “viewport minimum” and equals to the lower of vh (1% of the viewport height) and vw (1% of the viewport width). 10vmin , for example, equals to 10% of the viewport width on screens in portrait mode, and 10% of the viewport height on screens in landscape mode.

An example usage of vmin is to add spacing on every side of a container element. Such elements are often used as a parent for the whole website content, thus they often are centered and have maximum width.

. container { padding : 3 vmin ; margin : 0 auto ; max-width : 1200 px ; }

Using vmin for spacing like this has the added benefit of looking good on screens in every rotation. That means the spacing won't be too wide on narrow screens, and it won't be too high on wide screens. If you want to, you can even specify a minimal spacing using calc .

. container { padding : calc ( 10 px + 3 vmin ) ; }

With that approach, there will always be a spacing of at least 10 pixels regardless of the screen size. But on large screens, the spacing will still be much larger than on tiny screens.

Relative Font Sizes with CSS Locks

Having relative font sizes is very useful. Smaller devices (such as mobile phones) tend to be closer to your eyes than large desktop screens. That is why the font sizes have to adapt to the screen size. Bootstrap, for example, defines three different font sizes depending on the viewport. So what applies to spacing also applies to font sizes. There is, however, a crucial difference: font sizes must always be within fixed bounds. For example, too small font sizes should be avoided as they are hardly legible.

A very good approach to ensure legible font sizes is a mechanism called “CSS locks”. The concept was introduced by Mike Riethmuller in Precise control over responsive typography and the name “CSS locks” was coined by Tim Brown in Flexible typography with CSS locks. If you are interested in how exactly it works, you can read the very detailed article about the math of CSS locks.

Essentially, CSS locks allow you to set a minimum and a maximum font size. The minimum font size ( minf ) will be applied below a minimum viewport width ( minw ). And the maximum font size ( maxf ) will be applied above a maximum viewport width ( maxw ). In between minw and maxw , the font size will scale proportionally from minf to maxf . The following graph visualizes that behaviour.

Font Size Viewport Size

To apply a CSS lock, a calculation with calc and two media queries (for the minimum and maximum font size) are necessary. See the example below for a pseudo implementation. [minf] , [maxf] , [minw] , and [maxw] have to be replaced by numeric dimensions without a unit.

font-size : calc ( [ minf ] px + ( [ maxf ] - [ minf ] ) * ( ( 100vw - [ minw ] px ) / ( [ maxw ] - [ minw ] ) ) ) ; @ media only screen and ( max-width : [ minw ] px ) { font-size : [ minf ] px ; } ; @ media only screen and ( min-width : [ maxw ] px ) { font-size : [ maxf ] px ; } ;

You can use CSS locks for font sizes, line height, image dimensions, or any other size/width/height declaration in CSS. And a good starting point is to use it on the html element. You can specify a relative font size on it and then set your actual font-size properties using the rem unit. rem is a multiply of the base font size set on the root element. And as the font size on html is relative, all sizes with the rem unit are relative, too.

html { /* specify CSS lock on font-size */ } h1 { font-size : 1.25 rem ; } p { font-size : 1 rem ; }

The CSS lock calculation might look a little overwhelming. Simplifying the implementation for your use-case is thus a good idea. If you already use SCSS/Sass, you can add a mixin to do that. An example implementation of CSS locks with a SCSS/Sass mixin could look as follows.

@mixin css-lock ( $minf , $maxf , $minw : 320 , $maxw : 1200 , $property : font - size , $unit : px ) { # { $ property } : calc ( # { $ minf } # { $ unit } + ( # { $ maxf } - # { $ minf } ) * ( ( 100vw - # { $ minw } # { $ unit } ) / ( # { $ maxw } - # { $ minw } ) ) ) ; @media ( max-width : #{ $minw } #{ $unit } ) { # { $ property } : # { $ minf } # { $ unit } ; } @media ( min-width : #{ $maxw } #{ $unit } ) { # { $ property } : # { $ maxf } # { $ unit } ; } } html { // ensure a font size between 14 and 18 pixels @include css-lock ( 14 , 18 , 320 , 1200 , font - size , px ) ; }

CSS locks are not super simple to understand, but they are a great way to style fonts. And especially with the help of preprocessors like Sass, there is nothing stopping you from using them.

Grids, Containers, and Stacking

Most often you don't want a predefined grid. But what you need is a way to place content in columns and rows that adapt to the viewport. The shift from columns to rows should happen when the content requires it, not when a predefined screen dimension has been reached. Don't force your content into a generic grid system provided by a framework. But instead create your own grid that matches your content.

Having content-aware columns and rows is a prime-example for the use of Flexbox. You can add elements to a display: flex; container and the child elements will be aligned based on their content. You can also specify the growing and shrinking of their dimensions. Building a basic grid for the prominent case of columns that shift to rows on smaller devices is very simple with Flexbox. Just set the child elements to a minimum width with flex-basis and allow the Flexbox container to break ( flex-wrap: wrap; ) when there is not enough space to show all children in a row.

The browser support for Flexbox is very good. Unless you need to handle browsers from the Stone Age, specifically Internet Explorer below 11 or 10, Flexbox is the way to go. In addition to Flexbox, you might also want to use CSS Grid. While Flexbox is meant for local layouts (e.g. simple rows and columns), CSS Grid's task is to specify the overall page layout (e.g. navigation, sidebar, main content, and footer). But note that CSS Grid unfortunately still does not have the best browser support, yet.

Convincing the Designers

Relative CSS makes a lot of sense. The Internet is accessible from billions of devices with thousands of different resolutions. A generic grid system with predefined breakpoints can never handle every screen size equally well. And it is not enough to only have a mobile and a desktop version. There is a multitude of devices in between (as well as before and after). That sometimes contradicts with ideas from designers, I've experienced that first-hand. The web is not print, though. It is not static but dynamic and it has to work well on every single screen size.

Convincing your designers to embrace relative styling is something you have to do. Each and every team or project is different and has different requirements. So I cannot really give you an insight on whether it works for you or not. But I do have some suggestions which can be applied to a style guide for your next project.

A style guide should not differentiate between mobile and desktop when it is not absolutely necessary. So don't use $spacingParagraph1 on mobile and $spacingParagraph2 on desktop. There is no hard line between those two device categories anyway. Instead, you should, for example, specify the margin of paragraphs to $spacingParagraph (on every device version) and define it as a certain relative height. The same goes for font sizes and every other dimension. That way, the designers only have a certain number of different spacings that they can apply to different elements. But they don't have to think about mobile/tablet/desktop versions where it is unnecessary. And in situations where it is necessary, they can focus on the component that actually requires their attention (e.g. how to make a data table work on smaller screens).

Yes, thinking in relative dimensions is harder than to just extract a certain number of pixels from Design-Tool-of-Choice™. But every good webdesigner should be interested in how his/her creations look in an actual browser on an actual device. There will be changes to be made before the website can go live anyway. And in the end, it does not matter whether you iterate on the “proper” breakpoints and style before/after them, or change relative CSS dimensions.

Wrap-Up

CSS frameworks still have some advantages. They are useful for simple pages, are (usually) well-tested, and have good browser support. But they are definitely no one-size-fits-all solution.

Using relative sizes, dimensions, and spacing in CSS has been helping me a lot to focus on the actual styling and layout. It makes cross-device testing easier as you have much less breakpoints where things could go wrong. Yes, you will still need media queries in certain situations (and even for CSS locks). And it is a great technology which made responsive websites possible after all. But sometimes having breakpoints is just not necessary.