Rule of thumb - if you're messing with CSS too much in your layouts, switch to a framework. I've been going over the dozens of grid/layout frameworks out there and most of them focus on the traditional document grid layout.
My page is more of an SPA (single page application). It resembles the layout used by a desktop application. Obviously HTML doesn't handle this very well and a lot of CSS tinkering is required.
Here's an example of a layout I'm trying to achieve (this should fill the entire screen):
Please note that the fixed menus shouldn't be hovering over the scrolling content behind them. Each block should reside in its own separate "frame" - ie the scrollbars for the long list on the left should appear below the fixed menu.
I haven't been able to find a lightweight grid framework which supports this (frameworks like Sencha or Kendo UI are a bit of an overkill). Doing the CSS manually is very laborious. Especially if I decide to show a modal dialog box, and require similar layout inside this dialog box (this is btw what killed me eventually). Or if I decide I want another fixed menu on the bottom, all hell breaks loose.
I'm not even sure this is possible with CSS only (I know at least fixed bottom menus require some hard-coded values in the CSS in order to shrink the content behind it so the scrollbars don't overlap). If a JS solution is required, and I want it to handle browser screen resizing events - again all hell breaks loose as there's no easy common event that does that.
How should I approach this in a maintainable/flexible way?
COMMENT: As you'll notice, I've added an answer myself suggesting to implement this layout using HTML tables. I'm not really looking to accept my own answer. I had a feeling nobody would be "brave" enough to suggest tables (it's controversial after all) and I was willing to suffer the downvotes for the sake of discussion
CLARIFICATIONS: It seems I wasn't clear enough when saying maintainable and flexible. What I mean is having the layout deal gracefully (ie with little work on my behalf) with various scenarios or required modifications. To be absolutely specific, let's mention some examples:
The layout should not mind if it's displayed as a full page or inside a fixed modal dialog (ie the bootstrap dialog). Switching between these scenarios should change the layout code as little as possible.
The layout should support a multitude of ways to describe widths and heights (ie the width of a side bar or the height of the top menu). More specifically, the following sizing methods should be supported:
As Ian Clark most expertly pointed out, CSS can achieve most of these requests out of the box. There's no argument about that. The question remains if raw CSS is the simplest to use and if there's anything that can be off loaded from the developer by a framework / another mechanism like flexbox / tables.
Update 2 (This question seems to evolve!):
Criteria 1 - Switching between full page or inside modal should change the code as little as possible:
Because in this case the width/height of our body
is always 100% of the window
width/height, if you want your code to be easily adaptable to modals etc., you can always use position:absolute
, as without any context this will be relative to the body
.
Criteria 2 - The layout should support a multitude of ways to describe width and heights
Here's a JSFiddle that can do almost everything you mention. It uses both percentage and fixed pixel widths on the fixed elements. The first thing to note is that the only change I've had to do is add a box-sizing:border-box
to each of our elements. Unlike flexbox, this has good support going back to IE8, and so shouldn't cause any issues. Adding this allows us to add padding to our elements without having to change widths to compensate.
Whilst the left sidebars have a width of 20%, they also have a minimum width of 100 pixels. To prevent the overlap with the main content to the right, we use some simple logic to establish the inverse:
Reaches Min At = Min-width / Width * 100
In our example, our left sidebar therefore reaches a min at 100/20*100 = 500px
. Then we use an @media
selector to change our main content's width when we reach this window size:
@media(max-width:500px) {
#scrolling-content {
left:100px;
}
}
By changing the window size on the fiddle, you'll notice that our main content area changes colour when you reach the min-size. Now I admit, this might seem confusing, but again, by using a language such as Less you could easily set the width
and min-widths
as variables, and use a simple function to change your CSS. Now the reason I used the phrase "almost everything" is because this could get much more complex if you wanted to do a similar thing in a modal, but who knows, it could be possible!
A little thing to take away: If you're ever going to use JavaScript to pick up resize events, definitely consider the jQuery debounced resize plugin as you don't want to be doing calculations hundreds of times more than you need to! I'm leaving the extra content in this answer as most is still relevant and historically accurate to your developing question
Update: There's a lot of answers suggesting using Flexboxs, but I really don't think that's a great solution at the minute. Don't get me wrong, I'd love it if FlexBox's were a really viable solution, but at the minute Support for them is terrible, and if you're going to polyfill it for the majority of the web then, you might as well be using any other JavaScript solution, and I don't think JavaScript is the right tool to use for the entire presentation of your page.
I still don't see why your goals are hard to achieve using simple CSS position
s. You don't need to use any JavaScript and. I'd argue that it is very manageable. If you're really struggling with maintaining your stylesheets, then why not use something like Sass/SCSS or Less, as you could then create variables, mixins, functions etc., and significantly reduce the number of changes you need to make.
That said, he's a really quick mockup of your solution, working using just pure CSS and HTML. All I'm using is 4 fixed div
s, and a simple modification could be made so that the "scrolling content" is not fixed, but just uses margins to be positioned correctly. That would have the added bonus of not having to scroll within the div, but being able to do so against the body.
All the required CSS:
div { position:fixed; }
#top {
top:0;
left:0;
right:0;
height:40px;
}
#menu {
top:40px;
height:40px;
left:0;
width:200px;
}
#long-list {
top:80px;
left:0;
width:200px;
bottom:0;
overflow-y:auto;
}
#scrolling-content {
top:40px;
bottom:0;
right:0;
left:200px;
overflow-y:auto;
}
It's pretty easy to note the repetition of 40px
and 200px
as the height and width of the menus. With that in mind, you could simply use a dynamic language's variable and define them only once (Less example):
@leftwidth: 200px;
#scrolling-content {
left:@leftwidth;
}
#menu, #longlist {
width:@leftwidth;
}
You mentioned that it's difficult to easily manipulate CSS frameworks, as relying on static content, they have to make decisions over the number of columns to use (in Bootstrap and many other cases they choose 12 as it's easily divisible by 1,2,3,4,6). However, in Bootstrap 2, it's very easy to customise the number of columns. Expect this feature to also exist when Bootstrap 3 RC2 comes out. There are also many resources which allow you to use Less with Bootstrap. That said, it would be fairly routine to create a Less-style flexible fluid-grid system, see demo (Disclaimer: I have no experience with Less, this article helped me with looping):
/* The total number of columns you wish to use */
@columns: 15;
/*
We loop through and for each create class
.dynamic-<index>-<columns>
*/
.looping (@index) when (@index > 0) {
(~".dynamic-@{index}-@{columns}") {
width: percentage(@index/@columns);
}
.looping(@index - 1);
}
/* Stop and do nothing at 0 */
.looping (0) {}
/* Call our function */
.looping (@columns);
And then your HTML markup simply becomes:
<div class="cf rows">
<div class="dynamic-4-15">4/15th width</div>
<div class="dynamic-7-15">7/15th width</div>
</div>
In the comments you mentioned that you "would want the layout to be relative to the dialog width, not the browser window width". Well that's certainly possible too. In fact, it's really easy, and requires almost no change to our code:
#some-modal {
position:fixed;
top:10%;
bottom:10%;
left:10%;
right:10%;
}
/* Before this was simply div, and it was fixed, now it's absolute */
#some-modal div {
position:absolute;
padding:10px;
}
I don't know why you have been down voted on your answer. Everyone seems to agree that "There's no need for a specialized framework" is at least part of a good answer. Of course there are disadvantages in using table, but you seem to be aware of it, and listing the downsides made a very consistent answer in my opinion.
Despite of considering your answer, I would work with a Flexbox model. It seems more appropriate (as very well put by @cimmanon in another answer), semantic and even easier to maintain than tables. Besides, using up to date techniques may extend the life time of your application. Have a look at this library if your concern is backwards compatibility - http://flexiejs.com/
You can also simulate a table's behavior using other elements and a css Table model like: <div style="display:table">
. Take this fiddle as example - http://jsfiddle.net/JWqVQ/
Here is a bit more about different rendering options - https://developer.mozilla.org/en-US/docs/Web/CSS/display
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With