6

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): enter image description here

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:

  1. 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.

  2. 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:

    • Exact fixed pixel size.
    • Relative sizing by using a percentage of the container. For example: side bar width is 30% of the width of the entire container (full page or dialog). Another example: top menu height is 20% Of the entire height of the container (full page or dialog).
    • Wrap around its own content. Example: the height of the top menu will depend on the content inside it. If you enter one line of text, it will wrap it without scrolling. If you enter two lines of text, it will be twice as big and hold them without scrolling.
    • BONUS: A simple combination of two. Like having a sidebar width of 20% but no less than 50 pixels.

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.

talkol
  • 12,564
  • 11
  • 54
  • 64
  • this is really tough to achieve using fixed layouts to create a dynamic grid. Will the page be constantly changing or once you publish it then the layout is done? – samrap Jul 30 '13 at 09:17
  • The layout is pretty constant - it will not change during runtime. Problem is I have several screens with similar layouts and doing all the manual calculations and CSS tricks for each case feels like a big waste of time – talkol Jul 30 '13 at 09:42
  • Oh I see. I was gonna say if it's only one page, spend time figuring out everything that you're going to need, write out a list of it and then build your page with fixed layouts so you only have to do it once...but if you have multiple variations then yea hopefully you can find an alternative. Good luck! – samrap Jul 30 '13 at 09:55
  • Are you asking for a library to do handle layouts in general or are you trying to create this specific layout? If it is the former, the question is off topic for SO. – cimmanon Aug 06 '13 at 01:22
  • I'm asking how to implement this layout in particular, but adding a requirement for it to be done with a library and not manually. I'm looking for a maintainable solution, not just something dirty that works – talkol Aug 06 '13 at 01:34
  • CSS is dirty by nature, and most frameworks are flawed because they make too many assumptions (or the wrong kinds of assumptions). – cimmanon Aug 06 '13 at 01:52
  • I actually like and enjoy frameworks like bootstrap which fiddle with the CSS specifics for me and give me simple encapsulating classes – talkol Aug 06 '13 at 01:54
  • Ian's answer is correct, or your question needs to be refined further. – smcjones Aug 08 '13 at 17:00
  • I don't understand why Ian's answer would be incorrect. Also, I would recommend using a CSS preprocessor like LESS or SCSS to keep things nice and tidy (there are even IDE-ish offline converters for LESS/SCSS). – arothuis Aug 08 '13 at 19:30

9 Answers9

8

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 positions. 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 divs, 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;
}

Dispute #1: Frameworks aren't flexible or easy to edit

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>

Dispute #2: Percentage-based modals are difficult using CSS

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;
}
Ian Clark
  • 9,237
  • 4
  • 32
  • 49
  • The problem with this CSS is that it's quite inflexible. Many grid frameworks offer widths which are relative to the entire screen (like n/12 columns). Another issue is showing a modal dialog which has different sizes on different screens and having this layout in the dialog. Anywhere you can't use exact pixel sizes pretty much breaks.. – talkol Aug 04 '13 at 19:41
  • Another problem is wanting the menu div to be big enough to hold its content. Meaning that if the user has a different base font size, for example, it will grow or shrink accordingly. Just like a non fixed div changes its height according to the content inside. – talkol Aug 04 '13 at 19:46
  • I don't think these are issues with CSS, CSS just styles HTML, you can't use anything else – Ian Clark Aug 04 '13 at 19:53
  • I mean to say, I don't see how CSS can't be used to achieve everything you've said, it can handle percentages pretty well, and these could be used within a dialog – Ian Clark Aug 04 '13 at 19:59
  • It's pretty difficult to have fixed positions without specificying all sizes and positions in absolute pixels.. How can you have relative positions? – talkol Aug 04 '13 at 21:45
  • Ian, can you use a percentage width in a fixed positioning like the layout I have in the image on top? – talkol Aug 05 '13 at 14:25
  • Yes I don't see why not – Ian Clark Aug 05 '13 at 14:27
  • Sorry, was that a question or a request for me to demonstrate? Hah. – Ian Clark Aug 05 '13 at 14:34
  • I'm not sure you are correct. "fixed" in CSS is relative to the browser window size. How would you use fixed inside a modal dialog (like bootstrap dialogs).. I would want the layout to be relative to the dialog width, not the browser window width – talkol Aug 05 '13 at 14:56
  • add `position:relative` in the dialog, and then use `position:absolute` for the children – Ian Clark Aug 05 '13 at 15:41
  • Updated, take a look. – Ian Clark Aug 05 '13 at 20:47
  • @talkol updated my question again, but I'm not sure what else I can mention now! – Ian Clark Aug 08 '13 at 08:59
  • 1
    While I don't quite understand your question, I think Ian answered it correctly. – arothuis Aug 08 '13 at 19:38
  • @talkol update **again**! No more amendments to the question please! – Ian Clark Aug 09 '13 at 15:20
  • 1
    Well Ian, you get an A for effort, no doubt about that. Overall flexbox still feels like the best solution for me. It's where we are going, and I'm willing to polyfill until we get there ;) – talkol Aug 11 '13 at 07:02
  • Hah, ta. Well... I suppose if it's **really** what you want, then go with it. Final thing from me: Spotify web player recent came out, and that uses absolute positions. I'd imagine that decision was taken after a lot of consideration of the alternatives. – Ian Clark Aug 11 '13 at 10:01
