The Holy Grail of Mobile Layout

The Holy Grail of Mobile Layout

The perfect mobile layout should allow the presence of a navbar, a content area and maybe a footer toolbar. This layout should resize with orientation changes such that the bottom toolbar is always at the bottom and the content area resizes its width and height to fit the new dimensions. This is tough because, first off, there is not fixed positioning, and secondly, there is no single finger scrolling in Webkit for CSS defined scrolling regions.

it’s taken me a while to come up with a solution to these layout requirements. Any of you trying to make a Web app feel like a native mobile app know the frustration of trying to deal with orientation change and complex layout. Not being able to make a toxic mix of viewport meta tags, CSS media queries and onorientationchange events work to give me a fluidly resizing layout when the mobile device’s orientation changed drove me to despair. I compensated early on using JavaScript to resize everything, except I had to pay a performance penalty, especially if an app was complex. After a lot of sweat and blood and endless trial and error, I finally came up with a CSS only solution to making a layout that resizes smoothly with orientation changes.

So, the first big mistake, one that everyone and their brother recommends, is not to use the meta tag viewport settings of device width and device height. Instead, just use the initial scale, maximum scale and user scalability:

<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

The reason is that no matter what you set the width and height to with CSS, Webkit insists on thinking the document should have the height and width from when the document first loaded. This happens even if you use a CSS value of “width: 100%”. Switching from portrait to landscape, you’ll see that your layout fails to fill the width of the landscape screen:

iPhone Portrait Layout

iPhone Portrait Layout

iPhone Landscape Layout

iPhone Landscape Layout

As you can see with the landscape orientation the layout fails to fill the available width of the device. This app has its root elements’ widths set to 100% and a meta tag of:

<meta name="viewport" content="width=device-width; height=device-height; initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

Removing the device width and height from the meta tag will give us the fluid layout we seek:

<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

With the above change, when switching this document loaded in portrait orientation to landscape mode orientation we get a resized document the way we would like:

iPhone Landscape Layout Correctly Resized

iPhone Landscape Layout Correctly Resized

OK, so we have a navbar on the top, but what if we want a toolbar on the bottom? No problem. We can accomplish that with a few adjustments. To fix the toolbar to the bottom we’ll use absolute positioning. One thing to remember about this layout technique, if you are going to have navbars or toolbars, you’ll need to adjust the height of the content so that it fits the space left over by the bars. I do this using a class or class on the content section. A navbar or toolbar has a default height of 45 pixels, so you’d want to position your content that distance from a navbar or toolbar. To make this work, we’re going to have to use absolute positioning on the content section and on any bottom toolbar. One thing rather frustrating about using absolute positioning on block elements is that it causes them to collapse to what their content is, even with they have a width of 100%. We can get around this by setting the appropriate side to 0 pixels. For a bottom toolbar this could be something like this:

.toolbar.bottom {
    height: 45px;
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
}

With the above measurement, we have a toolbar that is 45px high fixed to the bottom and stretching from side to side. This toolbar will maintain these dimensions and placement even with an orientation change. We’ll need to do something similar for the content area. Like I said, we’ll need to use absolute positioning and account for any space taken up by a nav or toolbar. I use classes to do this, such as “withNavbar”, or “withNavbar withBottomToolbar”, or “withBottomToolbar”. The CSS for these classes would be something like this:

/* Definition for content area that fills the screen: */
#content {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}
#content.withNavbar {
    top: 45px;
    right: 0;
    bottom: 0;
    left: 0;
}
#content.withBottomToolbar {
    top: 0;
    right: 0;
    bottom: 45px;
    left: 0;
}
#content.withNavbar.withBottomToolbar {
    top: 45px;
    right: 0;
    bottom: 45px;
    left: 0;
}

We also need to make sure that the document’s content doesn’t bleed down below the bottom toolbar. We do this by wrapping the entire document in a div. I give it an id of #main:

