Layout with CSS (or should I give up and use tables?) - html

I am trying to achieve the following Layout, as per screenshot.
Main features are
Screen divided into 3 regions (columns);
Left / Right columns are fixed width;
Middle column expands as per Browser width
Right column is subdivided into two regions
Bottom region is fixed size, always at the bottom
Top region expands as per Browser height
Using HTML-tables took me about 2 hours to generate the above screenshot, with the above features.
After stuffing around with CSS for two days, I cannot get it looking as above, my attempt at CSS and associated screenshot follow below:
<html>
<head>
<title>My Layout</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
body{
height:100%;
background:beige;
}
#header {
width:100%;
height:60px;
text-align:center;
background:#A7C942;
color:#fff;
float:left;
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
font-size:2em;
}
#leftDiv {
float:left;
width:150px;
height:90%;
background:aquamarine;
}
#midDiv {
float:left;
width:auto;
height:90%;
background:lightsalmon;
display:block;
}
#rightDiv {
float:right;
width:365px;
height:90%;
background:green;
display:block;
}
#topRow {
background-color:lightgoldenrodyellow;
}
#bottomRow {
background-color:lightpink;
height:200px;
}
</style>
</head>
<body>
<div id="body">
<div id="main">
<div id="header">my header</div>
<div id="leftDiv">
<p>LEFT</p>
</div>
<div id="midDiv">
<p>MIDDLE</p>
</div>
<div id="rightDiv">
<p>RIGHT</p>
<div id="topRow">
TOP
</div>
<div id="bottomRow">
BOTTOM
</div>
</div>
</div>
</div>
</body>
</html>
Screenshot with CSS:
Problems with the CSS attempt are:
Middle col does not expand (salmon colored part);
instead white color appears out of nowhere;
cannot get pink region to stay at the bottom always
Cannot get yellow region to stretch up and down
unwanted wrapping (i.e. the right region goes under left, middle regions)
Therefore, I am about to unleash my solution using tables, unless some die-hard CSS fanatic comes to the rescue with a working answer:-)
Update
Great answers, at the time of this update there were 4. I tried out all 4 on Firefox and Chrome, and every answer is acceptable. But i can only choose one as the accepted answer, and i will go with the one that's plain and simple using only css and absolute positioning (no flexbox nor css-tables).
Thank you muchly to #matthewelsom, #Pangloss, #Shrinivas, #Paulie_D; I am sure anyone who stumbles upon your answers will find it useful for their use case. Upvotes for everyone, your efforts are appreciated!

Check out this fiddle.
It uses basic CSS and HTML and NO framework.
The key elements which allows this type of positioning are these css properties:
left, right, top, bottom.
The demo uses these properties.
Here is the snippet.
body {
background: beige;
margin: 0;
}
#header {
width: 100%;
height: 60px;
text-align: center;
background: #A7C942;
color: #fff;
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
font-size: 2em;
}
#leftDiv {
position: absolute;
float: left;
width: 150px;
top: 60px;
bottom: 0;
background: aquamarine;
}
#midDiv {
position: absolute;
float: left;
top: 60px;
bottom: 0;
left: 150px;
right: 365px;
min-width: 50px;
background: lightsalmon;
}
#rightDiv {
position: absolute;
float: right;
width: 365px;
top: 60px;
bottom: 0;
right: 0;
background: green;
}
#topRow {
position: absolute;
width: 100%;
top: 0;
bottom: 50px;
min-height: 20px;
background-color: lightgoldenrodyellow;
}
#bottomRow {
position: absolute;
background-color: lightpink;
width: 100%;
height: 50px;
bottom: 0;
}
<div id="body">
<div id="main">
<div id="header">my header</div>
<div id="leftDiv">
<p>LEFT</p>
</div>
<div id="midDiv">
<p>MIDDLE</p>
</div>
<div id="rightDiv">
<div id="topRow">TOP</div>
<div id="bottomRow">BOTTOM</div>
</div>
</div>
</div>