2

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

Hugo Silva
  • 6,748
  • 3
  • 25
  • 42
  • I'm not really looking to accept my own answer. I had a feeling nobody would be "brave" enough to suggest them (it's controversial after all) and I was willing to suffer the downvotes for the sake of discussion :) Your answer is currently the best one in my eyes, thanks. – talkol Aug 07 '13 at 05:29
1

You can do this with straight, minimal CSS and be completely flexible (user changes font sizes or your content is of unknown length? no problem).

http://codepen.io/cimmanon/pen/vusmz

Assuming this markup:

<header>Header</header>

<div class="container">
  <div class="sidebar">
    <aside>Menu (fixed)</aside>
    <nav>long list</nav>
  </div>
  <main>
    <p>...</p>
  </main>
</div>

The CSS:

body, html {
  height: 100%;
  max-height: 100%;
  margin: 0;
  padding: 0;
}

body {
  display: -ms-flexbox;
  display: -webkit-flex;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
}
@supports (flex-wrap: wrap) {
  body {
    display: flex;
  }
}

.container {
  display: -ms-flexbox;
  display: -webkit-flex;
  -webkit-flex-flow: column wrap;
  -ms-flex-flow: column wrap;
  flex-flow: column wrap;
  -webkit-flex: 1;
  -ms-flex: 1;
  flex: 1;
}
@supports (flex-wrap: wrap) {
  .container {
    display: flex;
  }
}

.sidebar {
  display: -ms-flexbox;
  display: -webkit-flex;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  -webkit-flex: 1 100%;
  -ms-flex: 1 100%;
  flex: 1 100%;
}
@supports (flex-wrap: wrap) {
  .sidebar {
    display: flex;
  }
}

header {
  background: yellow;
  height: 3em; /* optional */
}

aside {
  background: green;
  height: 4em; /* optional */
}

nav {
  background: orange;
  -webkit-flex: 1;
  -ms-flex: 1;
  flex: 1;
  overflow-y: scroll;
  height: 1px;
}

main {
  background: grey;
  -webkit-flex: 1;
  -ms-flex: 1;
  flex: 1;
  overflow-y: scroll;
  height: 1px;
}

Browser support is limited due to the requirement for flex-wrap: wrap support (IE10 is the only browser with "partial support" that supports it: http://caniuse.com/#feat=flexbox). Current browser support is limited to Chrome, Opera, IE10 and Blackberry 10.

Be aware that the scrolling elements have an extremely small height, which can cause issues for browsers that lack full Flexbox support (see: Flexbox and vertical scroll in a full-height app using NEWER flexbox api for more information).

Community
  • 1
  • 1
cimmanon
  • 67,211
  • 17
  • 165
  • 171
  • I agree that flexbox solves this problem pretty well. Unfortunately I'm pretty sure I can't use it due to lack of support in IE9 and partial support in many others (FF,Safari) – talkol Aug 06 '13 at 02:17
0

In some project I did it with the help of a code like this (tested on FF):

<style>
#topMenu {
    position: fixed;
    left: 0px;
    top: 0px;
    width: 100%;
    height: 100px;
}
#sidePanel {
    position: fixed;
    left: 0px;
    top: 100px;    
    width: 100px;
}
#sideMenu {
    width: 100%;
    height: 50px;
}
#sideList {
    border-width: 0px;
    width: 100%;
    padding: 0px;
    margin: 0px;
    background-color: #FFF0F0;
}
#content {
    border-width: 0px;
    padding: 0px;
    margin: 0px;
    background-color: #F0FFF0;
}
body {
    border-width: 0px;
    padding: 100px 0px 0px 100px;
    margin: 0px;
}
</style>

<div id="topMenu">Top menu</div>
<div id="sidePanel">
    <div id="sideMenu">Size menu</div>
    <iframe id="sideList"></iframe>
</div>    
<iframe id="content"></iframe>

<script>
function onResize() {

    var sizePanel = document.getElementById("sidePanel");
    sizePanel.style.height = (document.documentElement.clientHeight - 100) + "px";   
    document.getElementById("sideList").style.height = (sizePanel.clientHeight - 50) + "px";   

    var content = document.getElementById("content");
    content.style.width = (document.documentElement.clientWidth - 100 - 10) + "px";
    content.style.height = (document.documentElement.clientHeight - 100 - 10) + "px";
}

window.addEventListener("resize", onResize);
onResize();

</script>
lexasss
  • 302
  • 1
  • 2
  • 9
  • Thanks, I have something similar, but it's what I'm trying to avoid. All this manual work is time consuming and inflexible (one small change requires a complete rewrite). I wish some framework would take care of all this for me behind the scenes (like most grid frameworks do). – talkol Jul 30 '13 at 10:43
  • Also, the resize event is extremely problematic across different browsers. I've seen people check for changed window size with a periodical timer to get around that - now that's really disgusting :) – talkol Jul 30 '13 at 10:45
  • Yes, I agree that this code might be too rough and not appropriate as a stable cross-browser solution. – lexasss Jul 30 '13 at 10:48
