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.
- The bare minimum CSS styles can be used to target mobile devices first (no floats, columns, etc)
- The more complicated or advanced CSS can be applied through media queries for tablet and desktop (progressive enhancement)
- Improved speed and rendering time for mobile devices since media query styles aren't computed
- Stops the loading or inclusion of assets that aren't required by default
- Can be extremely difficult or near impossible to do for complex designs
And when designing desktop-first.
- 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)
- Much easier to design for when complex designs are used
- 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;
}
}
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!