Here is a quick flex layout, very rough but you'll get the idea.
You can read more about Flexbox on CSS tricks.
I have also made the design responsive so the layout will fill 100% width on screens under 480px width.
CODEPEN HERE
html, body {
height: 100%;
padding: 0;
margin: 0;
}
.wrapper {
height: 100%;
display: flex; /* NEW, Spec - Firefox, Chrome, Opera */
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6, BB7 */
display: -ms-flexbox; /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Safari 6.1+. iOS 7.1+, BB10 */
-webkit-flex-direction: column;
flex-direction: column;
}
.wrapper > * {
flex: 1 100%;
-webkit-flex: 1 100%;
}
.header {
flex: 1;
-webkit-flex: 1;
background: green;
width: 100%;
max-height: 80px;
}
.below {
flex: 1;
-webkit-flex: 1;
display: flex;
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
-webkit-flex-direction: row;
flex-direction: row;
}
.main {
background: deepskyblue;
flex: 3 0px;
-webkit-flex: 3 0px;
-webkit-order: 2;
order: 2;
}
.left {
background: yellow;
max-width: 100px;
flex: 1 auto;
-webkit-flex: 1 auto;
-webkit-order: 1;
order: 1;
}
.right {
background: hotpink;
max-width: 300px;
position: relative;
display: flex;
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
flex: 1 auto;
-webkit-flex: 1 auto;
-webkit-order: 3;
order: 3;
-webkit-flex-direction: column;
flex-direction: column;
}
.top {
background: lightpink;
flex: 1 auto;
-webkit-flex: 1 auto;
}
.bottom {
background: salmon;
max-height: 200px;
width: 100%;
flex: 1 auto;
-webkit-flex: 1 auto;
}
#media screen and (max-width: 479px) {
.below {
-webkit-flex-direction: column;
flex-direction: column;
}
.left, .right, .main {
max-width: 100%;
flex: 1;
-webkit-flex: 1;
}
body, html, .wrapper {
height: auto;
}
}
<div class="wrapper">
<header class="header"><h3>Header</h3><p>Fixed height 80px, 100% width</p></header>
<div class="below">
<article class="main">
<h3>Flexible width Article</h3>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
Mauris placerat eleifend leo.</p>
</article>
<aside class="left">
<h3>Left</h3>
<p>100px fixed width</p>
</aside>
<aside class="right">
<div class="top">
<h3>Right Top</h3>
<p>Flexible Height, Fixed width 300px</p>
</div>
<div class="bottom">
<h3>Right Bottom</h3>
<p>Fixed width 300px, Fixed Height 200px</p>
</div>
</aside>
</div>
</div>

Here we go the CSS table layout, adjusted the markup slightly - added a couple of <div> containers for applying the styles.
JsFiddle Demo
html, body {
height: 100%;
margin: 0;
}
#page, #main, #rTable {
display: table;
border-collapse: collapse;
width :100%;
height: 100%;
}
#page > div {
display: table-row;
}
#page #hRow {
background: lightgreen;
text-align: center;
}
#page #mRow {
height: 100%;
}
#main > div {
display: table-cell;
vertical-align: top;
}
#leftDiv {
width: 100px;
background: aquamarine;
}
#midDiv {
background: lightsalmon;
}
#rightDiv {
width: 200px;
background: green;
}
#rTable > div {
display: table-row;
}
#rTable #topRow {
background: lightgoldenrodyellow;
}
#rTable #bottomRow {
height: 100px;
background: lightpink;
}
<div id="page">
<div id="hRow">
<div id="header">HEADER</div>
</div>
<div id="mRow">
<div id="main">
<div id="leftDiv">
<p>LEFT</p>
</div>
<div id="midDiv">
<p>MIDDLE</p>
</div>
<div id="rightDiv">
<div id="rTable">
<div id="topRow">RIGHT-TOP</div>
<div id="bottomRow">RIGHT-BOTTOM</div>
</div>
</div>
</div>
</div>
</div>

