Vertically centre variable-length content inside responsive boxes with fixed-pixel margins? - css

I'm looking for the simplest way to achieve a type of layout that looks simple:
...but actually involves a lot of criteria, many of which involve non-trivial CSS issues:
Vertically centred content in a div...
...where the content is of variable length (so distance from top and bottom can't be hard coded)...
...where the div is inside a selection of floated divs...
...where those divs have percentage widths to fill the screen on a responsive layout...
...where there is a fixed pixel gap between each div...
...where the divs have solid background colours or images and the background behind the divs isn't a known solid colour that can be re-applied
Various elements of this have been addressed in separate questions (for example vertically aligning floated divs, and pixel gaps between responsive percentage-width divs), but I couldn't find anything combining them.
Simplest means:
As few HTML wrappers as possible
Minimal extra Javascript (none if possible)
Minimal CSS that needs to change when breakpoints change the number of divs on each row
Minimal code, quirks, or fragile CSS trickery (e.g. relying on browser quirks that could change in future)
Minimal cross browser issues (ideally, should work on IE8+ with minimal IE-specific markup)

Here's the simplest I can come up with. Code snippet below. It's basically an existing method for vertically centring floats, putting the background on the middle wrapper, and setting fixed pixel gaps using padding on the outer wrapper rather than margins with box-sizing: border-box;.
JSBIN demo
Three HTML elements per block - which seems to be the minimum for any floated vertically centred content where the inner content doesn't have a known height.
No JS
Only the % width needs to change to change the number of blocks per line
If the text content is too big for the div, the div expands slightly without breaking the layout - overflow: hidden; can be applied if this is undesirable
Works on IE8 with no issues (fails on IE7 if any poor souls still need to support IE7)
.box-outer {
box-sizing: border-box;
float: left;
/* editable */
width: 50%;
height: 110px;
padding: 1px 1px 0px 0px; /* sets gap */
/* Padding does't collapse like margins - 1px all round gives 2px gaps */
}
.box {
width: 100%;
height: 100%;
box-sizing: border-box;
display: table; /* height doesn't fill without display: table */
/* editable: */
background: #99ffff;
padding: 8px;
}
.box-inner {
vertical-align: middle;
display: table-cell;
}
.boxes-container {
padding: 0px 0px 1px 1px; /* opposite of each box's padding */
/* editable: */
background: #ffffff url('http://freedesignfile.com/upload/2012/10/sky_clouds_03.jpg');
}
<div class="boxes-container clearfix">
<h2> Title </h2>
<div class="box-outer">
<div class="box">
<div class="box-inner">
Box content
</div>
</div>
</div>
<div class="box-outer">
<div class="box">
<div class="box-inner">
Box with longer content
</div>
</div>
</div>
<div class="box-outer">
<div class="box">
<div class="box-inner">
Box
</div>
</div>
</div>
<div class="box-outer">
<div class="box">
<div class="box-inner">
Box with significantly longer textual content
</div>
</div>
</div>
<br/>
<p style="text-align: center;"> <--- responsive width ---> </p>
</div>

Related

Equally distribute 3 divs within a div - without float

I have been reading widely about this but haven't been able to solve it to my satisfaction.
I have a div (<section>) that contains one <p> and 3 <div>s. I would like to distribute the 3 divs equally in one line so that the left border of the 1st div is on the left border of the document (<body>) and the the right border of the 3rd div on the right border of the document.
I don't want to use float because the backround-color would vanish.
I have tried flex but justify-content did not yield the expected outcome.
Here's the code on JSBIN.
Thank you!
You can use display: flex on the container, and set the width of the three div elements to take up one third (or as close as we can get) of its container. The container must have a set width (either pixel or percentage) for it to work.
#container {
display: flex;
height: 600px;
width: 600px;
background-color: lightgreen;
}
#container div {
border: 1px solid black;
margin: 0 10px;
width: 33.333333%;
}
#container div img {
width: 100%;
}
<div id="container">
<div id="content1">
I'm some content. Regardless of the width of this div, the content will move to the next line and stay within the div instead of overflowing.
<img src="http://i.imgur.com/P8z2H80.jpg">
</div>
<div id="content2">
I'm some more content. Regardless of the width of this div, the content will move to the next line and stay within the div instead of overflowing.
<img src="http://i.imgur.com/NfnBZAI.jpg">
</div>
<div id="content3">
I'm even more content. Regardless of the width of this div, the content will move to the next line and stay within the div instead of overflowing.
<img src="http://i.imgur.com/W8M37N2.jpg">
</div>
</div>