0

I don't think you should need to use a framework, especially if you're not familiar with them and are having trouble. If you just set the different views to overflow:scroll, you should be able too create what you describe in that image. Another thing you could do is set an iframe to each view like what the Javadocs do.

0

For the record, I think Ian gave by far the most complete and accurate answer here for a pure CSS answer and that would work perfectly. However, you also wanted to know what alternatives you had, and I have to at least provide another option so you know it's available:

I challenge your premise that Javascript doesn't do what you want it to do. JQuery is in my view a truly revolutionary library for Javascript and simplifies it immensely. According to Wikipedia, Jquery is "used by over 65% of the 10,000 most visited websites". From personal experience, trying to use pure CSS on my website fell flat for me. Using JQuery gave me much more flexibility and control over my CSS, no longer forcing me to use relative values. I'm a control freak so that worked for me.

JQuery is very easy to learn and allows CSS to be supplemented with browser events using Javascript handlers and events. For example, if you want to change CSS on refresh:

$(document).ready(function() {
    $(window).resize(function() {
         var windowWidth = $(window).width();
         $('#div').css('width',windowWidth-100); //the # references id, like in CSS
    });
});

See a more detailed example in action on my JSFiddle

If JQuery sounds like the right path for you to make your CSS dynamic and flexible for any occasion, there are several tutorials out there. It doesn't take long if you know CSS and Javascript and it works like a charm. Even if you don't know Javascript, you can do quite a bit with JQuery. Certainly what you're looking for with just a bit of a learning curve.

smcjones
  • 5,490
  • 1
  • 23
  • 39
0

"dozens of grid/layout frameworks out there and most of them focus on the traditional document grid layout."

You make it sound like its a bad thing. I dont see why you would not use a grid framework. It is fairly standard practice if you have any intention what so ever to make your application tablet and mobile friendly. Most of them are extremely quick to setup with little to no extra css required.

Take kube for example. It is very minimalistic and flexible. You basically just add pre defined classes to the div tag you wish to apply as the for the size.

<div class="units-row">
    <div class="unit-30"></div>
    <div class="unit-70"></div>
</div>

This type of step does not mess with modals or ifames. To modify all you need to do is change the class .. need a larger left hand side, just change the class of the div

<div class="units-row">
    <div class="unit-80"></div>
    <div class="unit-20"></div>
</div>

This will degrade gracefully for the smaller resolutions while keeping your style of coding as it would be in any other application.

You can write from scratch if you want but .. why?

foundation is also really good and the one i use for my projects

0

You might want to check out Cascade Framework and use this template as a starting point for your layout.

enter image description here

John Slegers
  • 45,213
  • 22
  • 199
  • 169
-1

Disclosure: I'm the OP and I'm tossing an idea to get the general response.

There's no need for a specialized framework because you can use TABLES for that.

Yes, I'm talking about the widely avoided area of <table> <tr> <td> for layouts. It's common knowledge that tables should be avoided for layout. I tried to go over the reasons mentioned here: Why not use tables for layout in HTML?

It seems to me that most reasons aren't valid in this case (SPA):

Separation of content from layout - Since an SPA is not really content (it's just a skeleton with AJAX filled data), this argument doesn't apply. This is just an interface, I don't expect Google to index it.

Tables are less maintainable - Not in this case. The CSS hell you have to go through to implement this with divs is much less maintainable.

Tables are slower to render - Of course, but we want a complex layout that requires more calculations to render. Heck, we usually compensate with JS resize events. It would be much efficient for the browser to do this natively inside the render engine.

Community
  • 1
  • 1
talkol
  • 12,564
  • 11
  • 54
  • 64
  • 2
    Answers are for answers. If you need to provide additional information, update your question. – cimmanon Aug 06 '13 at 02:07
  • 1
    @talkol you could edit your original question and add this information. Please don't use space provided for answer for anything other than answer – Krishnabhadra Aug 06 '13 at 03:25
  • This was actually an answer suggested by a colleague of mine. My first instinct was to shut it down because tables are no no, but I couldn't think of many factual reasons against it. If you downvote it, please explain – talkol Aug 06 '13 at 05:22
  • Suggesting to avoid a framework like I originally wanted because a native layout mechanism like flexbox or tables are good enough is a valid suggestion – talkol Aug 06 '13 at 05:26
  • But this doesn't answer the question or even try to answer the question. It reads as a really long comment and that's not what answers are for. Tables have a specific semantic meaning. Using it for anything other than presenting tabular data is incorrect. – cimmanon Aug 06 '13 at 11:34
  • You are correct.. I should have gone into the trouble of implementing the requested layout with tables in this answer. Since tables are so well-known by everybody I felt the implementation was redundant. Honestly, the main point for discussion for this potential answer is not HOW can tables be used.. It's IF people believe it can be regarded as a valid approach at all (and obviously people don't) – talkol Aug 07 '13 at 05:27
  • CSS tables are definitely an option. –  Jun 07 '16 at 02:08