Skip to content

Instantly share code, notes, and snippets.

@spion
Last active December 10, 2015 19:58
Show Gist options
  • Save spion/4484746 to your computer and use it in GitHub Desktop.
Save spion/4484746 to your computer and use it in GitHub Desktop.
Layout CSS for web apps

Mobile layout

Mobile apps usually want a static header/footer around scrolling content. You have multiple options. Two of them suck:

iScroll and other JS-based solutions

Flexible but have random performance characteristics.

On some versions of Android with some kinds of content they're unbearably slow - and this is not because Android doesn't have requestAnimationFrame; depending on the content it can go below 1 frame per second.

What causes this slowness? Various things, such as using vector fonts (fontawesome), box-shadow or even 20 2x5 tables or 20 definition lists slow it down to a crawl - and it seems like more and more turn up every day. This renders the solution unusable (pun intended)

Does it ever work well? - It's great when all you're rendering are images (it works very well for maps)

Overflow scroll

Android 4.1+ only (in 4.0 it works but doesn't send events to the right coordinates).

The performance can also fluctuate depending on the content, just like with iScroll (also below 1 fps, making it unusable)

Position fixed

This one sucks much less. Use position:fixed for the toolbar and bottom bar. Use margin-top and margin-bottom with the sizes of the toolbar and the bottom bar for the main content container.

Аdvantages

  • compatible with Android 2.3+, iOS 5+
  • smooth scrolling all the time - as smooth as regular page scrolling anyway

Disadvantages

  • you can only have one visible srollable element per page (because the entire page scrolls).
  • the scrollbar doesn't match the scrollable area. You can use webView.setScrollbarStyle to remove them though.
  • on iOS, the fixed position elements "vibrates" as you scroll

Code

#parent > .toolbar {
    position: fixed;
    top:0; left:0; right:0;
    height: @toolbarHeight;
}

#parent > .bottombar {
    position: fixed;
    bottom:0; left:0; right:0;
    height: @bottombarHeight;
}

#parent > .content {
    margin-top: @toolbarHeight;
    margin-bottom: @bottombarHeight;
}

Desktop layout

Regular single page web apps are more similar to classic web pages. However, single-page web apps might have one or more toolbars, sidebars, tab bars + additional automatically resizing full screen content.

The main pain-point is displaying multiple elements inside a single parent where one or more of the elements have fixed height while the other uses up the remaining space. Examples where this happens:

  • Tabs - fixed size tab bar, variable-size tab contents. The tabbable area should not change size when tabs are changed.
  • Dialogs - fixed-size title and fixed-size button footer, variable-size contents.
  • toolbar + statusbar + panel on both the left and the right side and variable content in the middle.

Note how the last is very typical for desktop apps - but how the heck do you do this on the web?

A bad solution:

  • hard-coded dimension properties for the content container - You will need to recalculate them all the time when the content changes or window size changes.

Solution 1 - adaptable-size content

Advantage

Flexible: Automatic scaling of the parent can be preserved or discarded. Fixed or variable width content. Centered or uncentered content.

Disadvantage

If you need the content container to take up all the remaining available height, you're out of luck.

For example, you might want this for the tabs: you don't want their borders to just end at the middle of the parent if the content is short. But you don't want to put the borders on the container either because then the tabs don't look like tabs anymore. Like jQuery UI tabs, the tabs that make absolutely no visual sense.

Code

#parent {
    position:relative;
    width: 100%;
    height: 100%;
}
#parent > .toolbar { 
    position: absolute;
    top:0; left:0; right:0; 
    height: @toolbarHeight;
}
#parent > .statusbar {
    position:absolute;
    bottom:0; left: 0; right:0;
    height: @statusbarHeight;
}
#parent > .leftSidebar {
    position:absolute;
    left: 0;
    top: @toolbarHeight;
    bottom: @statusbarHeight;
    width: @leftSidebarWidth;
}
#parent > .content {
    padding: @toolbarHeight @leftSidebarWidth @statusbarHeight 0;
    
    /* For fixed-width content, add: */
    display:inline-block;
    width: @contentWidth;
    
    /* To center fixed-width content, add: */
    margin: 0 auto; 
}

The padding guarantees the minimum offset of the content, while the margin guarantees that the content is centered horizontally. The content container is allowed to auto-grow according to the content we put inside it.

Solution 2 - for full-size content container

The container has position:relative. All the inner elements use position:absolute.

Control the content element boundaries with top, bottom, left and right properties.

The CSS is the same as the previous example with the following changes for the content container:

#parent > .content {
    position:absolute;
    top: @toolbarHeight;
    left: @leftSidebarWidth;
    bottom: @statusbarHeight;
    right: 0;
}

The key here is having both left and right (or both top and bottom) style properties. If the height of the container changes, the height of the .content will also change to satisfy both top and bottom properties at the same time. Similarly if the width of the container changes, the width of .content also changes to satisfy both left and right properties.

This solution is mostly used when you want the content container to always take up all the remaining available height.

Box sizing model

Have you ever been frustrated that if you use

someelement {
    width: 100%;
    padding: 1em;
    border: solid 1px #ccc;
}

Your dear browser will render the element exactly 2px + 2em wider than 100% ?

Have you ever wondered why the heck isn't the border and padding included in the element dimensions?

Be frustrated no longer! Change the incredibly misguided css box sizing model to something much saner:

* { box-sizing: border-box; }

Like magic, someelement now fits inside his parent perfectly. The width now includes padding and border. Because really, it does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment