Cascading CSS inheritance with responsive design

Over the past year or two, responsive design has edged its way into the forefront. It allows websites to support multiple device sizes and viewports, all through the CSS layer. So why do so many developers implement their media queries in the most complicated or inefficient ways? If you're curious as to what I'm referring to, check out the code sample below.

@media only screen and (min-device-width : 320px) and (max-device-width : 480px) { }
@media only screen and (min-device-width : 768px) and (max-device-width : 1024px) { }
@media only screen and (min-device-width : 768px) and (max-device-width : 1024px) and (orientation : portrait) { }

The problem with this approach is that rules are defined between 2 ranges (or even more granular when using orientation) instead of inheriting from the highest resolution. What if mobile and tablet both have similar styles that need to be applied? Either the code will be duplicated between 2 media queries, or a 3rd media query would be created to handle this. Both of these approaches are incorrect. The proper way (personal opinion) to handle responsive designs is by cascading inheritance from the highest resolution down to the lowest resolution, or vice versa; not defining rules for specific resolutions.

Largest to Smallest, or Smallest to Largest

When implementing the CSS for a new website, which resolution do you implement first? The answer should always be dependent on the target audience. If you plan to support mobile, then mobile-first with progressive enhancement should be chosen. If your target audience are desktop browsers, then desktop-first with graceful degredation should be chosen.

The pros and cons between each approach differ greatly. There are many great articles on this subject, so I will not go into great detail. However, here are a few key points for each approach, starting with mobile-first.

  1. The bare minimum CSS styles can be used to target mobile devices first (no floats, columns, etc)
  2. The more complicated or advanced CSS can be applied through media queries for tablet and desktop (progressive enhancement)
  3. Improved speed and rendering time for mobile devices since media query styles aren't computed
  4. Stops the loading or inclusion of assets that aren't required by default
  5. Can be extremely difficult or near impossible to do for complex designs

And when designing desktop-first.

  1. Typically the desktop, tablet and mobile designs are based off the same design, with the smaller resolutions simply removing elements or altering styles (graceful degredation)
  2. Much easier to design for when complex designs are used
  3. Also easier to scale fluid websites from a larger size to a smaller one
Cascading Inheritance

As mentioned above, using min-width coupled with max-width doesn't allow for inheritance between resolutions. So what does allow for inheritance? The answer is simple — only use max-width (for desktop-first), or min-width (for mobile-first). When using a max-width media query, all media queries that are larger than the current browser viewport will be inherited. So in layman's terms, tablet will inherit desktop rules (since they are defined by default), and mobile will inherit tablet AND desktop rules (since they are both defined previously). The same concept applies when doing the reverse using min-width. This approach is vastly superior as it reduces code complexity and repetition.

Let's create an desktop-first example for a horizontal three column layout, with the left column containing a vertical menu. When viewed in a tablet, the columns will be displayed vertically and the menu will be switched to horizontal. When viewed in mobile, the menu will hide text and display icons, while also inheriting tablet styles. A similar example can be found on this website, just resize it!

<div class="wrapper">
	<div class="left">
		<ul class="menu">	
			<li><a href="/"><span class="icon"></span> <span class="text">Home</span></a></li>
			<li><a href="/page"><span class="icon"></span> <span class="text">Page</span></a></li>
			<li><a href="/contact"><span class="icon"></span> <span class="text">Contact</span></a></li>
		</ul>
	</div>
	<div class="middle">Middle</div>
	<div class="right">Right</div>
</div>

And the initial CSS that sets the float styles (desktop by default).

.wrapper:after {
	clear: both;
	display: block;
	content: "";
}
.left,
.right {
	float: left;
	width: 25%;
}
.middle {
	float: left;
	width: 50%;
}
.menu {
	list-style: none;
	margin: 0;
	padding: 0;
	/* ... styles ... */
}
.menu li > a { display: block; }
.menu .icon { display: none; }

Pretty simple right? Now let's add tablet support using the landscape breakpoint.

/* Inherits from desktop (default styles above) */
@media only screen and (max-width: 1024px) {
	.left, .right, .middle {
		float: none;
		width: 100%;
	}
	.menu li { display: inline-block; }
}

We now have our columns displaying vertically and the menu horizontally for all devices below a 1024px viewport. These rules will be inherited by any kind of device, whether it's a laptop, smart phone or tablet, just as long as their viewport is no larger than 1024px. And now for mobile support, also using the landscape breakpoint.

/* Inherits from tablet and desktop styles above */
@media only screen and (max-width: 480px) {
	.menu .text { display: none; }
	.menu .icon { display: inline-block; }
}

And now we have mobile styles using only a few lines, easy right? That's what I like to call responsive cascading inheritance. Why not try implementing a mobile-first approach?

Standard Breakpoints

When designing for responsive, I like to support 6 standard breakpoints (not including default styles). These breakpoints cover about 95% of use cases.

  • Large Desktop - Default styles if designing for desktop-first
  • Small Desktop - 1440px width
  • Laptop - 1280px width
  • Tablet Landscape - 1024px width
  • Tablet Portrait - 768px width
  • Mobile Landscape - 480px width
  • Mobile Portrait - 320px width

I use these desktop-first breakpoints in my Titon Toolkit project as Sass mixins; feel free to borrow them. I plan to add another agnostic mixin that handles min width, max width and other options, allowing for easier use case targeting.

@mixin if-desktop {
	@media only screen and (max-width: 1440px) {
		@content;
	}
}
@mixin if-laptop {
	@media only screen and (max-width: 1280px) {
		@content;
	}	
}
@mixin if-tablet-landscape {
	@media only screen and (max-width: 1024px) {
		@content;
	}
}
@mixin if-tablet-portrait {
	@media only screen and (max-width: 768px) {
		@content;
	}
}
@mixin if-mobile-landscape {
	@media only screen and (max-width: 480px) {
		@content;
	}
}
@mixin if-mobile-portrait {
	@media only screen and (max-width: 320px) {
		@content;
	}
}
Do note that I use min-width and max-width which rely on resolution size. If you want to target literal device size, use min-device-width and max-device-width alongside the viewport meta tag.
In Closing

I've used this approach on this website and a few other projects I have worked on and it seems to have worked perfectly. I've also tested it in Firefox, Chrome, IE and even on my Android device with no problems. There is always room for improvements, so if you have a suggestion or correction, be sure to comment below!

Easily resetting your stylesheets

I always hear talk about reset CSS, and how every website should encompass them. I mean it is everywhere. I honestly never found the use or reasoning to include an additional stylesheet, just to render elements as "dull". I simply use the code below to reset only elements I need to worry about, or ones that are used as the majority.

html, body, div, img, form, fieldset, ul, ol, li, h1, h2, h3 {
    border: none;
    padding: 0;
    margin: 0;
}

I personally like the way some elements are styled by default, mainly the table and its child elements. I use to use the global declaration of * to reset ALL elements in the page, but that caused weird and unexpected styling issues that I did not like. But to simply put it, its all a matter of personal preference, and to me you should only reset elements that are actually used (this also saves a few bytes on the filesize, boosh!).