<body>
	<div id="main">
		<div class="navbar">
			<h1>The Title</h1>
		</div>
		<div id="content" class="withNavbar withBottomToolbar">
			<ul class="table-view">
				<li><span class="title">Item One</span></li>
				<li><span class="title">Item Two</span></li>
				<li><span class="title">Item Three</span></li>
				<li><span class="title">Item Four</span></li>
				<li><span class="title">Item Five</span></li>
				<li><span class="title">Item Six</span></li>
				<li><span class="title">Item Seven</span></li>
				<li><span class="title">Item Eight</span></li>
			</ul>
		</div>
		<div class="toolbar placement-bottom">
			<div class="button">Edit</div>
			<div class="button">Save</div>
		</div>
	</div>
</body>

The styles for the div#main would be:

#main {
	width: 100%;
	display: -webkit-box;
	margin: 0 0 0 0;
	padding: 0;
	height: 100%;
	-webkit-box-orient: vertical;
	-webkit-box-align: stretch;
	-webkit-box-sizing: border-box;
	-webkit-transition: all 0.25s  ease-in-out;
	overflow: hidden;
	position: absolute;
	top: 0;
	left: 0;
	bottom: 0;
	right: 0;
}

Although the above arrangement solves the layout problems for mobile interfaces with automatic adjustment with orientation changes, it presents a new problem. That’s the problem of making the data in the content area reachable by the user. You’ll need to add a way for the user to scroll just that area. Normally the whole document would scroll with the browser, with the result that the top part would scroll out of view. Since we’ve fixed the toolbar to the bottom and have the height of the content area control by absolute position affecting its top, right, bottom and left, we need to provide a way to enable scrolling. Actually we could do this very simply using the CSS overflow property: #content { overflow-y: auto; }. However, on mobile Webkit this requires a two finger gesture. Most users would not expect this. They’ll use a single finger and think that the content is not scrollable.

There’s a simple way to provide single finger scrolling with a JavaScript module called “iScroll” created by Matteo Spinelli. This is a great implementation which provides deceleration animation of the scrolling, just like iOS, Android and other mobile OSes. One thing to be aware of when using iScroll, you don’t use it directly on the content container as this would cause that contain to slide up and down over the navbar and toolbar. Instead you’ll need to add an extra div tag inside it and use iScroll on that. iScroll will then enable scrolling of the div inside the content div. There are a number of options, my favorite is for desktop simulation. Here how you would do it:

<div id="content" class="withNavbar withBottomToolbar">
    <div id="scrollwrapper">
        <!-- Content In Here -->
    </div>
</div>
.........

<script type="text/javascript">
    var scroller = new iScroll(’scrollwrapper’, { desktopCompatibility : 
        true });
</script>

This gives you a basic mobile Web interface with a top navbar, a bottom toolbar and a content area with scroll that resizes quickly with orientation change.

Since publishing this article, I’ve created a framework for creating mobile Web apps. It uses many of the techniques explored in my blog posts. You can learn more about the framework by visiting ChocolateChip-UI.com. There are demos you can try in your desktop browser or on your mobile device.

About these ads

Practical Examples of the Flexible Box Model

If there’s one thing in CSS3 that really gives me warm fuzzies, it’s the flexible box model. What it does is provides a way to create many layout affects that otherwise would require workarounds with float or positioning or both. And there are many types of layouts that without out it would require JavaScript to create the same effect. Sadly, at present the flexible box model is only implemented in Firefox, Safari and Chrome. As it turns out, Microsoft’s IE team approached the W3C standards teams with a proposal for a grid-based layout module. It appears to be based on experience implementing the Silverlight grid layout system, which is a good thing. Silverlight grids are a powerful layout tool and is the most important feature for creating interfaces with Silverlight. Because this grid proposal is very solid, the W3C has dropped all further work on the flexible box model and instead are moving ahead in finalizing the draft for the grid layout module. If you read that speck, you’ll realize how much we need a CSS grid system. For now, however, we do have the flexible box model working in Firefox, Chrome and Safari, as well as on mobile Webkit, which includes iOS, Android, WebOS, and Blackberry. This means that if you’re doing mobile development you can use the flexible box model today to help you implement your layout needs. Once you start using the flexible box model, you’ll hate having to go back to situations where it’s not available.

The flexible box model got a lot of attention from a post by Alex Russell back in August 2009. The good thing is that in his post he showed some of the practical things that the flexible box model addresses and provided a stylesheet with a small subset of the flexible box model properties that people could use right away. His post has been replicated and linked to all over the Web. Unfortunately a lot of people who are not familiar with the spec for the flexible box model are under the impression that what’s in his stylesheet is it. So, with that in mind I’m loosing another stylesheet on the public that allows a greater utilization of the flexible box model. I’ll also give you a layout where a lot of these are in use so that you can see how they work.

Before I get going let me say this. The flexible box model is not perfect. It isn’t supported by IE9 or Opera. And now it looks like the W3C is bypassing it for a more robust grid system. Personally, I’d like to see something like the Silverlight grid system combined with aspects of the flexible box model. That would be the best of both approaches. If you’re targeting the mobile Web, in other words you intend on target Webkit browsers, you can expect robust support for the flexible box model. I doubt it’s going to go away just because the W3C doesn’t include it in any recommendation. I expect it to remain in Webkit for the foreseeable future because it’s already been in use for a number of years.

The flexible box model is about the way a collection of child nodes gets laid out in relation to each other and their parent node. To achieve its layout goals, the flexible box model offers properties for the parent node as well as for the child nodes that determine the layout characteristics of the child nodes.

To work with the flexible box model you need to define its display property to box:

#myDiv {
    display: -webkit-box;
    display: -moz-box;
}

Elements with display set to box can order their child nodes in two orientations: vertical or horizontal. If no orientation is provided, the box defaults to vertical:

#myDiv {
    display: -webkit-box;
   -webkit-box-orient: horizontal;
    display: -moz-box;
    -moz-box-orient: horizontal;
}

Besides defining the orientation, you can also designate what direction the child elements stack, either normal or reverse. A box direction of reverse means the elements display in the opposite order in which they physically appear in the document, so that the first would be last and the last first. By default the box direction is normal stacking order.

Then there is the packing order. This allows you to define how the child elements are packed inside the parent. By default they are packed in from the beginning of the parent. For vertical orientation this is the top and for horizontal orientation this is the left. Other packing orders are end, center and justify. If the packing order is end, for a vertical orientation the elements will stack up from the bottom. For a box with horizontal alignment, the boxes will stack from the right. A packing order of center means that for vertical orientation the child elements will be centered vertical with any left over space displayed equally at the top and bottom. For horizontal orientation a packing order of center means the elements are centered horizontally with left over space equally divided on the left and right. A packing order of justified means that any available space is spread equally between the child elements, for vertical orientation this is vertical spacing, and for horizontal orientation this is horizontal spacing.

Box-align-start

A box with alignment set to "start"

box-align-end

A box with alignment set to "end"

box-align-center

A box with alignment set to "center"

And elements can have their box alignment defined. The default is stretch. This stretches the dimensions of the child elements to fill the parent box. If the alignment is start, for horizontal orientation the elements are aligned to the top of the parent. If the orientation is vertical, they are aligned to the left of the parent. An alignment of end will align elements with horizontal alignment to the bottom of the parent. With vertical orientation they will be aligned to the right of the parent. An alignment of center will align horizontal elements along the horizontal center of the parent and for vertical orientation it will center them along the vertical center of the parent. There is also an alignment of baseline, but this is only for horizontal orientation. It aligns the elements along their horizontal baseline.

Then there is a set of properties that you can define on the child elements. These are flex and ordinal group. The flex property tells the browser how to deal with the dimensions of the child element. If you have three child elements and they don’t fill their parent, giving one of the a value of flex:1 will cause that element to take up all the left over space. If you gave one element flex: 1 and another one flex: 2, the available space would be divided up such that the element with flex: 1 would get one third of the available space and the element with flex: 2 would get two thirds of the available space. Of course the element with no flex value would default to whatever its width is. The ordinal group property allows you to designate groups of elements so that they appear in a different order than their document order. I’m not so sure about the practical use for this property. I’ve thought about it for months and have not been able to come up with a use case where I would need it. But it’s there.

I’ve created a flexible box model stylesheet with classes for all the various box properties. This means you can add the classes to an element to build out the definition you need for an element. I’ve also put together a interactive test case where you can dynamically toggle a number of the box properties and see their effect in real time. You can try it out online if you’re using Firefox, Chrome or Safari, or you can download the working example to dissect and learn.

Follow

Get every new post delivered to your Inbox.

Join 46 other followers