Responsive Web Design with HTML5
Bobby Solomon, from the Disney team, stated last year: “Honestly, our team is bored of talking about responsive design. It’s how any self-respecting website should behave in 2012”. Well, responsive web design (RWD) is no panacea, but definitely that is an indicator of good quality in the times when diversity of access point devices steady grows. If simply put, RWD is a way to provide a decent user experience (UX) on a web-site agnostic to the devices or platform it is accessed from. Commonly RWD is achieved via following techniques:
The fluid grid concept , which welcomes usage of only relative units such as percentages, viewport-percentages, ‘em’, ‘ex’, ‘ch’, ‘rem’. This applies not only to font size, but also to box dimension and size properties (padding, margin, border, width, height and so on).
Flexible images in relative units that set the proportions to the box. For example, img{width: 100%} will adjust to the bounding box width but keeping its height according to the image aspect ratio.
Media queries that allow tuning styles depending on used device screen characteristics
Responsive Design + Server Side Components (RESS); this method reduces bandwidth, speeds up loading time and frees up system resources by removing device-depend redundant data (HTML, JavaScript, CSS, images) or replacing them with lighted versions.
From designer’s point of view, one shall think of informational architecture for a wide range of possible devices, going from weaker ones to advanced devices (progressive enhancement rather than graceful degradation). Here come handy RWD patters.
Is that it? Not really. Spec-writers of W3C are aware of the trend and they have been taking care about the way developer could achieve RWD. Developers are used to rather resort to hacks while implementing RWD. In fact many patterns can be done natively to the HTML5 layout by using features already available in modern browsers or coming soon.
We take now a generic example of Holy Grail Layout and see how it solves with different HTML5 approaches. Imagine that we need to arrange the components of the page depending on the screen width like it is shown on the illustrations below:
CSS Flexible Box Module
The Flexible Box Module provides an efficient way to arrange (dynamically sized) elements within a container. Thereby using CSS only one can define how elements lay out, align and distribute space depending on the viewport size.
Checkout how easy it is to implement our example relying on flex box module (Note: here used the modern syntax, which is not backward compatible to the syntaxes from 2011 and 2009):
<div class="flex-container">
<header id="header">Header</header>
<article class="article">Article</article>
<aside class="sidebar sidebar1">Sidebar</aside>
<aside class="sidebar sidebar2">Sidebar</aside>
<footer id="footer">Footer</footer>
</div>
.flex-container {
display: flex;
flex-flow: row wrap;
text-align: center;
}
#header, #footer {
background: rgb(173, 194, 226);
width: 100%;
}
#header {
order: 1;
}
#footer {
order: 5;
}
.article {
order: 3;
flex: 1 60%;
background: rgb(255, 255, 204);
}
.sidebar {
flex: 1 20%;
}
.sidebar1 {
order: 2;
background: rgb(255, 204, 204);
}
.sidebar2 {
order: 4;
background: rgb(255, 153, 153);
}
/* iPads (landscape) ----------- */
@media only screen
and (min-device-width : 768px)
and (max-device-width : 1024px)
and (orientation : landscape) {
.article {
order: 2;
flex: 1 100%;
}
.sidebar {
order: 3;
flex: 1 50%;
}
.sidebar2 {
order: 4;
flex: 1 50%;
}
}
/* iPads (portrait) ----------- */
@media only screen
and (min-device-width : 768px)
and (max-device-width : 1024px)
and (orientation : portrait) {
.sidebar {
flex: 1 100%;
}
}
Here we define flex container .is-flexbox and its flex items (#header, #footer, .article, .sidebar) . We declare that the flex items of the container arrange into a multiline row (flex-flow: row wrap;). We specify the order in which we want the items appear on the page ( order: 1; ). We also describe the expected behavior of the items within the container (flex: <grow> <shrink> <basis>). Where the first parameter defines how the item absorbs the available space comparing to other ones (2 – twice wide as others). The second parameter describes ability to shrink in the same way. The third one defines the default size before the surrounding space is distributed.
That is just an example of using flex box module. As you see you can gain a lot by fiddling with flex property. Moreover, using such flex container properties as justify-content, align-items , align-content you can control vertical and horizontal alignments and spacing around flex items and their contents.
You can find details at:
-
https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes
-
http://www.w3.org/TR/css3-flexbox/ To see flex boxes in action
-
http://the-echoplex.net/flexyboxes/ In spite of browser compatibility table promising look in fact Flex Box Module is supported to varying degrees. For an instance, Firefox still doesn’t support multiline flex-boxes and cannot render properly that particular example given above.
Unfortunately the only polyfill I found so far enables obsolete (2009) Flex Box Model.
CSS Grid Layout Module
The Grid Layout Module introduces a two-dimensional grid-based layout system where developer can control in CSS how children elements arrange by grid slots. So we can simply specify what rows and columns intended for layout components within the grid and change it dynamically with media queries for various display sizes.
<div class="grid">
<header id="header">Header</header>
<article class="article">Article</article>
<aside class="sidebar sidebar1">Sidebar</aside>
<aside class="sidebar sidebar2">Sidebar</aside>
<footer id="footer">Footer</footer>
</div>
html, body {
height: 100%;
}
.grid {
display: -ms-grid;
-ms-grid-columns: 20% 1fr 20%;
-ms-grid-rows: auto 5px auto 5px auto;
text-align: center;
height: 100%;
}
#header, #footer {
background: rgb(173, 194, 226);
-ms-grid-column-span: 3;
}
#header {
-ms-grid-row: 1;
}
#footer {
-ms-grid-row: 5;
}
.article {
-ms-grid-row: 3;
-ms-grid-column: 2;
background: rgb(255, 255, 204);
}
.sidebar {
-ms-grid-row: 3;
}
.sidebar1 {
-ms-grid-column: 1;
background: rgb(255, 204, 204);
}
.sidebar2 {
-ms-grid-column: 3;
background: rgb(255, 153, 153);
}
/* iPads (landscape) ----------- */
@media only screen
and (min-device-width : 768px)
and (max-device-width : 1024px)
and (orientation : landscape) {
.grid {
-ms-grid-columns: 50% 50%;
-ms-grid-rows: auto 5px auto 5px auto 5px auto;
}
.article {
-ms-grid-column: 1;
-ms-grid-column-span: 2;
}
#header, #footer {
-ms-grid-column-span: 2;
}
.sidebar {
-ms-grid-row: 5;
}
.sidebar1 {
-ms-grid-column: 1;
}
.sidebar2 {
-ms-grid-column: 2;
}
#footer {
-ms-grid-row: 7;
}
}
/* iPads (portrait) ----------- */
@media only screen
and (min-device-width : 768px)
and (max-device-width : 1024px)
and (orientation : portrait) {
.grid {
-ms-grid-columns: 100%;
-ms-grid-rows: auto 5px auto 5px auto 5px auto 5px auto;
}
.grid > * {
-ms-grid-column: 1;
}
.sidebar2 {
-ms-grid-row: 7;
}
#footer {
-ms-grid-row: 9;
}
}
You can find details at:
- http://css-tricks.com/almanac/properties/g/grid/
- http://www.w3.org/TR/css3-grid-layout/ Currently Grid Layout module is supported only by IE, but you can use the polyfill to enable it in other browsers.
CSS Regions Module
The Regions Module provides a content flow mechanism allowing to specify how content is flowing from one regions to others. Developer can just declare that for a specific display size some content elements flow to the defined regions.
<div id="view">
<header id="header">Header</header>
<aside class="sidebar region1"></aside>
<article class="article">Article</article>
<aside class="sidebar region2"></aside>
<aside class="sidebar region3"></aside>
<footer id="footer">Footer</footer>
</div>
<div id="sidebar-sources">
<aside class="sidebar1">Sidebar</aside>
<aside class="sidebar2">Sidebar</aside>
</div>
#view > * {
display: inline-block;
float: left;
text-align: center;
}
#header, #footer {
background: rgb(173, 194, 226);
width: 100%;
}
.article {
background: rgb(255, 255, 204);
width: 60%;
}
#view > .region1,
#view > .region2 {
background: rgb(255, 204, 204);
width: 20%;
}
#view > .region1 {
-webkit-flow-from: region1;
-ms-flow-from: region1;
}
#view > .region2 {
-webkit-flow-from: region2;
-ms-flow-from: region2;
}
#view > .region3 {
-webkit-flow-from: region3;
-ms-flow-from: region3;
background: rgb(255, 153, 153);
width: 20%;
}
#view > .region2 {
display: none;
}
#sidebar-sources {
text-align: center;
}
#sidebar-sources .sidebar1 {
-webkit-flow-into: region1;
-ms-flow-into: region1;
}
#sidebar-sources .sidebar2 {
-webkit-flow-into: region3;
-ms-flow-into: region3;
}
/* iPads (landscape) ----------- */
@media only screen
and (min-device-width : 768px)
and (max-device-width : 1024px)
and (orientation : landscape) {
#view > .article {
width: 100%;
}
#view > .region3 {
width: 50%;
}
#view > .region2 {
display: inline-block;
width: 50%;
}
#view > .region1 {
display: none;
}
#sidebar-sources .sidebar1 {
-webkit-flow-into: region2;
-ms-flow-into: region2;
}
}
/* iPads (portrait) ----------- */
@media only screen
and (min-device-width : 768px)
and (max-device-width : 1024px)
and (orientation : portrait) {
#view > .region3 {
width: 100%;
}
#view > .region2 {
width: 100%;
}
}
We define a region by use of flow-from and flow-into and flow content from non-semantic block #sidebar-sources into layout components.
This approach benefits from concern separation. As you see we have data separated from the view (layout) and can regulate in CSS where in layout the data lands.
You can find details at:
CSS Multi-column Layout Module
The Multi-column Layout Module provides an easy way to define multiple columns of text. It is not intended for Holy Grail Layout example, but it is still a great tool for RWD where content is represented in columns. Just examine how simple it is to specify and control the text flow with the module:
<article class="article">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu lorem malesuada, feugiat lorem ut, placerat purus. Curabitur sem risus, suscipit ultrices suscipit in, elementum sit amet lorem. Cras volutpat rhoncus nibh, mollis pharetra ipsum semper eget. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin eu odio neque. Nullam ut metus commodo est porta vulputate at quis arcu. Donec sed facilisis dolor. Sed viverra blandit sagittis. Suspendisse ullamcorper sem a placerat tincidunt.</p>
<p>Pellentesque a auctor massa. Donec in convallis neque. Praesent in eros ut purus semper commodo in pharetra purus. Nulla facilisi. Cras accumsan ipsum ac pharetra faucibus. Quisque commodo felis eu mattis vehicula. Nam tincidunt hendrerit risus in ornare. Mauris eu nisl fringilla, pretium mi in, aliquet lacus. </p>
</article>
.article {
-webkit-column-count: 4;
-moz-column-count: 4;
column-count: 4;
-webkit-column-gap: 2em;
-moz-column-gap: 2em;
column-gap: 2em;
}
/* iPads (landscape) ----------- */
@media only screen
and (min-device-width : 768px)
and (max-device-width : 1024px)
and (orientation : landscape) {
.article {
-webkit-column-count: 3;
-moz-column-count: 3;
column-count: 3;
}
}
/* iPads (portrait) ----------- */
@media only screen
and (min-device-width : 768px)
and (max-device-width : 1024px)
and (orientation : portrait) {
.article {
-webkit-column-count: 2;
-moz-column-count: 2;
column-count: 2;
}
}
You can find details at:
Polyfills available:
CSS Template Layout Module
The Template Layout Module abstracts layout grid on a new level. It works in the manner of Grid Layout. Developer describes layout and specify in CSS where on slots the layout components go. I didn’t find any implementation yet available.
You can find details at:
Position sticky
I guess everybody at least once already was making an element stick on scrolling like it does Top Story sidebar at https://news.google.com/. Twitter Bootstrap has a JavaScript plugin for that task. Whatever the implementation that requires a handler on scroll event. If we need these elements to stick per widget we get a lot of handlers running with every scroll action. Examine how simple to make it the native way using position:sticky.
.is-sticky {
position: -webkit-sticky;
position: -moz-sticky;
position: -ms-sticky;
position: -o-sticky;
top: 5px;
}
You can find details at:
-
http://updates.html5rocks.com/2012/08/Stick-your-landings-position-sticky-lands-in-WebKit Polyfills available:
Fast loading
Bruce Lawson at FOWA 2013 conference in London was urging to use .webp graphical format as that gains up to 26% reduction in file size comparing to PNG without notable quality loss. Well, .webp is supported by WebKit browsers, but what about other browsers? He proposed the following fallback for background images:
background-image: url("wavy.gif");
background-image: image("wavy.webp", "wavy.png", "wavy.gif");
And this one for content images:
<picture>
<source src=foo.webp type=image/webp>
<source src=foo.png type=image/png>
<img src=foo.png alt="insert alt text here"> <!-- fallback content -->
</picture>
He also was presenting lazyload and postponed attributes (https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/ResourcePriorities/Overview.html).
By the first you can specify to user agent which elements associated resource data not prioritized for loading. The second does the same but to invisible elements only.
Conditional image loading using srcset is not a new approach, but still needs to be added to the list:
<img alt="Example"
src="banner.jpeg"
srcset="banner-HD.jpeg 2x, banner-phone.jpeg 640w, banner-phone-HD.jpeg 640w 2x">
Here we specify what image have to be loaded in the case of mobile device and device with Retina display. As far as I know srcset attribute is already supported by WebKit browsers. There is also a polyfill available https://github.com/borismus/srcset-polyfill. However the polyfill is not considered a perfect solution, so may prefer the RESS solution by BBC News developers.
You can find details at:
- http://www.html5rocks.com/en/mobile/high-dpi/#toc-srcset
- http://www.w3.org/html/wg/drafts/srcset/w3c-srcset/
Conclusion
Enlisted here specs are not yet fully implemented in browsers or implemented poorly. With the polyfills you can succeed to varying degrees, there still will be completeness of support and performance concerns. Nonetheless while doing RWD we still have to somehow solve the tasks. So I would say, rather than developing one-time custom solutions, we can stay consistent to the W3C specs and shim the properties we actually need for the tasks.