Site Meter

Full-height app layouts: A CSS trick to make it easier

In HTML layouts, there’s a fundamental difference between width and height. The natural state of affairs is that your page’s width is constrained to the width of the browser window, but its height is unconstrained – it grows however tall is necessary to contain its contents.


That makes sense for documents, but it’s a pain for application UIs. Native application UIs tend to slice up the screen both horizontally and vertically into a nested set of panels, some of which may scroll/resize, others being docked against particular edges of their parent panes.


So, what’s the robust way, with HTML/CSS, to set up a nested collection of panes that exactly divide both the width and height of the browser window?

Hang on, didn’t we have this one solved back in 1999?

Hmm… this reminds me of something… I remember: the <frameset> tag from HTML 4. Yes, HTML frames do divide the browser window both horizontally and vertically, exactly consuming the available screen area. So why don’t we use them any more? There are loads of reasons, including:

  • What you’re building is logically one page, but technically each frame is a separate HTML document, which makes interactions between them so much more complex
  • It’s not really practical to support deep-linking to (i.e., bookmarking) particular UI states
  • Mobile devices and tablets have very limited support for HTML 4 frames. iOS in particular requires the user to use two-fingered scrolling within panes. This is UX death.

OK, so what’s the 21st century solution?

I’ve used lots of hacks and tricks over the years to get columns and panes into my web applications, often involving JavaScript, $(…).height(…), window.body.clientHeight, and the onresize event. Ugh. Fragile and messy. But at long last this week I learned there is actually an elegant and robust way to set up nested exact-height panes with pure CSS, and it works on all browsers back to IE 7, even on current mobile browsers that don’t support position:fixed.

You know that if you set position: absolute on an element, then you can make it appear at a specified distance from the top, left, right, or bottom from its parent element. Well, it turns out that you can specify both top and bottom, or both left and right, and then it will dock against both edges and always resize to match its parent’s dimensions.

Let’s define some generic CSS rules for panes:

/* Generic pane rules */
body { margin: 0 }
.row, .col { overflow: hidden; position: absolute; }
.row { left: 0; right: 0; }
.col { top: 0; bottom: 0; }
.scroll-x { overflow-x: auto; }
.scroll-y { overflow-y: auto; }

Now it’s easy to set up a fixed-height header, variable-height body, and fixed-height footer:

    <div class="header row">
        <h2>My header</h2>
    <div class="body row scroll-y">
        The body
    <div class="footer row">
        My footer

Of course, you also have to configure the heights and positions of the three rows by using a bit more CSS:

.header.row { height: 75px; top: 0; }
.body.row { top: 75px; bottom: 50px; }
.footer.row { height: 50px; bottom: 0; }

The result? It’s the classic mobile phone app layout. Screenshot with some colours and content added for interest:


Try it in your desktop or mobile browser here. Works on IE7+.

More nesting (or, tablet-style layouts)

The reason for setting up the generic CSS rules in that way is that it makes it easy nest panes arbitrarily. For example, you could split the main viewport into two vertical columns:

    <div class="left col">
        Left col here    
    <div class="right col">
        Right col here

… and then subdivide the right column so it has a fixed header, variable-height body, and fixed footer, just by putting some more row divs inside it:

<div class="right col">
    <div class="header row">
        View or edit something
    <div class="body row scroll-y">
        Here’s some content that can scroll vertically
    <div class="footer row">
        Some status message here

Here’s the CSS to configure the widths/heights/positions of those panes:

.left.col { width: 250px; }
.right.col { left: 250px; right: 0; }
.header.row { height: 75px; line-height: 75px; }
.body.row { top: 75px; bottom: 50px; }
.footer.row { height: 50px; bottom: 0; line-height: 50px; }

Going further, in the right-hand pane’s body, you could also nest a horizontally-scrollable row:

<div class="body row scroll-y">
    <p>Here's a horizontally-scrollable thing:</p>
    <div class="scroll-x">
        Any content in here, if it's too wide, becomes independently scrollable
    <p>That's enough - bye.</p>

The result of all this? Well, it’s a structure like this:


… but that’s pretty boring to look at, so here’s a version where I threw in a rough effort of some iPad-like styling:


Here’s a live example. It still renders correctly on very small screens (like a WP7 or iPhone) but this 2-column layout really needs a wider screen to be usable.

Enabling touch scrolling

The scrolling looks and works fine on a desktop browser, but on a mobile browser it varies:

  • On WP7, you’ll see no scrollbars, but you’ll get one-finger touch scrolling, without the lovely inertia/momentum effect. This is kind-of OK, though imperfect.
  • On iOS, you’ll see no scrollbars, and it requires two-finger scrolling (which is horrible), and it won’t use the inertia/momentum effect either. Badness.
  • There are similar problems on Android

With iOS 5, it will be possible to enable fluid, native touch-based momentum scrolling to our divs just by making a tiny tweak to the CSS, thanks to the new “touch scrolling” feature:

.scroll-x, .scroll-y { -webkit-overflow-scrolling: touch; }

I really hope this catches on and becomes a standard.

But in the meantime, for visitors on other mobile OSes, and until iOS 5 becomes prevalent, you can use the open-source iScroll library that provides one-finger momentum scrolling for Webkit (iOS and Android) and Mozilla browsers.

Enabling momentum scrolling on any given element requires only one line of JavaScript (assuming you’ve referenced iScroll.js):

new iScroll(theElementYouWantToEnableItFor);

For my example, I used the following block of JavaScript, which enables touch scrolling on all the .scroll-x and .scroll-y elements:

<!-- Touch scrolling -->
<!--[if !IE]><!-->
<script src="script/iscroll.js" type="text/javascript"></script>
<script type="text/javascript">
    var xScrollers = document.getElementsByClassName("scroll-x");
    for (var i = 0; i < xScrollers.length; i++)
        new iScroll(xScrollers[i], { vScroll: false });
    var yScrollers = document.getElementsByClassName("scroll-y");
    for (var i = 0; i < yScrollers.length; i++)
        new iScroll(yScrollers[i], { hScroll: false });                

You could do this in fewer lines if you’re using a library like jQuery or XUI (which is a tiny implementation of a small part of the jQuery API surface, intended for mobiles). Here’s the resulting mobile-style scrollbar:


Of course, to see the momentum effect, you’ll need to run it in your Chrome/Safari/Firefox browser.

Credits: Thanks to Rob Swan and FellowshipTech for their articles and projects where I found the CSS positioning trick that underlies this approach to exact-height layout.

19 Responses to Full-height app layouts: A CSS trick to make it easier

  1. Lucian Maran

    Hello, I like your ideia…it’s clean and smart, but what about an open source JQuery library like
    I used it (only 33k, free) for my demo project with fixed height, autoresize, collapsible and cross-browser layout.
    You can skip over my text (sorry for the language) and go directly to the LiveDemo link.

    Anyway, I will bookmark your post for the touch-scrolling technique.
    Thank you!

  2. This is great! Using clean CSS to make the UI more app like is a great way to go. Much more elegant than using JavaScript or some ugly CSS hack.

    Thanks for this. I’m going to be using it.

  3. Steve

    FYI: I’m getting quite the ‘bouncing’ going on in chrome with those test pages

  4. Application-style UI layout is the current major failing of CSS. I’m keenly watching the various new CSS3 modules (e.g. ) My hope is that once a few modern browsers implement them, someone will create a decent JavaScript implementation to back port to old browsers.

    For now though, position: absolute seems like the least worst option. I’m certainly making use of it in my apps.

  5. Eliman

    Great Steve, very useful! A problem I was trying to solve differently

  6. Ian

    iScroll is really cool, but I’ve found it starts to break down a bit when its contents resize or it has several form elements that can receive focus.

    That said, for many cases, it works great.

  7. dencio

    very usefull. thanks steve.

  8. Pingback: Full-height app layouts: Animated transitions within panes - Steve Sanderson’s blog - As seen on YouTube™

  9. Sander

    Looks like somthing I need or not, I am new to css and I am trying to create 3 equal height colums (eg. the longest column should determine the height of all the colums). I do not want my footer to be fixed/always visible and have the main content scroll as in your example.

    Is it possible to create such a layout with the techniqe you mention? or do I need faux-colums or any of the other trick I read about elsewhere.


  10. Great post. Have you experimented with media queries? Couldn’t that make it even more flexible? Like having one version for all devices and then have the UI to adjust to the screen size. The navigation panel could for example go on top if the screen size is smaller than on a tablet, ot if take up to much space you could add some funky javascript to create a drop down of the panel.

  11. Nice article, I’ve used a similar technique in the past for creating mobile layouts.

    More recently however I’ve started to use the new flexbox module of CSS3, I think it provides a much more flexible/extensible UI:

  12. Pingback: Full-height app layouts: Navigation and History - Steve Sanderson’s blog - As seen on YouTube™

  13. Steve G.

    Very nice – this was exactly what I didn’t know I was looking for! I re-did my full panel app using this and it greatly simplified my code. Here’s a nifty extension: making inner panes resizable. Let’s say you have 2 main content pane divs ‘A’ and ‘C’ separated by a slim gutter div ‘B’. E.g.:


    The key is to make B draggable, and dynamically resize A and C. What’s nice about this layout technique is that it becomes really simple. Using JQuery’s Draggable extension, the code looks like:

    drag: function (obj, ui) {

    var prevright = $(this).parent().width() – ui.position.left;
    $(this).prev().css(“right”, prevright);

    var nextwidth = prevright – $(this).width();
    $(this).next().css(“width”, nextwidth);
    stop: function () {

    I also use the styles “{ cursor: e-resize; width: 6px; }” and the class “col” from this article to style the gutter div, B. That’s it.

    This sample creates a vertical, draggable bar that will allow your users to dynamically resize divs ‘A’ and ‘C’. The horizontal version is left as an exercise for the reader. ;-)

    Steve G.

  14. Corey

    You are a champion Steve. I didn’t think it should be that hard to get divs to play nicely, but up until now I was having limited success. Your solution worked a treat, tested in chrome(15.0), ie(8.0), firefox(8.0) and safari(5.1.1) thanks heaps.

  15. Wow this is wonderful, really saved my day and is exactly what I needed! Bookmarked and shared, sir!

  16. We are a bunch of volunteers and opening a brand new scheme in our community. Your web site provided us with valuable information to paintings on. You have done an impressive task and our whole group will likely be grateful to you.

  17. luigi

    Hi, it’s great but I wonder how to improve the “bouncing issue” (like Steve found).
    I am getting page bouncing on chrome and firefox, not safari.
    Any idea?

  18. Arthur

    Does not work on Android!!! RRRR… Position absolute + overflow auto – the ancient issue of android!

  19. Martin Andersen

    Very nice and very useful

    Is there a way to create a body with 3 columns? basically the same layout as in the demo with a extra center column.

    left fixed width
    center “100%” width
    right fixed width

    center: fill