CompanyArticlesOpen SourceServices
 

Clean Responsive Design Code With CSS Custom Properties

Way back in the late 2000's / early 2010's there was heated debate about Adaptive Web Design (AWD) (serving up different versions of a page based on device) versus Responsive Web Design (RWD) (serving a single page that can render appropriately for any device). The best arguments for AWD centered around performance for mobile devices and support from older browsers. The best arguments for RWD centered around maintainability of code.

Today, that argument has mostly been settled as connection speeds have improved, older browsers have died out and the variety of combinations of devices and features has continued to expand. Developers and project sponsors alike prefer responsive design for it's ability to serve a wider range of devices at a cheaper cost. While many large sites still feature adaptive design over responsive design, that is simply a matter of product life cycle, in my experience. Every greenfield implementation I have worked on since 2012 has featured responsive design.

Traditional Responsive Code with Pre-processor Variables

From a development perspective, the responsive design revolution was aided by the prevalence of two technologies:

  • CSS3 Media Queries for device view port size and feature targeting (ex: touch devices)
  • CSS Pre-processors (Sass and LESS) for managing large CSS projects and, specifically, the introduction of variables into CSS

These technologies allow a developer to specify when the CSS rules should change with media queries and how the rules should change (in a maintainable way) using pre-processor variables.

Consider the common responsive use case of displaying two elements side-by-side in larger view ports and stacking the same elements in smaller view ports.

Side-by-side for larger displays such as laptops and tablets.
Stacked for smaller displays such as phones.

This type of flexible layout approach is extremely common in responsive sites and it can be achieved rather easily. For these examples, we'll be using the SCSS variant of Sass as our pre-processor.

<body>
	<h1>Look At This Red Panda!</h1>
	<img src="./panda.jpg">
</body>

The approach takes advantage of the way inline elements flow within the DOM: left-to-right within a line, then top-to-bottom for each line or block element. For larger displays, the default style is applied. Each item is given 50% of the width of the document and appropriate padding for the gutters.

$cell-width: 50%;
$cell-padding: 10px;

h1 {
	float: left;
	box-sizing: border-box;
	text-align: center;
	width: $cell-width;
	padding: $cell-padding;
}

img {
	box-sizing: border-box;
	width: $cell-width;
	padding: $cell-padding;
}

When the view port is 480px or less, the width of the elements is increased from 50% to 100% and the padding is removed.

@media (max-width: 480px) {
	$cell-width: 100%;
	$cell-padding: 0;

	h1 {
		width: $cell-width;
		padding: $cell-padding;
	}

	img {
		width: $cell-width;
		padding: $cell-padding;
	}
}

While the previous example is perfectly functional, it contains something that will irritate the engineering purist in all of us and lead to code which is more difficult to maintain - boilerplate code. That is, code which is unnecessarily repeated. Closer inspection of the previous two blocks of SCSS code show the width and padding lines defining the h1 and img in the mobile (< 480px) block are completely repeated from the initial description of those tags.

$cell-width: 50%;
$cell-padding: 10px;

h1 {
	float: left;
	box-sizing: border-box;
	text-align: center;

              	width: $cell-width;

              	padding: $cell-padding;
}

img {
	box-sizing: border-box;
	width: $cell-width;
	padding: $cell-padding;
}

@media (max-width: 480px) {
	$cell-width: 100%;
	$cell-padding: 0;


              	h1 {

              		width: $cell-width;

              		padding: $cell-padding;

              	}


              	img {

              		width: $cell-width;

              		padding: $cell-padding;

              	}
}

The repeated definitions are unfortunately required with SCSS variables since pre-processors won't repeat previous rules in the generated CSS just because the developer changes the value of a variable in a more-specific context (smaller scope).

CSS Custom Properties Over Pre-processor Variables

CSS Custom Properties (or CSS variables) are fundamentally more powerful than pre-processor variables because they are interpreted in real-time by the browser, rather than compiled to static CSS. This allows us to redefine variables in a more-specific context and have all inherited rules in that scope re-evaluate with the new value for the variable. That's a fancy way of saying, we no longer have to repeat rules that are unchanged. Here's the same example using only pure CSS with custom properties.

:root {
	--cell-width: 50%;
	--cell-padding: 10px;
	
	@media (max-width: 480px) {
		--cell-width: 100%;
		--cell-padding: 0;
	}
}

h1 {
	float: left;
	box-sizing: border-box;
	text-align: center;
	width: var(--cell-width);
	padding: var(--cell-padding);
}

img {
	box-sizing: border-box;
	width: var(--cell-width);
	padding: var(--cell-padding);
}

The same code, rewritten has the following advantages:

  • Reduced from 31 lines to 23 lines overall
  • Removed copied lines, leading to fewer bugs during maintenance
  • More readable code since you can see how your variables change in different contexts all at once
  • No need for a pre-processor (this is minor since you'll still likely want a pre-processor for imports and other features)

CSS Custom Properties still have one significant drawback...

Browser Support

Or, how I learned to stop worrying an de-supportted Internet Explorer 11. While this may not be an option for everyone, it's global usage is less than 3% at the time of this writing. Even some of the largest e-commerce clients I've worked with have de-supported all browsers with combined version usage of less than 5%. Think about it.

The only other "major" browser which doesn't fully support CSS variables is Opera Mini. Opera Mini's usage is also less than 3% globally and the predominant share of that usage comes from markets in Africa and Asia, due to it's built-in data compression.

The invaluable site, Can I Use, shows every other tracked browser supporting CSS custom properties (even Edge!)

Wrap

The usage of CSS Custom Properties comes down to one question. Do you need to support IE11 or Opera Mini? If so, then I'm sorry - they will be gone soon enough. Until then, keep using pre-processor variables. For everyone else, it's time. CSS variables are more flexible and will consequently lead to more maintainable code, especially for responsive design.

Comments