Using flexbox this is actually pretty simple.
Codepen Demo
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
min-height: 100%;
}
.wrapper {
width: 80%;
margin: auto;
display: flex;
flex-direction: column;
border: 1px solid red;
height: 100%;
}
header {
height: 50px;
background: green;
text-align: center;
line-height: 50px;
}
main {
flex-grow: 1;
border: 1px solid red;
height: 100%;
display: flex;
}
.left {
width: 200px;
background: lightblue;
flex: none;
}
.center {
flex-grow: 1;
background: salmon;
}
.right {
width: 300px;
flex:none;
background: yellow;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
}
.top {
flex-grow: 1;
background: 000;
}
.bottom {
height: 50px;
flex: none;
background: pink;
}
<div class="wrapper">
<header>
<h2>Header</h2>
</header>
<main>
<div class="col left">Fixed Left</div>
<div class="col center">Flexible Center</div>
<div class="col right">
<div class="top">Top Flexible Height inside fixed Right</div>
<div class="bottom">fixed Height Bottom</div>
</div>
</main>
</div>
CSS-Tricks.com Ultimate Guide
Prefixes will be required.
Support Reference: CanIUse.com

Related

Flexbox and Left Sidebar Overlay

How do I get the left sidebar to overlay the content instead of pushing the content? I want it overlay on mobile but push on web layout. Flexbox is a little new to me so not sure if I need a different layout to do this or if this is possible with flexbox? I'm guessing I need to remove the sidebar from the flexbox and use a fixed relative layout ??
I'm also using angular but I removed the angular code just for simplicity so don't mind the extra divs please.
<div class="wrapper">
<header class="header">
header
</header>
<div class="main">
<div class="left-sidebar">
left sidebar
</div>
<div class="main-content-wrapper">
<div class="main-content">
<h3>Main </h3>
</div>
<footer class="footer">
<p>content</p>
<p>content</p>
<p>content</p>
</footer>
</div>
</div>
</div>
body,
html {
height: 100%;
overflow: hidden;
}
header {
min-height: 60px;
flex: none;
width: 100%;
background-color: silver;
}
.wrapper {
height: 100vh;
display: flex;
flex-direction: column;
.main {
flex: 1;
display: flex;
.main-content-wrapper {
display: flex;
flex: 1;
flex-direction: column;
overflow: auto;
.main-content {
padding: 2rem;
flex: 1;
background-color: antiquewhite;
}
footer {
background-color: silver;
min-height: 300px;
flex-shrink: 0;
}
}
}
}
.left-sidebar {
width: 0;
height: 100%;
overflow: auto;
flex: none;
&.active {
width: 250px;
}
.left-sidebar-content {
padding: 1rem;
}
}
Try this, I actually made pretty much a new structure, although not that different to yours, (did remove some elements just to work with less code); I'm using flexbox to make the whole wrapper a flex container, as well as media queries to indicate when the sidebar should push the content to the side, and when to overlap the content along with an overlay.
document.getElementById('toggleBtn').onclick = function() {
document.getElementById('sidebar').classList.toggle('active');
document.getElementById('overlay').classList.toggle('hidden');
}
document.getElementById('overlay').onclick = function() {
document.getElementById('overlay').classList.toggle('hidden');
document.getElementById('sidebar').classList.toggle('active');
}
body {
margin: 0;
}
.wrapper {
display: flex;
align-items: stretch;
height: 100vh;
}
.main-content-wrapper {
width: 100%;
}
#sidebar {
min-width: 230px;
max-width: 230px;
background-color: lightgray;
transition: all 0.3s;
height: 100vh;
margin-left: -230px;
/* top layer */
z-index: 3
}
#sidebar.active {
margin-left: 0px;
}
#media (max-width: 580px) {
#sidebar {
position: fixed;
top: 0;
/* top layer */
z-index: 3
}
.overlay {
background-color: rgba(0, 0, 0, 0.425);
width: 100vw;
height: 100vh;
z-index: 2;
top: 0;
left: 0;
position: absolute;
}
.overlay.hidden {
display: none;
}
}
<div class="wrapper">
<div class="left-sidebar" id="sidebar">
left sidebar
</div>
<div class="main-content-wrapper">
<div class="main-content">
<h3>Main </h3>
<button type="button" id="toggleBtn">Toggle</button>
</div>
</div>
<div class="overlay hidden" id="overlay"></div>
</div>

