An expanding middle in CSS - html

How would I go about designing a website which has a fixed height header and footer (attached to the top and bottom of the browser window) but an expanding middle. The scroll bars would be only for the middle (orange section in diagram) so that the rest of the page would never need to scroll. I have drawn a mock-up below to explain more clearly.
Ideally it needs to be entirely implemented in CSS and HTML (no javascript fiddles!). I've got quite far with this problem but I can't force the orange section to fill up the remaining space when it isn't full(whatever it's content) and start scrolling if it overflows.

I think this is what you want:
Live Demo (edit)
HTML:
<div id="header">Patrick</div>
<div id="content">..</div>
<div id="footer">Beardmore</div>
CSS:
html, body {
margin: 0;
padding: 0;
border: 0;
overflow: hidden
}
#header, #content, #footer {
position: absolute;
left: 0;
width: 100%
}
#header {
top: 0;
height: 100px;
background: #ccc
}
#content {
top: 100px;
bottom: 100px;
overflow-y: auto
}
#footer {
bottom: 0;
height: 100px;
background: #ccc
}

It's called StickyFooter or the "footer push" method. It's all over the web, but this is the best option I've found:
http://ryanfait.com/sticky-footer/

An old question, but flexbox has given us a super easy way to implement this pattern, a familiar variation on the 'Holy Grail' layout:
body {
/*set container to vertical (column) flex mode, ensure body is full height*/
display: flex;
min-height: 100vh;
flex-direction: column;
}
header, footer {
/*more or less equivalent to min-height:50px*/
flex-basis:50px
}
header {
background-color: #7AEE2D;
}
main {
background-color: #EBAE30;
/*tell main section to expand to fill available space, this is same as flex 1; or flex:1 1 auto;*/
flex-grow:1;
}
footer {
background-color: #34A4E7;
}
<header>header</header>
<main>main</main>
<footer>footer</footer>
A note about the syntax: I've used the "atomic" flexbox CSS properties here for simplicity, but in the wild you are more likely to run into the shorthand syntax using the flex keyword by itself. The default values for the 3 properties you can set on flex items (children of a display:flex container) are:
Initial value as each of the properties of the shorthand: flex-grow: 0
flex-shrink: 1 flex-basis: auto
Using flex, there are multiple ways to compose these properties, specifying one, two, or three values, and those values can by keywords, unit lengths (2px), or unitless grow/shrink ratios 2. Many different "overloads" are available, depending on your arguments.
For example, flex-basis:50px could've been flex:50px, flex:0 1 50px, and flex-grow:1 could have been flex 1; or flex:1 1 auto;. It's still not as bad as some other CSS shorthands I can think of (position, I'm looking at you). The 'flex' shorthand syntax MDN page has more details.

Related

Complex responsive layout

I have a complex layout design and I want to make it responsive. The problem is I don't know-
How I should approach this design (how to structure the html to achieve this)
flex?
grid?
absolute positioning?
Is it even possible to make this kind of layout without making 2 sets of everything and hiding one of them for desktop and vice versa?
This is the layout that I'm talking about:
Desktop layout
Mobile layout
I don't even need the code part for this implementation.
I just need to know if this is possible and if so how do I do that(which structure, what display type for the parent etc)
If not, what are the work arounds?
Changing the design is the final resort but i really don't want to do that.
Have the three elements as siblings, in the order you want them displayed on mobile.
Detach the blue element's background from its contents (conceptually) - they are not the same thing.
Render the yellow and blue (transparent on desktop) elements as right column (using grid or flex), while placing the blue background under them (I used the parent's :before) for positioning the background.
I purposefully placed an inline red border on the contents of the blue element, to highlight its position.
You're pretty much done. The rest are implementation details.
I challenge you to do it yourself before looking at the solution below.
Key points of the solution below:
Note the required position: relative on the parent and also the position:relative; z-index: 0 on the "right-column" elements (without those rules they're rendered below the :before).
I used min-heights here to size the elements (as they're empty) but, once you place content inside them, you could/should remove the min-heights.
You'll probably need to adjust the margins/paddings to suit your needs, once you place real content inside them, but that should be fairly straight-forward.
Here it is:
body {
margin: 0;
}
* {
box-sizing: border-box;
}
.red {
background-color: #ff8584;
padding: 1rem;
}
.yellow {
background-color: #fff742;
}
.green {
background-color: #c0ff72;
}
.blue {
background-color: #72ccff;
}
.red>* {
min-height: 100px;
margin-bottom: 1rem;
}
.red>*:last-child {
margin-bottom: 0;
}
#media (min-width: 768px) {
.red {
position: relative;
display: grid;
grid-template: 'green yellow' auto 'green blue' 1fr / 1fr 1fr;
min-height: 100vh;
grid-column-gap: 1rem;
}
.red:before {
content: '';
position: absolute;
background-color: #72ccff;
top: 1rem;
right: 1rem;
bottom: 1rem;
left: calc(50% + .5rem);
}
.red>* {
margin-bottom: 0;
}
.yellow {
margin: 1rem 1rem 0;
grid-area: yellow;
}
.green {
grid-area: green;
}
.blue {
grid-area: blue;
}
.yellow,
.blue {
position: relative;
z-index: 0;
}
.blue {
background-color: transparent;
}
}
<div class="red">
<div class="yellow"></div>
<div class="green"></div>
<div class="blue" style="border: 1px solid red"></div>
</div>
I also wrote an SCSS version, making the background properties #mixins and the $spacer a variable, so they could be easily replaced.
Even though #tao answered this question with an better answer, I'd also like to provide a less "professional" answer but works great.
My answer uses grid-template-areas and media queries. In addition to that, I'm not going to select my own answer as accepted answer(Even though this is what I used in the end)
https://codepen.io/absanthosh/pen/WNRqLYQ?editors=1100
I suppose what you could do is:
In the HTML code make the order of elements yellow-green-blue, all three as siblings and direct children of red.
For the mobile version there will be no position settings necessary, no flex or anything, just regular divs with default (= full) width, with some padding and margins.
For the desktop version make green and blue inline-blocks, with a little less than 50% width, all taking into account according paddings and margins. Apply position: relative to red and position: absolute to yellow, which gets a width of around 45% (again, depending on paddings and margins) and a right setting that places it where you want it (as a percentage value). The top setting for yellow depends on the padding/margins of red and blue.

display: table row does not allow scroll

I am currently designing a book website and on the right-hand side want to have a "Table of contents" which is in a fixed position and scrollable. I set the header for my website to "display: table-row" and similarly did so with the table of contents and its internal elements. (A header and a the scrollable list of chapters) As I understand it, display: table row should make a div element fill the remaining height and only the remaining height. [1] However, in this case, the content continues offscreen instead of allowing the user to scroll through it. (You can see the problem on this jsfiddle: https://jsfiddle.net/chrmon2/9wzjckvn/6/)
My css:
#container {
height: 300px;
border: 1px solid black;
}
#header {
background: blue;
display: table-row;
}
#toc h1 {
background: red;
display: table-row;
}
#toc #content {
background: yellow;
overflow-y: scroll;
display: table-row;
}
Is this not a capability of display: table-row or am I doing something wrong? Thanks
https://www.whitebyte.info/programming/css/how-to-make-a-div-take-the-remaining-height
As I understand it, the effect you want is as follows:
When there is remaining space, stretch the table of contents items;
When the space is too small, begin scrolling.
This is an ideal application of CSS Flexboxes. Flexboxes allow you to define how items stretch (or don't), how they align with each other, how they wrap etc.
In this case, we're going to use flexboxes for all the divs in this example.
.container, .toc, .content {
display: flex; /* begins flexbox layout */
flex-direction: column; /* Flexboxes can be aligned from left-to-right
or from top-to-bottom. In this case, we want
them to be top-to-bottom. */
flex-grow: 1; /* On .container, this does nothing. However, for
children of Flexboxes, this tells them to grow
if any space is still available. */
min-height: 0; /* For a more complicated reason, this is necessary
to make flexboxes scroll correctly. This needs
to be set on all the flexboxes in this example. */
}
.toc .content .item {
flex-grow: 1; /* Make items grow when there is room available */
}
.content {
overflow-y: scroll;
}
You can see the effects of this at this JSFiddle.
Just remove display: table-row from #toc #content and add this :
#content{
max-height:200px;
}
height can be as per your requirement.