CSS Position element on bottom of container without removing it from flow

I have a container with 3 children elements.
<div class="container">
<img />
<div class="element1"></div>
<div class="element2 bottom"></div>
</div>
They must be positioned as shown on the diagram below:
image is in the top of the left column and nothing goes below it (it is the only element in the left column)
element1 is in the top of the right column
element2 is stick to the bottom of the right column (and must not collide with the element1 which is above it)
Does somebody know how to achieve such layout using pure CSS? Ideally I wouldn't like to add any markup, but I can do that if that's the only possible way.
The biggest problem I'm facing here is how to stick that second element (non-image) to the bottom of the container without removing it from the flow. Because if I use position: absolute and remove it from the flow, the elment above it can collide with it (both elements have unknown height).
Here's a pen to work on: http://codepen.io/anon/pen/yNwGvQ
I would suggest you to use two columns in your html and then use the property display: flex; for your right column as suggested in the article A Complete Guide to Flexbox.
http://codepen.io/AlexisBertin/pen/QboYyY
All the HTML:
<div class="container">
<div class="column column-left">
<div class="image">This is an image</div>
</div>
<div class="column column-right">
<div class="element1">This container has dynamic content so it's height is unknown and may change.<br/><br/> Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger.</div>
<div class="element2">This container also has dynamic content so it's height is unknown and may change</div>
</div>
</div>
Part of this CSS:
.column {
float: left;
height: 100%;
}
.column.column-left { width: 100px; }
.column.column-right {
width: calc(100% - 100px);
display: flex;
flex-direction: column;
justify-content: space-between;
}
Hope you get the idea. Good Luck'.
EDIT:
The easiest way to achieve this without declaring height to the container seems to only create a third parent div to the first block of the second column and define it as flex: 1; while the second block of this same second column would be define as flex: 0;.
http://codepen.io/anon/pen/yNwZmJ
More details explained in the comments.
The easiest solution I figured out is this one:
First you create this CSS:
.container {
width: 400px;
padding: 10px;
border: 1px solid red;
background-color: white;
}
.container > img {
float: left;
}
.container > div {
position: relative;
overflow: auto;
padding-left: 5px;
min-height: 120px;
}
.container > div > .bottom{
position: absolute;
bottom: 0;
display: block;
}
And then use these divs, depending on your content. The first one you use when you know your text is short:
<div class="container">
<img src="http://placehold.it/120x120">
<div>
<div>
<p>This container has dynamic content so it's height is unknown and may change.</p>
</div>
<div class="bottom">
<p>This container also has dynamic content so it's height is unknown and may change</div>
</div>
</div>
The second one you use when you know your text is long
<div class="container">
<img src="http://placehold.it/120x120">
<div>
<div>
<p>This container has dynamic content so it's height is unknown and may change.</p>
<p>Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger.</p>
</div>
<div>
<p>This container also has dynamic content so it's height is unknown and may change</p>
</div>
</div>
</div>
The difference is that you remove bottom class from the last div in your div that has long text.
Also in your CSS you can see .container > div{... min-height: 120px; ...}, you should set it to height of your image. In case you want the bottom text more down then you have to increase min-height to be bigger than your image height.
Here is it in action: http://codepen.io/anon/pen/YXgBXx

How to align 'n' divs horizontally WITH margin/padding?

