Unexpected result from margin-top next to a floated element - html

I'm having issues with a layout like this:
.wrapper {
clear: both;
background-color: #ccc;
}
.wrapper+.wrapper {
margin-top: 50px;
}
.side,
.main {
height: 100px;
padding: 10px;
box-sizing: border-box;
margin-top: 20px;
}
.box {
padding: 10px;
}
.top {
background: yellow;
}
.side {
width: 100px;
float: left;
background: lightblue;
}
.main {
margin-left: 100px;
background: lightgreen;
}
<div class="wrapper">
<div class="top"></div>
<div class="side">side</div>
<div class="main">main</div>
</div>
<div class="wrapper">
<div class="top">
<div class="box">top</div>
</div>
<div class="side">side</div>
<div class="main">main</div>
</div>
The .main and .side elements need to be aligned. As you can see in the above snippet, everything is fine unless the .top element has no height in which case the margin-top rule causes them to be skewed. All of the following "fix" the issue but each has a drawback:
adding border to .wrapper (I might be able to live with a transparent border but I really don't like this since it feels like a dirty hack and I'd rather not add a border. For some reason the border needs to have a width of at least 1px or this doesn't work)
.wrapper {
clear: both;
background-color: #ccc;
border: 1px solid #000;
}
.wrapper+.wrapper {
margin-top: 50px;
}
.side,
.main {
height: 100px;
padding: 10px;
box-sizing: border-box;
margin-top: 20px;
}
.box {
padding: 10px;
}
.top {
background: yellow;
}
.side {
width: 100px;
float: left;
background: lightblue;
}
.main {
margin-left: 100px;
background: lightgreen;
}
<div class="wrapper">
<div class="top"></div>
<div class="side">side</div>
<div class="main">main</div>
</div>
adding overflow: hidden to .wrapper (this hides parts of some elements and causes others to fall in the wrong place)
adding overflow: auto to .wrapper (this adds scroll bars in some scenarios)
Those last two are not apparent in my snippet but in the real world application they cause problems as mentioned here.
I have a strong suspicion the issue is related to Why doesn't the height of a container element increase if it contains floated elements? and CSS container doesn't stretch to accommodate floats but I've tried many of those suggestions and none seem to quite solve the issue - perhaps because one of my divs is floated and the other is not.
Since this is part of a large application, I don't want to drastically change the layout, just have some css that will keep .main and .side aligned regardless of the content before those elements.

You can make the main element to be inline-block and use calc to set the width. This shouldn't affect your layout a lot and you will get the correct output:
.main {
width:calc(100% - 100px);
display:inline-block;
background: lightgreen;
}
Full code:
.wrapper {
background-color: #ccc;
clear: both;
}
.wrapper+.wrapper {
margin-top: 50px;
}
.side,
.main {
height: 100px;
padding: 10px;
box-sizing: border-box;
margin-top: 20px;
}
.box {
padding: 10px;
}
.top {
background: yellow;
}
.side {
width: 100px;
float: left;
background: lightblue;
}
.main {
width:calc(100% - 100px);
display:inline-block;
background: lightgreen;
}
<div class="wrapper">
<div class="top"></div>
<div class="side">side</div>
<div class="main">main</div>
</div>
<div class="wrapper">
<div class="top">
<div class="box">top</div>
</div>
<div class="side">side</div>
<div class="main">main</div>
</div>
Another hacky idea is to make sure your top element is never empty:
.top:empty {
font-size:0;
}
.top:empty::before {
content: "\80"; /* a random character */
}
Full code
.wrapper {
background-color: #ccc;
clear: both;
}
.wrapper+.wrapper {
margin-top: 50px;
}
.side,
.main {
height: 100px;
padding: 10px;
box-sizing: border-box;
margin-top: 20px;
}
.box {
padding: 10px;
}
.top {
background: yellow;
}
.side {
width: 100px;
float: left;
background: lightblue;
}
.main {
margin-left: 100px;
background: lightgreen;
}
.top:empty {
font-size:0;
}
.top:empty::before {
content: "\80"; /* a random character */
}
<div class="wrapper">
<div class="top"></div>
<div class="side">side</div>
<div class="main">main</div>
</div>
<div class="wrapper">
<div class="top">
<div class="box">top</div>
</div>
<div class="side">side</div>
<div class="main">main</div>
</div>
You can also consider the same trick but using a pseudo element on the main wrapper:
.wrapper::before {
content: "\80"; /* a random character */
display:block;
font-size:0;
}
Full code
.wrapper {
background-color: #ccc;
clear: both;
}
.wrapper+.wrapper {
margin-top: 50px;
}
.side,
.main {
height: 100px;
padding: 10px;
box-sizing: border-box;
margin-top: 20px;
}
.box {
padding: 10px;
}
.top {
background: yellow;
}
.side {
width: 100px;
float: left;
background: lightblue;
}
.main {
margin-left: 100px;
background: lightgreen;
}
.wrapper::before {
content: "\80"; /* a random character */
display:block;
font-size:0;
}
<div class="wrapper">
<div class="top"></div>
<div class="side">side</div>
<div class="main">main</div>
</div>
<div class="wrapper">
<div class="top">
<div class="box">top</div>
</div>
<div class="side">side</div>
<div class="main">main</div>
</div>
You can also make the wrapper inline-block with a width equal to 100% and it will behave almost the same as a block element:
.wrapper {
background-color: #ccc;
display:inline-block;
width:100%;
vertical-align:top; /* avoid some unwanted white space issue*/
}
.wrapper+.wrapper {
margin-top: 50px;
}
.side,
.main {
height: 100px;
padding: 10px;
box-sizing: border-box;
margin-top: 20px;
}
.box {
padding: 10px;
}
.top {
background: yellow;
}
.side {
width: 100px;
float: left;
background: lightblue;
}
.main {
margin-left: 100px;
background: lightgreen;
}
<div class="wrapper">
<div class="top"></div>
<div class="side">side</div>
<div class="main">main</div>
</div>
<div class="wrapper">
<div class="top">
<div class="box">top</div>
</div>
<div class="side">side</div>
<div class="main">main</div>
</div>
For the explanation, you are facing a margin collpasing issue like described in the specification:
Two margins are adjoining if and only if:
both belong to in-flow block-level boxes that participate in the same block formatting context
no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for this purpose.)
both belong to vertically-adjacent box edges, i.e. form one of:
top margin of a box and top margin of its first in-flow child