Force Bootstrap's variable height footer to the bottom of page or content, whichever is lower

In my bootstrap page, I want to force the <footer>...</footer> to be at the bottom of the page or the content, whichever is lower.
And there is a solution for that already which works great. However, that approach requires that I know ahead of time what the height of the footer will be.
How do I achieve the same thing, but the footer height is determined by its content?
You can use flexbox to achieve sticky footer.
Using flex: 1 0 auto; to section will make it flexible, it will acquire all available free space in flex-container.
flex: <positive-number>
Makes the flex item flexible and sets the flex basis to zero,
resulting in an item that receives the specified proportion of the
free space in the flex container. If all items in the flex container
use this pattern, their sizes will be proportional to the specified
flex factor.Equivalent to flex: 1 0.
html,
body {
margin: 0;
padding: 0;
}
html {
height: 100%;
}
body {
min-height: 100%;
display: flex;
flex-direction: column;
height: 1px; /* Height hack for IE */
}
header,
footer {
padding: 10px;
color: white;
background-color: #000;
}
section {
flex: 1 0 auto; /* This will make section flexible */
}
<header>Header</header>
<section></section>
<footer>Footer</footer>

Using flexbox, vertically center items and respect top 150px space of flexbox container

I am trying to craft a flexbox in CSS with the following characteristics:
Three items stacked and centered within the middle of the flexbox
One item locked to bottom of the flexbox
Flexbox is set to a size of 100vh and 100vw, taking up visible screen area
Items within flexbox must not occupy the top 150px of the flexbox. This may push some items below the viewable area (see "Desired Result 3" in image below)
Ideally, a pure CSS solution is feasible with the HTML elements provided
The Problem
The vertically centered boxes won't honor the 150px space I would like to put at the top of the flexbox, and I haven't been able to create an elegant way to make sure the items don't float up past the top of the screen if I make the window too short. That is, the example in "Desired Result 3" in the image above remains elusive.
Example Code
HTML:
body {margin:0; font-family: sans-serif; font-weight:bold;}
.parentFlexBox {
background-color:grey;
display: flex;
flex-direction: column;
margin-top:0;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
height:100vh;
width:100vw;
}
.itemA, .itemB, .itemC, .itemD { padding:5px; text-align:center; margin-bottom:5px; color:#fff;}
.itemA { background-color:red; width:50px; margin-top:auto;}
.itemB { background-color:hotpink; width:150px; height:50px}
.itemC { background-color:purple; width:40px; height: 35px}
.itemD { background-color:blue; margin-top:auto; width: 80px;}
<div class="parentFlexBox">
<div class="itemA">A</div>
<div class="itemB">B</div>
<div class="itemC">C</div>
<div class="itemD">D</div>
</div>
Please run above code in full screen to see the issue
This might work for you:
HTML (add two invisible flex items)
<div class="parentFlexBox">
<div class="itemA">A</div>
<div class="itemB">B</div>
<div class="itemC">C</div>
<div class="itemD">D</div>
<div class="itemE">E</div><!-- invisible spacer item with 150px height -->
<div class="itemF">F</div><!-- invisible spacer item with 120px height -->
</div>
CSS
.parentFlexBox { justify-content: space-between; } /* changed from `center` */
.itemA { order: 1; } /* removed `margin-top: auto` */
.itemB { order: 2; }
.itemC { order: 3; }
.itemD { order: 5; height: 30px; } /* added `height` for centering calculation;
removed `margin-top:auto` */
.itemE {
order: -1;
flex: 0 0 150px; /* don't grow, don't shrink, remain at 150px height */
visibility: hidden;
margin-bottom: auto; /* stick to top of container */
}
.itemF {
order: 4;
flex: 0 1 120px; /* don't grow, shrink proportionally, start at 120px height */
visibility: hidden;
margin-top: auto; /* go south as much as possible (sticks to Item D) */
}
/* NOTE: Item D has height 30px. Item F has height 120px. Together they equal height of
Item E. Equally balanced on both ends, Items A, B & C are centered in the container. */
DEMO 1
I placed the spacer divs last in the mark-up to keep with the alphabetical ordering. If you prefer to list all divs (including spacers) in order, that would eliminate the need for the order property.
Also, in the demo, the code includes borders, in case you want to see the spacers at work. Just disable the visibility property.
Update (based on comment)
Nice, a couple of questions though: 1) Possible to make it so that BCD don't change height when resizing the window? 2) Possible to make the gray background extend to contain D when window is short? 3) Possible to do items E and F as pseudocode elements?
Question #1: Yes. Add flex: 0 0 <<absolute height>> to BCD. For instance, add flex: 0 0 50px to each item, which tells them to stay fixed at 50px height. (Also, remove the height property from each rule, to avoid any potential conflict with flex.)
Question #2: Yes. Instead of limiting the container to height: 100vh, use min-height: 100vh.
Question #3: Yes. Remove the E and F code from the HTML and CSS, and add this to the CSS:
.parentFlexBox::before {
content: '';
flex: 0 0 150px;
visibility: hidden;
margin-bottom: auto;
}
.parentFlexBox::after {
content: '';
flex: 0 1 100px;
visibility: hidden;
margin-top: auto;
order: 4;
}
DEMO 2