How can I create this effect with CSS only?

So I have HTML that CANNOT be changed whatsoever and I have a certain design that I'm aiming for. And I cannot use JS for this yet.
Just a note too, the container is actually the parent of all the other elements on the screen. However, the reason I drew like that is because that's the look i want, and if possible, as I scroll down the only thing that scrolls is element 3 and 4, and 3 and 1 stay fixed. At the moment the container is a flex box. The CSS that I have doesn't really do what I need it to do, but here it is:
html,
body {
min-height: 100%;
height: 100%;
}
#container {
display: flex;
flex-wrap: wrap;
height: 100%;
}
#intro {
background-color: yellow;
width: 20%;
order: 2;
}
#nav {
background-color: red;
width: 15%;
order: 1;
}
#content {
background-color: blue;
width: 65%;
order: 3
}
article {
order: 4;
}
footer {
background-color: magenta;
}
<div id="container">
<section id="intro">...</section>
<div id="content">...</div>
<aside id="nav">...</aside>
<article>
<li><img src="http://via.placeholder.com/100x100"></li>
<li><img src="http://via.placeholder.com/100x100"></li>
</article>
</div>
<footer>...</footer>
as I scroll down the only thing that scrolls is element 3 and 4
Using position: fixed we can make nav/intro/footer stay while content/article scroll.
The align-items: flex-end; on content/article will keep them right aligned, and flex-grow: 1 on container/content will make them fill the remaining space.
Stack snippet
html {
display: flex; /* IE min-height bug fix */
}
body {
width: 100%; /* using IE bug fix it need a width */
display: flex;
flex-direction: column;
min-height: 100vh; /* instead using precent all over */
margin: 0;
}
#container {
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: flex-end;
}
#nav {
position: fixed;
left: 0;
top: 0;
width: 15%;
height: calc(100% - 30px); /* make up for footer */
background-color: red;
}
#intro {
position: fixed;
left: 15%;
top: 0;
width: 20%;
height: calc(100% - 30px); /* make up for footer */
background-color: yellow;
}
#content {
background-color: lightblue;
width: 65%;
flex-grow: 1;
}
article {
margin-bottom: 30px; /* make up for footer */
}
article li {
list-style: none;
}
footer {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 30px;
background-color: magenta;
}
<div id="container">
<section id="intro">Intro</section>
<div id="content">
Content along with below article that will scroll and leave "Nav"/"Intro" fixed. <br>
Content along with below article that will scroll and leave "Nav"/"Intro" fixed. <br>
</div>
<aside id="nav">Nav</aside>
<article>
<li><img src="http://via.placeholder.com/100x100"></li>
<li><img src="http://via.placeholder.com/100x100"></li>
</article>
</div>
<footer>Footer</footer>
You are almost there.
Just set the article to have the same width as the #content and set the
justify-content:flex-end on the #container.
#container {
display: flex;
flex-wrap: wrap;
height: 100%;
justify-content: flex-end;
}
article {
order: 4;
width:65%;
}
html,
body {
min-height: 100%;
height: 100%;
}
#container {
display: flex;
flex-wrap: wrap;
height: 100%;
justify-content: flex-end;
}
#intro {
background-color: yellow;
width: 20%;
order: 2;
}
#nav {
background-color: red;
width: 15%;
order: 1;
}
#content {
background-color: blue;
width: 65%;
order: 3
}
article {
order: 4;
width: 65%;
background: cyan;
}
footer {
background-color: magenta;
}
<div id="container">
<section id="intro">...</section>
<div id="content">...</div>
<aside id="nav">...</aside>
<article>
<li><img src="http://via.placeholder.com/100x100"></li>
<li><img src="http://via.placeholder.com/100x100"></li>
</article>
</div>
<footer>...</footer>