You can do this much more elegantly with grid. Here is the grid code:
.wrapper {
display: grid;
grid-template-areas:
"top top"
"side main";
grid-template-columns: 100px 1fr;
}
.top{grid-area:top}
.side{grid-area:side}
.main{grid-area:main}
Notice how many other elements I was able to comment out and still keep the desired layout.
.wrapper {
/*clear: both;*/
background-color: #ccc;
}
.wrapper+.wrapper {
margin-top: 50px;
}
.side,
.main {
height: 100px;
padding: 10px;
box-sizing: border-box;
/*margin-top: 20px;*/
}
.box {
padding: 10px;
}
.top {
background: yellow;
}
.side {
/*width: 100px;
float: left;*/
background: lightblue;
}
.main {
/*margin-left: 100px;*/
background: lightgreen;
}
.wrapper {
display: grid;
grid-template-areas:
"top top"
"side main";
grid-template-columns: 100px 1fr;
}
.top{grid-area:top}
.side{grid-area:side}
.main{grid-area:main}
<div class="wrapper">
<div class="top"></div>
<div class="side">side</div>
<div class="main">main</div>
</div>
<div class="wrapper">
<div class="top">
<div class="box">top</div>
</div>
<div class="side">side</div>
<div class="main">main</div>
</div>

Related

How to adjust the spaces between elements in inline-block layout?