I have the following HTML:
<div class="top_buttons">
<div class="top_button">
<img src="img/image1.png">
</div>
<div class="top_button">
<a href="#">
<img src="img/image2.png">
</a>
</div>
<div class="top_button">
<a href="#">
<img src="img/image3.png">
</a>
</div>
</div>
And the following CSS which works perfectly:
.top_buttons{
width: 100%;
height: auto;
margin: 0 auto;
overflow: hidden;
}
.top_button{
float: left;
width: 33.333%;/*Because I have 3 images with the same width, I want each to have 1/3 of the total width space available (mobile website)*/
}
The problem comes when I want to add some margin and/or padding to the inner divs (that is, the parents of the images). If I add a border/padding/margin to those divs, the above 33.333% I set will not work anymore as obviously each "top_button" div will contain not only the image, but the border/padding/margin added to the div too.
So, my temporary solution was to decrease that percentage to something like 31%. However, depending on the cell phone, more or less pixels will be left in the right side of the screen, as I am just trying to guess the length that the div will have with the extra border/padding/margin in terms of percentage. What I want is a solution that I can still use 33.333% even if I set borders/padding/margins to the inner divs, so I can have a pixel perfect horizontal alignment.
In short, how can I align 'n' divs horizontally knowing that the divs will have border/padding/margin too and that I do not know the total width of the "top_button*s*" div (because the width of the display varies depending on the phone screen).
Use box-sizing. Box-sizing allows you to add padding and border without worrying about breaking the width. However, margin will still break the alignment. In order to account for margin you need to subtract the amount of margin from the width of the element.
.top_button{
float: left;
width: 33.333%;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
The current standard box-model behavior is what you described in your question. If you define a width it doesn't mean the element will only be as wide as the width-value. Instead padding, etc. get added to it.
To achieve the more intuitive behavior you need the an alternate box-model. Now if you define width you're really defininf the width with all possible paddings, borders and so on.
The following CSS is exactly what you need for this. Note that this CSS will enable the alternate box model for all elements. You may also just define them for your needed elements, although you than have to mess with two different approaches in one project.
*, ::before, ::after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
If you run into cross-browser compatability problems with box-sizing, here's an alternative. It requires an additional "inner" div for each button, to which you can apply your margins.
HTML:
<div class="top_button">
<div class="top_button_contents">
<a href="#">
<img src="img/image2.png" /> some button text for testing
</a>
</div>
</div>
CSS:
.top_button {
float: left;
width: 33.333%;
}
.top_button .top_button_contents {
position:relative;
margin:0px 10px 0px 0px;
background-color:#CCC;
}
.top_button.last .top_button_contents {
margin:0px;
}
Here is a jFiddle.

Div spacing problem

I am trying to space out DIVs. I have five DIVs that are 30px wide and want to put these into another DIV that is 150px wide. Sounds simple but I find the five DIVs don't fit.
5*30 = 150 (but it requires a 166px outer div for them to fit inline)
I have this fiddle
<div class="A">
<div class="B" >a</div>
<div class="B" >b</div>
<div class="B" >c</div>
<div class="B" >d</div>
<div class="B" >e</div>
<div class="B" >f</div>
</div>
div.A { background-color: Red; width: 150px;}
div.B { display: inline-block; height: 20px; width: 30px;}
Is there something I am missing? I can't understand why the browsers space the way they do.
As you are turning the divs into inline elements, the other inline content will also come into play, i.e. the white space between the elements. You get a space between each div, which takes up a few pixels more.
If you remove the white space between the divs, there will be no spaces between them, and five elements fit in 150 pixels:
http://jsfiddle.net/SLq6z/1/
Can you replace display:inline-block with float:left without causing any other issues? it solves your current problem...
div.B { float:left; height: 20px; width: 30px;}
Use float:left property for both your classes A and B ;).

CSS content overflow out of box IE <6

I have a div that holds some text, it has a background with a border, but for some reason the box is not expanding to the text, even with overflow: auto; here is my script for the box as well as a picture:
.box { background: #ffdcba; border: 1px solid #f78d25; display: block; clear: both; margin: 4px 0px; padding-left: 15px; overflow: auto; }
the divs inside are just floating, left and right, and have display: inline on them. heres a picture:
http://i45.tinypic.com/2woj1br.gif
A floated box will not expand to fit its contents. You need to add a clearing element after your content. <br> is usually good.
YOu don't specify the exact construction of the HTML, but I"m asssuming you've got something like this:
<div class="box">
<div style="float: left">test subject></div>
<div style="float: right">
<div>ASD</div>
etc...
</div>
</div>
Floating elements removes them from the regular flow and will cause the "overflow" you are seeing. You need to add a non-floated element below the floated parts to force the containing div.box to "expand" to contain the floats:
<div class="box">
<div style="blah blah" ....
etc....
<br style="clear: both" />
</div>
As well, the overflow: auto will not have any effect on your .box style, because it does not specify any height or width - it will naturally just expand to contain whatever content you put in there. To force a scrollbar to appear, you need to put in either height or width styling, and enough content to exceed either of the limits.

Resources