I'm trying to achieve the three-column layout generally described as the "holy grail" (see this ALA article) using the new display: flex syntax.
The requirements are as follows:
A header and footer, with between them three columns
The outer columns have fixed widths
The inner column stretches to fill the space between the side columns, with a minimum and maximum width beyond which it will not stretch (so neither should the container)
The footer should be at the bottom of the viewport, until the content actually pushes it below
I got the first three requirements down with the following code:
<body>
<div class="container">
<header class="masthead">
<h1>The Header</h1>
</header>
<div class="side-left column">
Left sidebar
</div>
<div class="middle column">
Content goes here
</div>
<div class="side-right column">
Right sidebar
</div>
<footer class="footer">
© Footer
</footer>
</div>
</body>
CSS:
.container {
display: flex;
flex-flow: row wrap;
min-width: 500px;
max-width: 1100px;
}
.masthead {
flex: 1 100%;
}
.side-left,
.side-right {
flex: 0 0 150px;
}
.middle {
flex: 1;
}
.footer {
flex: 1 100%;
}
Live in action: jsBin
However, I'm stuck with the 100% height. I already tried setting either some of the columns or the container to height: 100% or min-height: 100% but none seem to work. Do I need one of the many other flex properties to handle this? I can't seem to see the forest through the trees.
.container { min-height: 100vh; }
Related
In my site I have the following structure:
Header
Content
Footer
And I want to make the Header and the Footer size based on their content (not a fixed size). And the Content to fill the remaining space.
I saw many questions and answers like: Make a div fill the height of the remaining screen space
that solves similar cases but in my case, the Header and Footer sizes are unknown so I can't use the calc() function, and the Header Has position:fixed which removes it from the layout calculations and makes the
flex solutions of various kinds wrong:
html,
body {
height: 100%;
margin: 0;
}
.box {
display: flex;
flex-flow: column;
height: 100%;
}
.box .row {
border: 1px dotted grey;
}
.box .row.header {
flex: 0 1 auto;
position: fixed;
/* The above is shorthand for:
flex-grow: 0,
flex-shrink: 1,
flex-basis: auto
*/
}
.box .row.content {
flex: 1 1 auto;
}
.box .row.footer {
flex: 0 1 40px;
}
<!-- Source - https://stackoverflow.com/a/24979148-->
<div class="box">
<div class="row header">
<p><b>header</b>
<br />
<br />(sized to content)</p>
</div>
<div class="row content">
<p>
<b>content</b>
(fills remaining space)
</p>
</div>
<div class="row footer">
<p><b>footer</b> (fixed height)</p>
</div>
</div>
Or using this solution:
html,
body {
height: 100%;
}
body {
display: flex;
flex-direction: column;
}
.header{
position:fixed;
}
.content {
flex-grow: 1;
border: 1px dotted red;
}
<!-- Source - https://stackoverflow.com/a/28771764-->
<body>
<div class="header">header</div>
<div class="content"></div>
</body>
Is there any way to do make the Content height = 100% - FooterHeight - HeaderHeight
When the Footer and Header dimensions are unknown, and the Header has fixed position?
Since the header is fixed, I think you would need to know its height through JavaScript, and set the body's min-height as 100% of the viewport's height minus the header's height. After, you could simply use CSS Grid on body, to have the content take all the avaiblable height. Like so:
document.body.style.minHeight=`calc(100vh - ${document.querySelector("header").clientHeight}px)`;
document.body.style.paddingTop= document.querySelector("header").clientHeight + "px";
body{
margin:0;
display:grid;
grid-template-rows:1fr auto;
}
header{
background:lightblue;
position:fixed;
top:0;
left:0;
width:100%;
}
div{
background:lightgreen;
}
footer{
background:lightyellow;
}
<header>I'm the header</header>
<div>I'm the content</div>
<footer>I'm the footer</footer>
I can think of two solutions based on the rather general description of your problem:
A) Use JavaScript to do the calculations for you and apply the values to margins or positions, whichever works better in your case;
B) You could repeat the contents of the header (and footer, id that's out of the document flow also) in element(s) atop the content and make it transparent and non-inter-active (pointer-events: none) - dirty, but if JS is not an option and your header does not offer some other way to determine it's height through some 'css-magic' it might be the only solution.
Quite often I find, that there are better solutions when the problem is more specifically described, so if you can tell us what elements make it impossible to know the height of the header, there might be better solutions. Often when ratios as with images are in play, vh can come to the rescue - even though tha can be tricky too...
Finally I found a pure css solution.
since the Header is in the top , using position: sticky instead of fixed will have the same result but the layout will take it into account when calculating:
html,
body {
height: 100%;
margin:0;
}
body {
display: flex;
flex-direction: column;
}
.header{
position:sticky;
}
.content {
flex-grow: 1;
border: 1px dotted red;
}
<!-- Source - https://stackoverflow.com/a/28771764-->
<body>
<div class="header">header</div>
<div class="content"></div>
</body>
I have a requirement on which we have Header, Middle Section and Footer.
Header and Footer height can be more or less depending on the various screen sizes and content within them.
Footer will be sticky, at the end of the screen if the page doesn't contain scroll and should not overlap over the middle content if scrolling occurs for small screens. Typical sticker footer behavior.
Middle section contains two columns. And Each column content should be vertically center aligned.
Please refer below screenshot for reference what
Note: I can use normal sticky footer and CALC to adjust the height of the main content, but it will not be dynamic. I don't want to use javascript to do all mathematics on DOMContentLoaded and window resize.
My approach uses a bunch of flexboxes and keeps things simple.
.container is a columnar flexbox
main takes up the most available space
header, footer take only the space they need (dynamic)
main is also a flexbox, but in the row direction to house the left and right panels
the panels, too, are flexbox containers, centering their content horizontally and vertically
You might want to view the demo in 'Full page' mode, or in jsFiddle
html,
body {
margin: 0;
}
.container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex-grow: 1;
display: flex;
background-color: #eee;
}
.panel1,
.panel2 {
background-color: brown;
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
}
.panel-content {
background-color: #ccc;
padding: 3em;
}
header,
footer {
background-color: #ccc;
padding: 1em;
text-align: center;
}
<div class="container">
<header>Header</header>
<main>
<div class="panel1">
<div class="panel-content">
Content
</div>
</div>
<div class="panel2">
<div class="panel-content">
Content
</div>
</div>
</main>
<footer>Sticky Footer</footer>
</div>
jsFiddle
I have a simple HTML document. I have a header, a section and a div (that contains an unknown number of other divs).
The header and the section do not (and can not) have set heights. Their height comes from the content. Only their width is known (set to 100%).
Is it possible, with flexbox or other means, to get each of those child divs, in this case with class="fill" to be the height of the body - minus the header and section?
In other words, when someone goes to the page, I want them to see the header and the section and then have the first div.fill reach all the way to the bottom, forcing them to scroll to see the next div (but not scroll to see the bottom of the first child div).
I am using a templating system so unfortunately the structure of the HTML can not change and I would like to do this only in CSS.
<html>
<body>
<header> Header content, might contain an image</header>
<section> This is the sub header, unknown height </section>
<div class="container">
<div class="fill">I Want</div>
<div class="fill">Each of These</div>
<div class="fill">To be </div>
<div class="fill">The height of the body - the Header - the Section</div>
</div>
</body>
</html>
body {
display: flex;
flex-direction: column;
height: 100vh;
margin: 0;
}
.container {
flex: 1; /* 1 */
display: flex;
flex-direction: column;
overflow-y: scroll;
}
.fill { flex: 0 0 100%; } /* 2 */
header { background-color: aqua; }
section { background-color: orange; }
.fill:nth-child(odd) { background-color: yellow; }
.fill:nth-child(even) { background-color: lightgreen; }
<body>
<header> Header content, might contain an image</header>
<section> This is the sub header, unknown height </section>
<div class="container">
<div class="fill">I Want</div>
<div class="fill">Each of These</div>
<div class="fill">To be </div>
<div class="fill">The height of the body - the Header - the Section</div>
</div>
</body>
jsFiddle
Notes:
The flex-grow: 1 component of flex: 1 tells the .container element (a flex item child of body) to consume all remaining space. This will cause .container to use up any space not consumed by header and section.
The flex-basis: 100% component of flex: 0 0 100% tells the .fill items (flex item children of .container) to consume 100% height of the parent. So these items will always take the full height of flex-grow: 1 on the parent.
Because flex items are set, by default, to shrink in order to not overflow the container, an override is set with flex-shrink: 0 in the flex: 0 0 100% rule. This disables the shrinking feature and allows the items to stay fixed at 100% height. (Otherwise, regardless of the defined height / flex-basis, the items would shrink evenly to prevent an overflow. See demo.)
If you change the structure of the elements a bit you can get it with only css.
Basically add the first .fill element in a container with the header and the section (let's call it first). For the other divs use height: 100vh
body {
margin: 0;
}
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.fill {
height: 100vh;
overflow: auto;
}
.first {
flex: 1;
}
header { background-color: aqua; }
section { background-color: orange; }
.first, .fill:nth-child(odd) { background-color: yellow; }
.fill:nth-child(even) { background-color: lightgreen; }
<html>
<body>
<div class="container">
<header> Header content, might contain an image</header>
<section> This is the sub header, unknown height </section>
<div class="first">I Want</div>
</div>
<div class="fill">Each of These</div>
<div class="fill">To be </div>
<div class="fill">The height of the body - the Header - the Section</div>
</body>
</html>
I've prepared a mobile site, which has the right aspect ratio of images, but on some phones with bigger screens it's to short and after the footer I get white gap. I would prefer to add some DIVs between contents which they would auto stretch when needed.
I'm thinking of something like this:
<div id="wrapper">
<div id="header"></div>
<div id="content1"></div>
<div class="empty_gap"></div>
<div id="accordion"></div>
<div class="empty_gap"></div>
<div id="footer"></div>
</div>
The .empty_gap DIVs would get evenly height so the page would fit the screen height. How to get this done? Or do you have any other solution for such a situation when the page is to short to fill the entire mobile screen?
EDIT: I have to add one thing which I didn't think before it will cause a problem. None of these solutions are working for me, because I use jquery accordion, and when I execute $("#wrapper").outerHeight(); I get much bigger value than the screen size even when I have all panels collapsed. I guess that this is also the reason for flexible boxes that those solutions don't work too.
EDIT2: The JS solution is working now, I just needed to subtract the panel's content from the wrapper.
If you're OK with a JavaScript solution, try this:
Usually your divs should be wrapped with a div container. If it's not the case, then go ahead and add one like this:
<div id="wrapper">
<div id="header"></div>
<div id="content1"></div>
<div class="empty_gap"></div>
<div id="content2"></div>
<div class="empty_gap"></div>
<div id="footer"></div>
</div>
Then in JavaScript, calculate the height of this container div and subtract it from the window's height. That would be the total of all your gaps, so divided by the number of your gaps (2 in your example) to get the height of each gap. Something like this:
var totalGap = $(window).height() - $("#wrapper").outerHeight();
var gap = totalGap / 2; //2 is the number of your gaps
$(".empty_gap").css("height", gap);
Place this code in event for when the page finished loading. However, if your app supports rotation, then you'll have to place this code in the $(window).resize() event.
I suggest to use flexbox layout, for the equal space above and below #content2 you will just need to set #content2 { margin: auto 0; } to make it to work.
jsFiddle
html, body {
height: 100%;
}
body {
display: flex;
flex-direction: column;
margin: 0;
}
#content2 {
margin: auto 0;
}
<div id="header">header</div>
<div id="content1">content1</div>
<div id="content2">content2</div>
<div id="footer">footer</div>
If you do not need to support old browsers, it can be a job for flexible boxes.
Have a look here for browsers support (94.22% support when used jointly the with -webkit prefixed version).
You just have to set your container element to display: flex; with flex-direction: column; then add flex-grow: 1 (or anything > 0) to the .empty_gap elements.
#container {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
#header, #footer {
height: 25px;
background-color: red;
}
#content1, #content2 {
height: 50px;
background-color: blue;
}
.empty_gap {
background-color: yellow;
flex-grow: 1;
}
<div id="container">
<div id="header"></div>
<div id="content1"></div>
<div class="empty_gap"></div>
<div id="content2"></div>
<div class="empty_gap"></div>
<div id="footer"></div>
</div>
I'm trying to accomplish the following. I've dumbed it down for purposes of demonstration:
A page with a header, a footer, and 2 columns
The header is always on top (i.e., vertically above the rest of the content) with height 20px
The footer is always at the bottom (i.e., vertically below the rest of the content) with height 20px.
In between, there are 2 columns
If the viewport is wide enough, the 2 columns are presented side by side, and everything fits inside the viewport. Column A takes 75%, Column B 25%. Each is (100% - 40px) tall
If the viewport is not wide enough, the 2 columns are presented on top of each other, each 100% wide. Column A is still (100%-40px) tall, column B has a height to fit its content. The page is now more than 1 viewport tall (with the header visible when scrolling all the way up, and the footer visible when scrolling all the way down)
If content does not fit a column, a scroll bar should appear inside the overflowing column
I've accomplished all points except number 6 here
I cannot got number 6 to work. I've tried min-width. I've also tried to 'port' the whole thing to bootstrap (I'm using bootstrap elements on the page already) but that doesn't work nicely with some angular components (that need to poll their parent's size; for some reason the element then keeps growing)
I'd be grateful for good ideas!
edit
I've tried using flexboxes. It's closer, but the viewport doesn't scroll when it gets too narrow... ideas? https://jsfiddle.net/498xpp6n/2/
edit
I've thought about it some more, and made a little change to the wanted column heights when going into stacked mode. Hope that doesn't ruin someone's day
The layout is possible with flexbox. This is all you need:
HTML
<div id="outer-flex-container"><!-- primary flex container -->
<div id="header">The Header The Header The Header ... </div><!-- flex item #1 -->
<div id="inner-flex-container"><!-- flex item #2 -->
<div id="mainpanel">Mainpanel Mainpanel Mainpanel Mainpanel Mainpanel ... </div>
<div id="aside">settings settings settings settings settings settings ... </div>
</div><!-- end #inner-flex-container -->
<div id="footer">The Footer The Footer The Footer ... </div><!-- flex item #3 -->
</div><!-- end #outer-flex-container -->
CSS
html { height: 100%; }
body { height: 100%; margin: 0; }
#outer-flex-container {
display: flex;
flex-direction: column;
height: 100%;
}
#inner-flex-container {
display: flex;
height: calc(100% - 40px);
}
#header { height: 20px; }
#footer { height: 20px; }
#mainpanel { flex: 0 0 75%; }
#aside { flex: 1; overflow-y: scroll; }
#media screen and ( max-width: 500px) { #inner-flex-container { flex-direction: column; } }
I believe the code above covers all seven points in your question :-)
DEMO
UPDATE (based on comments)
HTML
<div id="outer-flex-container"><!-- primary flex container -->
<div id="header">The Header The Header The Header ... </div><!-- flex item #1 -->
<div id="inner-flex-container"><!-- flex item #2 -->
<div id="mainpanel">Mainpanel Mainpanel Mainpanel Mainpanel Mainpanel ... </div>
<div id="aside">settings settings settings settings settings settings ... </div>
<div id="footer">The Footer The Footer The Footer ... </div>
</div><!-- end #inner-flex-container -->
</div><!-- end #outer-flex-container -->
Notes:
moved footer into .inner-flex-container
now only two primary flex items
three inner flex items
CSS
body { margin: 0; }
#outer-flex-container {
display: flex;
flex-direction: column;
}
#inner-flex-container {
display: flex;
flex-wrap: wrap;
}
#header { height: 20px; }
#mainpanel {
flex: 0 0 75%;
height: calc(100vh - 40px);
min-height: calc(100vh - 40px);
overflow-y: auto;
}
#aside {
flex: 0 0 25%;
height: calc(100vh - 40px);
overflow-y: auto;
}
#footer {
flex-basis: 100%;
height: 20px;
}
#media screen and ( max-width: 500px) {
#inner-flex-container { flex-direction: column; }
#mainpanel { height: 100vh; }
#aside { height: auto; }
}
Notes:
removed fixed heights (was limiting footer positioning on smaller screens)
added footer to inner flex container so it's always below content columns
Revised Demo