How can I fix flex 50% layout issue in Safari?

I have a small example of flex layout and I have some problem with Safari (Version 10.1.2 (12603.3.8))
So there is a content and inside of it there are four boxes, with the layout of 2x2. At the bottom there is a footer section.
I would like to place the boxes inside the content div to fill its height by 50% height and width. But in Safari it seems that it ignores the footer section and it places the boxes to align to the full page.
So here it what I want to achieve and it works in chrome:
And this is what it looks like in Safari:
I managed to try it in High Sierra where there is a newer Safari (Ver. 11) and it WORKS. So it must be a bug, but can we handle this in Safari 10? Thank you!
Here it is my code
HTML:
* {
box-sizing: border-box;
}
body, html {
width: 100%;
height: 100%;
margin: 0;
}
.wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
.content {
display: flex;
flex-wrap: wrap;
width: 100%;
height: 100%;
background: white;
border: 1px solid tomato;
}
.box {
width: 50%;
height: 50%;
background: skyblue;
border: 1px solid black;
}
.footer {
opacity: 0.7;
flex: 0 0 auto;
height: 100px;
background: plum;
}
<div class="wrapper">
<div class="content">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<div class="footer"></div>
</div>
Since content is a flex column item you don't use height to make it fill its parent, you use flex-grow.
Remove the height on content and add flex-grow: 1
.content {
flex-grow: 1; /* added */
display: flex;
flex-wrap: wrap;
background: white;
border: 1px solid tomato;
}
Also no need for width: 100%, it does that by default
Stack snippet
* {
box-sizing: border-box;
}
body, html {
width: 100%;
height: 100%;
margin: 0;
}
.wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
.content {
flex-grow: 1;
display: flex;
flex-wrap: wrap;
background: white;
border: 1px solid tomato;
}
.box {
width: 50%;
background: skyblue;
border: 1px solid black;
}
.footer {
opacity: 0.7;
flex: 0 0 auto;
height: 100px;
background: plum;
}
<div class="wrapper">
<div class="content">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<div class="footer"></div>
</div>

Wanting height of two objects to match in spite of expanding content