I am trying to create a layout without using grid or flexbox, I am using display: inline-block to achieve that but i have a problem with adjusting spaces.
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.wrapper {
text-align : justify;
}
.wrapper > div {
display: block;
margin-top: 5px;
}
.header {
background: lightgreen;
margin-top: 0;
}
.footer {
background: #eee;
}
.main > div {
display: inline-block;
width: 49%;
height: 20vh;
background: #eee;
}
<div class="wrapper">
<div class="header">header</div>
<div class="main">
<div class="item">item1</div>
<div class="item">item2</div>
<div class="item">item3</div>
<div class="item">item4</div>
</div>
<div class="footer">footer</div>
</div>
I am trying to achieve the same effect as justify-content: space-between in flexbox
but i got elements that are not aligned well in the layout.
I can fix the spaces around item4 but using margin-left but i don't like this solution.
Add a hidden element to trigger the justify alignment for the last line but you will need to use a negative margin-bottom to remove the extra line added.
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.wrapper {
text-align : justify;
}
.wrapper > div {
display: block;
margin-top: 5px;
}
.header {
background: lightgreen;
margin-top: 0;
}
.footer {
background: #eee;
}
.main > div {
display: inline-block;
width: 49%;
height: 20vh;
background: #eee;
}
.main:after {
content:"";
display:inline-block;
width:5%;
height:50px; /* we consider a bigger value than the line-height*/
}
.main {
margin-bottom:-50px; /*the same value defined in the pseuo element*/
}
<div class="wrapper">
<div class="header">header</div>
<div class="main">
<div class="item">item1</div>
<div class="item">item2</div>
<div class="item">item3</div>
<div class="item">item4</div>
</div>
<div class="footer">footer</div>
</div>
Or use font-size:0 trick to avoid that extra line:
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.wrapper {
text-align : justify;
}
.wrapper > div {
display: block;
margin-top: 5px;
}
.header {
background: lightgreen;
margin-top: 0;
}
.footer {
background: #eee;
}
.main > div {
display: inline-block;
width: 49%;
height: 20vh;
background: #eee;
font-size:initial;
}
.main:after {
content:"";
display:inline-block;
width:5%;
}
.main {
font-size:0;
}
<div class="wrapper">
<div class="header">header</div>
<div class="main">
<div class="item">item1</div>
<div class="item">item2</div>
<div class="item">item3</div>
<div class="item">item4</div>
</div>
<div class="footer">footer</div>
</div>
I don't think you can achieve this without setting the spacing. As you probably know, you can get better control of inline-block layouts setting negative letter-spacing on the container and resetting on the item: .main {letter-spacing: -4px} .item {letter-spacing: 0} (You need to test if 4px works).
Or (since we're not using flex :), you could float odd and even divs left and right: .item:nth-child(odd) {clear: left; float: left} .item:nth-child(even) {float: right}

Dynamic grid system with different widths css3?

I need to perform a dynamic grid system like this:
Each section is an article that contains an image, a title and a link/button to that article.
The problem is that each section is loaded dynamically and i only have the html of the section so i need to put each section on the correct position dynamically from the CSS. The one i know is that there are 5 sections.
The html code of each section and the container of all the sections is this:
<section class="scroll">
<!-- ARTICLES -->
<!-- ARTICLE -->
<div class="article-content">
<img class="article-image" src="${item.imgPath}" />
<div class="article-texts">
<h1 class="article-title">${item.title}</h1>
<a class="article-button" href="${item.link}.html" role="button">Read Article ></a>
</div>
</div>
<div class="clear"></div>
<!-- END ARTICLE -->
<!-- END ARTICLES -->
</section>
If you have control over the dimensions of your sections, you can use a fixed width container and float the sections inside that. Clear the float on the fourth section.
Example Fiddle: http://jsfiddle.net/abhitalks/mbuf9957/3/
Example Snippet:
* { box-sizing: border-box; padding: 0; margin:0; }
div { width: 380px; overflow: hidden; }
section { border: 1px solid #666; float: left; }
section:nth-child(1) { width: 240px; height: 240px; }
section:nth-child(2) { width: 120px; height: 120px; }
section:nth-child(3) { width: 120px; height: 120px; }
section:nth-child(4) { width: 120px; height: 120px; clear: left; }
section:nth-child(5) { width: 240px; height: 120px; }
<div>
<section>1</section>
<section>2</section>
<section>3</section>
<section>4</section>
<section>5</section>
</div>
Since you have tagged this as CSS3, I think Flexbox would be an option. You could set display:flex on the parent and then have percentage widths for each box's flex-basis and set the flex-grow property to the amount of space, relative to other boxes, you want them to take up in the container and set flex-shrink to 0 since you don't need them to shrink.
CSS/HTML:
.grid-system {
/* Uncomment the next line to see the container */
/* border:1px solid black; */
}
.grid-system .box-width-2 {
border:1px solid black;
-webkit-flex:2 0 65%;
flex: 2 0 65%;
}
.grid-system .box-width-1 {
border:1px solid black;
-webkit-flex:1 0 32%;
flex: 1 0 32%;
}
.grid-system .box-height-2 {
-webkit-flex-grow:2;
flex-grow:2;
}
.grid-system .box-height-1 {
-webkit-flex-grow:1;
flex-grow:1;
}
.grid-system .flex-row {
display:-webkit-flex;
display:flex;
-webkit-flex-flow:row nowrap;
flex-flow:row nowrap;
-webkit-justify-content:flext-start;
justify-content:flex-start;
}
.grid-system .flex-column {
display:-webkit-flex;
display:flex;
-webkit-flex-flow:column nowrap;
flex-flow:column nowrap;
width:32%;
}
.grid-system .flex-row > div {
margin:0.5%
}
.grid-system .box-width-1.box-height-1 {
margin-bottom:0.5%;
-webkit-flex-grow:1;
flex-grow:1;
}
.grid-system .box-width-1.box-height-1.end {
margin-bottom:0px;
}
<div class="grid-system">
<div class="flex-row">
<div class="box-width-2 box-height-2">1</div>
<div class="flex-column">
<div class="box-width-1 box-height-1">2</div>
<div class="box-width-1 box-height-1 end">3</div>
</div>
</div>
<div class="flex-row">
<div class="box-width-1">4</div>
<div class="box-width-2">5</div>
</div>
</div>
jsFiddle
A solution only involving floats can reproduce your layout. Compatibility IE8+ (and even below but nobody cares). Pseudo-class :nth-child() (compat. IE9+) is used here to give an arbitrary width and height for demo, you'll have your own layout in real conditions.
* { box-sizing: border-box; }
div { width: 360px; }
section { border: 1px solid #666; }
.left { float: left; }
.right { float: right; }
.clear { clear: both; }
section:nth-child(1) { width: 240px; height: 240px; }
section:nth-child(2) { width: 120px; height: 100px; }
section:nth-child(3) { width: 120px; height: 80px; }
section:nth-child(4) { width: 200px; height: 120px; }
section:nth-child(5) { width: 160px; height: 100px; }
<div>
<section class="left">1</section>
<section class="right">2</section>
<section class="right">3</section>
<section class="left clear">4</section>
<section class="right">5</section>
</div>

Different width divs in the same row

I'm trying to put 3 divs(with different widths respectively : 10%,70% & 20%) in the same row but the middle one always go full width of the page.
Here is my code:
#left-bar {
width: 10%;
background-color: #FF0000;
}
#middle-bar {
width: 70%;
background-color: #6600FF;
}
#right-bar {
width: 20%;
background-color: #99FF99;
}
<div class="row">
<div id="left-bar"></div>
<div id="middle-bar"></div>
<div id="right-bar"></div>
</div>
By default div is a block level element that's why they aren't in the same row.
You have a few options to fix this:
option with CSS flexbox:
.row {
display: flex;
width: 100%
}
.row>div {
/*demo purposes */
height: 30px;
}
#left-bar {
flex: 0 10%;
background-color: #F00;
}
#middle-bar {
flex: 1;
background-color: #60F;
}
#right-bar {
flex: 0 20%;
background-color: #9F9;
}
<div class="row">
<div id="left-bar"></div>
<div id="middle-bar"></div>
<div id="right-bar"></div>
</div>
(old options)
option with display:inline-block
.row {
/*fix inline-block gap*/
font-size: 0;
}
.row>div {
display: inline-block;
/*demo purposes */
height: 30px;
}
#left-bar {
width: 10%;
background-color: #F00;
}
#middle-bar {
width: 70%;
background-color: #60F;
}
#right-bar {
width: 20%;
background-color: #9F9;
}
<div class="row">
<div id="left-bar"></div>
<div id="middle-bar"></div>
<div id="right-bar"></div>
</div>
option with display:table-[cell]
.row {
display: table;
width: 100%
}
.row>div {
display: table-cell;
/*demo purposes */
height: 30px;
}
#left-bar {
width: 10%;
background-color: #F00;
}
#middle-bar {
width: 70%;
background-color: #60F;
}
#right-bar {
width: 20%;
background-color: #9F9;
}
<div class="row">
<div id="left-bar"></div>
<div id="middle-bar"></div>
<div id="right-bar"></div>
</div>
The table-cell option actually doesn't work in some internet explorer versions. But the same result can be achieved with the property float:
#left-bar{
width:10%;
background-color: #FF0000;
}
#middle-bar{
width:70%;
background-color: #6600FF;
}
#right-bar{
width:20%;
background-color: #99FF99;
}
.row > div {float:left;}
<div class="row">
<div id="left-bar">a</div>
<div id="middle-bar">b</div>
<div id="right-bar">c</div>
</div>
#left-bar{
width:10%;
background-color: #FF0000;
float:left;
}
#middle-bar{
width:70%;
background-color: #6600FF;
float:left;
}
#right-bar{
width:20%;
background-color: #99FF99;
float:left;
}
If that doesn't work, please provide more html and css because the problem will be somewhere else. Also, verify that you have heights set for your divs.

Div content in wrong position

I'm trying to put 3 divs in the same row as the following code.
My CSS and HTML:
.row {
display: table;
width: 100%
}
.row > div {
display: table-cell;
height:30px; /*demo purposes */
}
#left-bar {
width: 10%;
background-color: #FF0000;
}
#middle-bar {
width: 70%;
background-color: #6600FF;
}
#right-bar {
width: 20%;
background-color: #99FF99;
}
<div class="row">
<div id="left-bar"> here I have an accordion </div>
<div id="middle-bar"> heve a have my canvas </div>
<div id="right-bar"> and here I have an editor</div>
</div>
Somehow the content of the middle-bar(my canvas) is positioned in the correct place, but the other two divs contents are in the bottom of the page as you can see here see photo. Do you guys know why this is happening?
After discussing the project further with you in the comments, and in chat, I think you should take an approach that uses flexbox instead. The code is fairly straight forward:
.container {
display: flex;
}
.left { flex-basis: 10%; background: #F99; }
.right { flex-basis: 20%; background: #99F; }
.middle { flex-basis: 70%; background: #9F9; }
<div class="container">
<div class="left">L</div>
<div class="middle">C</div>
<div class="right">R</div>
</div>
I only managed width.
There's nothing problematic see this.
.row {
display: table;
width: 100%
}
.row > div {
display: table-cell;
height:30px; /*demo purposes */
}
#left-bar {
width: 20%;
background-color: #FF0000;
}
#middle-bar {
width: 60%;
background-color: #6600FF;
}
#right-bar {
width: 20%;
background-color: #99FF99;
}
<div class="row">
<div id="left-bar"> here I have an accordion </div>
<div id="middle-bar"> heve a have my canvas </div>
<div id="right-bar"> and here I have an editor</div>
</div>

