A simple CSS approach for building traditional desktop UI interfaces in the browser
Many articles cover the subject of building and styling modern and responsive web pages but here I present a few simple CSS techniques for dividing up a web page to allow for quick layout of the kind of interfaces found in desktop applications.
Desktop user interfaces
The user interfaces within desktop applications are designed to show and arrange lots of information in a very different way to web pages.
We see them today commonly as developers or designers. Integrated development environments (IDEs) like Visual Studio or Eclipse or popular applications like Photoshop and Illustrator commonly make use of docked scrollable areas and have common elements like menu bars and toolbars.
Web applications demand a similar interface
Web pages in contrast are traditionally a medium for displaying content of any vertical length which fills the width of the browser window. Importantly when the content extends beyond the height of the browser window it gives you the ability to see the rest of the document by scrolling.
Suddenly we expect more from a browser, trying to replicate elements from a desktop application interface.
Dividing down the UI into areas
If we look closer at the Visual Studio interface as an example, there is a definite subdivision of the window into rectangular areas:
Even within one of these areas, the subdivision continues:
The approach below describes a way to represent these divisions within a browser window.
Taking control of the vertical plane
Using HTML 5 with CSS 3 it is entirely possible to recreate the rich desktop UI layouts we are used to seeing in desktop applications. However, achieving this as a web developer is not so easy especially if you approach the problem with traditional methods of styling and laying out a web page.
To describe the problem, let’s look at this example:
That’s a disappointment. The height seemingly takes no effect. But as per the CSS spec for height, specifically when you give it percentage size, this is to be expected. The relevant part of the height spec for percentage values is:
If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the value computes to auto.
To explain this, it’s a common misconception that the default height of a web page (more so the height of the body element) is the height of a browser window. By default this is not the case — the height is determined by whatever it contains which fits the notion that by default a web page is a variable height medium. The browser window (or commonly viewport) is merely a window on to the page. If the body element exceeds the viewport you get scrollbars to be able to see other parts of it.
To allow the percentage height to work for the div element we need to give the containing body element a height:
However it would not be appropriate to assume that everyone has a browser window 500px high. To get around this an alternative way is to make the body element fixed to the size of the viewport by doing the following:
Now we have a body element that matches the dimensions of the viewport both horizontally and vertically, a starting point that very much matches a desktop window. From this point onward we can now effectively say 50% height is 50% of the browser window.
Note, as a side effect of doing this we have lost the ability to scroll content directly if it overflows the current height of the browser window. Any content that overflows is effectively hidden outside the viewport and no scrollbar will be shown. However, I will revisit this in particular later.
Introducing anchored elements
Now we have a web page sized to the dimensions of the viewport, I’m going to introduce a few foundational building blocks which are not too dissimilar to components and options available when building desktop UIs. These will make dividing a web page into areas such as those found in a desktop application user interface much easier.
- Viewport area — matches the size of the browser viewport (as described above).
- Left, right anchored areas — Creates an area on the left or right side of the parent. Should be given a width.
- Top, bottom anchored areas — Creates an area on the top or bottom side of the parent. Should be given a height.
- Fill — An area anchored on every side which should take up the remaining area within the parent avoiding any anchored areas by giving it a left, right, top or bottom offset matching the neighbouring area width or height.
First we can setup a few reusable CSS classes representing the default styling for these items:
There are a few important resets here:
- Ensure elements used for our areas do not have any margins / paddings by default.
- box-sizing: border-box ensures that if we do add margins or borders to areas, this does not affect the overall size of our areas.
- overflow: hidden ensures that any content does not overflow the area into neighbouring areas.
A simple example usage is:
In this example above we know the header element is 20% high, so we can offset the main element 20% from the top so that it sits below the header. Resizing the browser window shows that this division of area remains proportional because we are using percentages.
We can equally use fixed units. If we gave the header element a height of 50px and the main element a top offset of 50px, resizing the browser windows results in a fixed size area at the top, and a resizable area filling the rest of the space.
What’s more, this experience is totally repeatable by adding nested child areas. The parent element simply becomes the new boundary for the child areas:
Note the use of the scrollable class to bring back scrolling of overflowed content to only one particular area on the page.
We can equally add a right anchored element and introduce a bit more nesting to allow a side pane to be created next to the main element:
With a few reusable CSS classes we now have the following:
- A web page that behaves as a desktop window when it’s body element is given the viewport class.
- A repeatable and nestable way of adding anchored areas to any side of a parent element to enable a webpage to be divided and respond well to resizing of the browser window.
- The ability to make any single area scrollable by giving it a scrollable class.
The examples presented above use very simple nesting to achieve general layout of headers, side panes etc. However, you could continue nesting child elements to achieve UI furniture such as top anchored title bars or right anchored close and minimise buttons within that title bar.
The idea is that using these foundational nestable elements, you can sub-divide a web page into areas which can then be filled with visual elements/components to complete the UI.
The approach described here is a very manual approach to building UI using just HTML and CSS. All the concepts presented are the foundation of my React layout library, React Spaces, which uses these basics and adds much more functionality such as resizability of areas and removes a lot of the manual calculation of offsets needed for neighbouring elements to be placed correctly beside each other.