iPhone App Layout with CSS3 box-flex on mobile webkit (jqtouch)

Introduction

CSS3 provides a much needed improvement on the box model: flex-box: it fills a void left by CSS2 by providing a better way to do elastic layout, in which some part of the HTML expands to fill leftover space.

The best tutorial of flex-box can be found at Mozilla's site: http://hacks.mozilla.org/2010/04/the-css-3-flexible-box-model/.

>

Behaviors are similar in Mozilla and WebKit implementations. Although we are online concerned with mobile WebKit here, it’s a good start. It should be relatively easy to port for your own purpose.

iPhone App Layout

To support navbar (available to BeeDesk jQTouch's forks), landscape layout, and iPad split screen, reworking jQTouch's theme using flex-box makes sense. It doesn't hurt that it works better on a desktop screen.

Before the change we used javascript to calculate height for the flex <div> triggered by orientation change and page events. It is bug-prone, not always reliable, and noticeably slower than CSS.

Layout

The layout generally contains three parts: toolbar (fixed), tabbar (fixed), and content (flexible).

screenshot of iphone

Achieving backward compatibility was tricky, and it supports content wrapper, which is used to support scrolling (not available in the official fork).

That jQTouch doesn't have a class name for the flex part also adds some work.

To get boxflex starts, three markups need to be added:

  1. on container: { display: -webkit-flex-box; }
  2. on each flex children of the container: { display: -webkit-flex-box; }
  3. non-zero value on each flex children of the container: { -webkit-box-flex: 10; }

For our vertical layout, we add these lines to the container:

{
    -webkit-box-sizing: border-box;
    -webkit-box-orient: vertical;
    -webkit-box-pack: start;
    -webkit-box-align: stretch;
}

Using { display: block; } as children of { display: -webkit-flex-box }

With WebKit the default layout of the "children of the {display: webkit-flex-box;}" behaves differently than "children of {display: block;}". Content of the children does not span the width of the flex box. To compensate we need to add these lines to the children:

{
    display: block;
    top:0;
    left
:0;
    right:0;
}

The { left:0; right:0; } markup causes the content to span the entire width.

On the other hand, using { width: 100%; } would not work, because width does not compensate for the margin, and causes content to overflow to the right.

Markup { width: ?px; } won't work either, because it is not flexible.

No name for content

Because jqt didn't give a name for the flex part, we have to resolve to a series of :not() to avoid affecting the toolbar, navbar, and tabbar. This is is how it looks:

#jqt > * > div:not(.contentwrap):not(.navibar):not(.toolbar):not(.bar):not(.info)

Support content wrapper

The rest of the complexity comes from the need to support a variety of content. For example:

<body id="jqt">
  <div id="var1"><!-- simple case -->
     <div class="toolbar"></div>
     <div class="">Content</div>
     <div class="navibar"></div>
  </div>

  <div id="var2"><!-- simple case with .info -->
     <div class="toolbar"></div>
     <div class="info">Warning: this is a warning to warn you that you're warned.</div>
     <div class="">Content</div>
     <div class="navibar"></div>
  </div>

  <div id="var3"><!-- scroll pane case -->
     <div class="toolbar"></div>
     <div class="contentwrap">
       <div>Content</div>
     </div>
     <div class="navibar"></div>
  </div>

  <div id="var4"><!-- modified scroll pane case -->
     <div class="toolbar"></div>
     <div class="contentwrap">
       <div class="afloat_header"></div>
       <!-- must specify .content if it is not the first element -->
       <div class="content">Content</div>
     </div>
     <div class="navibar"></div>
  </div>
</body>

310 responses
This is great. I have been using your branch for a while in combination with the zombie dude and I did not realize how much simpler my setHeight function could be for this nifty iScroll. Thanks for all the hard work.
Luke, thanks for the encouragement. Glad that you find it useful.
I can't seem to figure out for the life of me how to have headers that don't scroll with the data. The toolbar stays on the screen just fine but I can't figure out where to put the headers so that they don't scroll and don't stretch to take up half the screen. Sorry for the noob question.
I hope I understood you correctly. If I am, then there are a few ways to do it.

Give the "header" a class, say "listheader" and style it as "display: block", or "-webkit-box-flex: 0". Put it right after the toolbar. Put a class "contentwrap" to the scroller, and "content" to the div. (was trying to show you the html code, but it was too hard to do it as a comment).

Hope it helps.

306 visitors upvoted this post.