Div fill space between 2 divs

how can I make all divs get on the same line and fill div#2 the space between the left floated div#1 and right floated div#3?
Maybe flex will help you, here is an JSFiddle.
HTML
<div class="parent">
<div class="div1"></div>
<div class="div2"></div>
<div class="div3"></div>
</div>
CSS
.parent {
display: -webkit-flex;
display: flex;
}
.div1 {
width: 30px;
height: 30px;
background: #FFCC99;
}
.div3 {
width: 30px;
height: 30px;
background: #FCF305;
}
.div2 {
-webkit-flex: auto;
flex: auto;
height: 30px;
background: #CCFFCC;
}
You could use display: table for this kind of implementation (note this is not using tables for layout):
html,
body {
margin: 0;
padding: 0;
}
.wrap {
display: table;
width: 100vw;
}
.one {
display: table-cell;
height: 50px;
width: 20%;
background: red;
}
.two {
display: table-cell;
height: 50%;
width: 60%;
background: cornflowerblue;
}
.three {
display: table-cell;
background: lime;
height: 50px;
}
<div class="wrap">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
Notice how I haven't set a width on the last element, yet it's filling the rest of the space available?
Here's a dummy implementation:
<div id="l"></div>
<div id="r"></div>
<div id="c"></div>
<style>
#l {
float: left;
width:30%;
}
#r {
float: right;
width: 30%;
}
#c {
margin: 0 auto;
width: 40%;
}
</style>