I have a page that has three main section:
project-arrow-box
white-green
light-gray
project-arrow-box and light-gray are on the left side of the page and white-green is on the right. I am wanting light-gray and white-green's bottom to meet at the same point and touch my footer. My white-green's section will change size based if validation errors are made or whatever the case may be. On different screen sizes these two divs (light-gray and white-green) very in placement. Sometimes light-gray surpasses white-green and vise versa. Again, I want the end/bottom of those two divs to touch my footer regardless of the screen size.
I have tried adding bottom: 0; to both of light-gray and white-green, but this did not help. I have white-green's height set to auto, so it adapts to the expanding nature of this div. This happened before the footer, so the footer has nothing to do with any of these issues.
What can I do so this becomes possible?
.project_arrow_box {
position: relative;
/*background: rgb(69,186,149);*/
background: #00a16d;
border: 4px solid #00a16d;
width: 33%;
height: 800px;
z-index: 99;
}
#project-content-wrap {
margin: 30% 13%;
}
.white-green {
background-color: rgb(241, 250, 247);
width: 66.56%;
height: auto;
z-index: 55;
margin-left: 33.4%;
margin-right: 0;
position: absolute;
top: 0;
right: 0;
text-align: center;
}
#white-green-section {
position: relative;
left: 30%;
text-align: center;
opacity: 0;
}
.light-gray {
background-color: #E0E0E0;
width: 33.5%;
padding-top: 150px;
bottom: 0;
margin-bottom: 0;
}
.light-gray-container {
left: 15%;
position: relative;
width: 80%;
height: auto;
padding-bottom: 40px;
}
<div class="project_arrow_box">
<div id="project-content-wrap">
</div>
</div>
<div class="white-green">
<div id="white-green-section">
</div>
</div>
<div class="light-gray">
<div class="light-gray-container">
</div>
</div>
You can do this with Flexbox
body, html {
margin: 0;
padding: 0;
}
.container {
display: flex;
}
.left {
display: flex;
flex-direction: column;
flex: 0 0 33%;
}
.one {
background: #A1EB88;
flex: 100vh;
}
.two {
flex: 100vh;
background: #F2BB7C;
}
.three {
background: lightblue;
flex: 1;
}
footer {
height: 50px;
background: red;
width: 100%;
}
<div class="container">
<div class="left">
<div class="one">One</div>
<div class="two">Two</div>
</div>
<div class="three">Three</div>
</div>
<footer>Footer</footer>
Using Flexbox is likely the simplest way to pull that off:
HTML:
<div class="container">
<div class="left-container">
<div class="project-arrow-box">
<p>
</p>
</div>
<div class="light-grey">
<p>
</p>
</div>
</div>
<div class="white-green">
<p>
</p>
</div>
</div>
<div class="footer">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eos, vero?
</p>
</div>
CSS:
.container, .left-container, .white-green {
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6, BB7 */
display: -ms-flexbox; /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Safari 6.1+. iOS 7.1+, BB10 */
display: flex; /* NEW, Spec - Firefox, Chrome, Opera */
margin: 0;
}
.left-container {
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
flex-direction: column;
}
.project-arrow-box {
background-color: white;
flex-grow: 1;
padding: 1em;
}
.light-grey {
background-color: #aaa;
flex-grow: 1;
padding: 1em;
}
.white-green {
background-color: #ccFFcc;
flex-grow: 1;
padding: 1em;
}
.footer {
background-color: #000;
color: #fff;
}
See: https://jsfiddle.net/bL2ymhps/1/

Prevent flexbox items from overflowing into siblings

I am trying to make a one-pager site, which is working quite well. I have three sections that need to be fullscreen, that works. But when I resize the window to 500px width and make the height also shorter, the title from the second page comes up on the first page. Same thing happens with the title on the third page, this one displays on the second page.
Here is the codepen: http://codepen.io/anon/pen/jWaxMK
HTML:
<section>
<h2>Title 1</h2>
<div class="box">
</div>
<div class="box">
</div>
</section>
<section>
<h2 class="blue">Title 2</h2>
<div class="box circle"></div>
<div class="box circle"></div>
<div class="box circle"></div>
</section>
<section>
<h2 class="white">Title 3</h2>
</section>
CSS:
html,
body,main {
height: 100%;
margin: 0;
padding: 0;
}
section {
position: relative;
width: 100%;
height: 100%;
color: #ececec;
text-align: center;
display: flex; /* default: row nowrap */
flex-flow:row wrap;
justify-content: center;
align-items: center;
align-content: center;
}
section:nth-child(1) {
background: #06a2cb;
}
section:nth-child(2) {
background: #ececec;
}
section:nth-child(3) {
background: #F5E5D8;
}
h2{
margin-top:0;
font-family: 'Lobster';
margin: 1em;
flex: 0 0 100%;
color: black;
}
.box{
width: 250px;
height: 250px;
display: inline-block;
background-color: #ececec;
border-radius: 10px;
flex: 0 0 250px;
}
.circle{
border-radius: 50%;
background-color: white;
}
Any help is appreciated, thanks!
You can solve the problem by making all the section elements flex items, and giving them a minimum height.
Add this to your CSS:
body {
display: flex;
flex-direction: column;
}
section {
min-height: 100%; /* alternatively, try `min-height: 100vh` */
flex-shrink: 0;
}
Revised Codepen