How to force a div to scroll after a certain size?

The Fiddle
This fiddle explains the problem clearly: fiddle (Edit: fixed broken fiddle.)
The Problem
I have a container div that has 3 divs inside of it.
The top div and middle div are dynamic in height. The bottom div is fixed.
Once the middle div expands enough, I want it to be scrollable.
Code Snippet
The basic structure:
<div id='container'>
<div id='top'>Top (dynamic) content</div>
<div id='middle'>Middle (dynamic) content</div>
<div id='bottom'>Bottom (fixed) content</div>
</div>
The basic CSS:
#container {
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 250px;
padding-bottom: 100px; /* bottom div height */
}
#top {
???
}
#middle {
???
}
#bottom {
position: absolute;
bottom: 0;
left: 0;
height: 100px;
}
The Question
Is there a way to accomplish this using just CSS? (By just CSS, I mean no JavaScript.)
Here is an approach using flexboxes for the layout:
Example Here
Set the display of the parent #container element to flex. Since you want the element to stack vertically, set the flex-direction property's value to column. And justify-content: space-between is used to position the last element at the bottom when the height of the middle element decreases.
It's worth pointing out that vh units are used to set the height of the parent element to the height of the viewport.
#container {
height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
}
I also set the middles element's flex-shrink property to a relatively arbitrary number in order for it to shrink. Then flex-basis: 100% is used to force the element to fill the remaining space.
#middle {
overflow-y: auto;
flex-shrink: 50;
flex-basis: 100%;
}
Yes, set a max-height on the #container. You may also need to fiddle with the overflow property. Start by setting it to scroll.
You could do
#middle {
overflow-y: auto; /* does nothing */
height: calc(100% - 100px);
}