Left aligned last row in centered grid of elements
CssGrid LayoutText AlignCss Problem Overview
I have a bunch of same-size blocks set to display:inline-block
inside a div that has text-align:center
set to align the blocks.
| _____ _____ _____ _____ |
| | | | | | | | | |
| | 1 | | 2 | | 3 | | 4 | |
| |_____| |_____| |_____| |_____| |
| _____ _____ _____ _____ |
| | | | | | | | | |
| | 5 | | 6 | | 7 | | 8 | |
| |_____| |_____| |_____| |_____| |
| |
The blocks fill the div horizontally, and as the browser window shrinks, some blocks break to new lines, creating more rows and less columns. I want everything to still remain centered, with the last row aligned flush to the left, like this :
| _____ _____ _____ |
| | | | | | | |
| | 1 | | 2 | | 3 | |
| |_____| |_____| |_____| |
| _____ _____ _____ |
| | | | | | | |
| | 4 | | 5 | | 6 | |
| |_____| |_____| |_____| |
| _____ _____ |
| | | | | |
| | 7 | | 8 | |
| |_____| |_____| |
| |
What currently happens is this:
| _____ _____ _____ |
| | | | | | | |
| | 1 | | 2 | | 3 | |
| |_____| |_____| |_____| |
| _____ _____ _____ |
| | | | | | | |
| | 4 | | 5 | | 6 | |
| |_____| |_____| |_____| |
| _____ _____ |
| | | | | |
| | 7 | | 8 | |
| |_____| |_____| |
| |
I cannot add extra filler divs like one suggestion, because there could be any number of blocks, and the amount of rows and columns will vary depending on browser width. I also cannot style block #7 directly, for the same reason. The blocks must always remain centered no matter how many columns.
Here is a pen to better demonstrate:
http://codepen.io/anon/pen/IDsxn
Is this possible? I feel like it sure should be. I would prefer not to use flexbox as it is only ie10+, and I'd like ie9+. I would really like a pure CSS solution, but if you tell me JS is the only way, I'd love to see that in action.
For reference - similar questions, though none were thoroughly explained:
https://stackoverflow.com/questions/12119978/css-left-align-the-last-row-of-images-in-a-centered-div
Css Solutions
Solution 1 - Css
Solution with display inline-block
This adaptive grid is much simpler : less markup and less CSS so it will be easier to implement in a production site and adapt to your exact needs.
=>> DEMO <<= (resize the result window to see the effect)
html, body {
margin:0;
padding:0;
}
#container{
font-size:0;
margin:0 auto;
width:1000px;
}
.block {
font-size:20px;
width: 150px;
height: 150px;
margin:25px;
background: gold;
display:inline-block;
}
@media screen and (max-width: 430px) {
#container{
width:200px;
}
}
@media screen and (min-width: 431px) and (max-width: 630px) {
#container{
width:400px;
}
}
@media screen and (min-width: 631px) and (max-width: 830px) {
#container{
width:600px;
}
}
@media screen and (min-width: 831px) and (max-width: 1030px) {
#container{
width:800px;
}
}
<div id="container">
<div class="block">1</div>
<div class="block">2</div>
<div class="block">3</div>
<div class="block">4</div>
<div class="block">5</div>
<div class="block">6</div>
<div class="block">7</div>
<div class="block">8</div>
<div class="block">9</div>
<div class="block">10</div>
<div class="block">11</div>
<div class="block">12</div>
<div class="block">13</div>
</div>
It involves :
-
4 media queries for 200px wide blocks and a container expandable to 1000px. Depending on the width of your grid elements and the total width of your container you may have to make less or more
-
removing white-spaces between inline-block elements (in the following demo I used the font-size technique but you can use other ones (see How to remove the space between inline-block elements? for other techniques)
-
fixed margins between blocks
The number of blocks in one line adapts to the size of the container. The text-align
property stay to default value left
so the last items are aligned to the left.
Floats with adaptive margins between both blocks and container
=>> DEMO <<= (you need to resize the result window under 750px to see it in action)
html, body {
margin:0;
padding:0;
min-width:150px;
}
.wrap {
float:left;
position:relative;
}
.foto {
width: 150px;
height: 150px;
background: gold;
position:absolute;
}
#warning{display:none;}
@media screen and (min-width: 631px) {
.wrap {
width:20%;
padding-bottom:25%;
}
.wrap:nth-child(4n+2), .wrap:nth-child(4n+3){
}
.wrap .foto {
top:-75px;
margin-top:100%;
right:-30px;
}
.wrap:nth-child(4n+2){
margin:0 5% 0 7.5%;
}
.wrap:nth-child(4n+3){
margin-right:7.5%;
}
.wrap:nth-child(4n+2) .foto{
left:50%;
margin-left:-75px;
}
.wrap:nth-child(4n+3) .foto{
right:50%;
margin-right:-75px;
}
.wrap:nth-child(4n) .foto{
left:-30px;
}
#container{
margin-top:-45px;
}
}
@media screen and (min-width: 481px) and (max-width: 631px) {
.wrap {
width:25%;
padding-bottom:33.3%;
}
.wrap:nth-child(3n+2){
margin:0 12.5%;
}
.wrap .foto {
top:-75px;
margin-top:100%;
right:-37px;
}
.wrap:nth-child(3n+2) .foto{
left:50%;
margin-left:-75px;
}
.wrap:nth-child(3n) .foto{
left:-37px;
}
#container{
margin-top:-37px;
}
}
@media screen and (min-width: 331px) and (max-width: 480px) {
.wrap {
width:33.3%;
padding-bottom:50%;
clear:left;
}
.wrap:nth-child(even) {
float:right;
clear:right;
}
.wrap .foto {
top:-75px;
margin-top:100%;
right:-50px;
}
.wrap:nth-child(even) .foto {
left:-50px;
}
.wrap:nth-child(4n+3) .foto, .wrap:nth-child(4n+4) .foto {
bottom:-75px;
margin-bottom:100%;
}
#container{
margin-top:-25px;
}
}
@media screen and (max-width: 330px) {
.wrap {
width:50%;
padding-bottom:100%;
clear:left;
}
.wrap:nth-child(odd) .foto {
right:-75px;
bottom:0;
bottom:-75px;
margin-bottom:100%;
}
.wrap:nth-child(even) .foto {
top:0px;
right:-75px;
top:-75px;
margin-top:100%;
}
}
@media screen and (min-width: 751px) {
#warning{
color:#fff;
display:block;
position:fixed;
width:100%;
height:50%;
top:25%;
left:0;
background:#000;
text-align:center;
font-size:30px;
}
<div id="container">
<div class="wrap"><div class="foto">1</div></div>
<div class="wrap"><div class="foto">2</div></div>
<div class="wrap"><div class="foto">3</div></div>
<div class="wrap"><div class="foto">4</div></div>
<div class="wrap"><div class="foto">5</div></div>
<div class="wrap"><div class="foto">6</div></div>
<div class="wrap"><div class="foto">7</div></div>
<div class="wrap"><div class="foto">8</div></div>
<div class="wrap"><div class="foto">9</div></div>
<div class="wrap"><div class="foto">10</div></div>
<div class="wrap"><div class="foto">11</div></div>
<div class="wrap"><div class="foto">12</div></div>
<div class="wrap"><div class="foto">13</div></div>
<div class="wrap"><div class="foto">14</div></div>
<div class="wrap"><div class="foto">15</div></div>
</div>
<!-- FOLLOWING JUST FOR THE DEMO -->
<div id="warning">I haven't written the code for windows bigger than 751px.<br/>
You must resize this window under 751px.</div>
This technique involves :
- floats
position:absolute;
:nt-child()
css selector- media queries
It centers the blocks in their container and gives the same margin on the top/left/tight/bottom of all the blocks + sides of the container. As this solution uses floats, the last row is aligned to the left.
The number of blocks in one line adapts to the width of the window.
Solution 2 - Css
Here's a very simple JavaScript (and some small changes in your CSS) solution for you:
It's working fine for me.
CSS:
.container {
margin: 0 auto;
max-width:960px;
background-color: gold;
}
.block {
background-color: #ddd;
border:1px solid #999;
display: block;
float: left;
height: 100px;
margin: 4px 2px;
width: 100px;
}
JavaScript:
$(document).ready(function(){
setContainerWidth();
});
$(window).resize(function(){
setContainerWidth();
});
function setContainerWidth()
{
$('.container').css('width', 'auto'); //reset
var windowWidth = $(document).width();
var blockWidth = $('.block').outerWidth(true);
var maxBoxPerRow = Math.floor(windowWidth / blockWidth);
$('.container').width(maxBoxPerRow * blockWidth);
}
jQuery is required :)
Solution 3 - Css
For what it's worth: It's now 2017 and the grid layout module does this out of the box
* {
margin:0;
padding:0;
}
.container {
display: grid;
grid-template-columns: repeat(auto-fill, 100px);
grid-gap: 10px;
justify-content: center;
align-content: flex-start;
margin: 0 auto;
text-align: center;
margin-top: 10px;
}
.block {
background-color: #ddd;
border: 1px solid #999;
height: 100px;
width: 100px;
}
<div class="container">
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
<div class="block">Foo</div>
</div>
If the browser support suits you - then use grid. If not, then read on....
As mentioned in @Web-tiki's answer, the best you can do with CSS is with a series of media queries.
That being said, if you are using a preprocessor such as LESS - this isn't such a difficult or error-prone task. (although, yes, the CSS will still be long and ugly)
UPDATED CODEPEN (Resize the window to see the results)
Here's how to take advantage of LESS to set up the media queries:
First set up some less variables according to the design which you need:
@item-width:100px;
@item-height:100px;
@marginV: 4px;
@marginH: 2px;
@min-cols:2;
@max-cols:9; //set an upper limit of how may columns you want to write the media queries for
Then:
Set up an iteration mixin like this: (You can paste this code into http://less2css.org)
.loopingClass (@index-width) when (@index-width <= @item-width * @max-cols) {
@media (min-width:@index-width) {
.container{
width: @index-width;
}
}
.loopingClass(@index-width + @item-width + 2*@marginH);
}
.loopingClass (@item-width * @min-cols + @min-cols*@marginH*2);
The above mixin will spit out a series of media queries in the form:
@media (min-width: 208px) {
.container {
width: 208px;
}
}
@media (min-width: 312px) {
.container {
width: 312px;
}
}
@media (min-width: 416px) {
.container {
width: 416px;
}
}
@media (min-width: 520px) {
.container {
width: 520px;
}
}
@media (min-width: 624px) {
.container {
width: 624px;
}
}
@media (min-width: 728px) {
.container {
width: 728px;
}
}
@media (min-width: 832px) {
.container {
width: 832px;
}
}
With remaining CSS (LESS):
.container {
margin: 0 auto;
text-align: center;
overflow: auto;
min-width: @min-cols * @item-width;
max-width: @max-cols * @item-width;
display: block;
list-style:none;
}
.block {
background-color: #ddd;
border:1px solid #999;
box-sizing:border-box;
float: left;
height: @item-height;
width: @item-width;
margin:@marginV @marginH;
}
... you get the desired result.
...and it's super easy to customize the layout:
All I need to do is change the variables that I used in the LESS mixin according to my needs - I get the exact layout that I'm after.
Solution 4 - Css
Try this with simple css:
CSS:
.row{text-align:center;font-size:0;} .block{text-align:center;display:inline-block;width:150px;height:15px;margin:5px; border:1px solid #dddddd;font-size:13px;}
HTML:
<div class="row">
<div class="block"></div>
</div>
.row{text-align:center;font-size:0;}
.block{text-align:center;display:inline-block;width:150px;height:150px;margin:5px; border:1px solid #dddddd;font-size:13px;line-height:150px;}
<div class="row">
<div class="block">1</div>
<div class="block">2</div>
<div class="block">3</div>
<div class="block">4</div>
<div class="block">5</div>
<div class="block">6</div>
<div class="block">7</div>
<div class="block">8</div>
</div>
Solution 5 - Css
With flexbox, some pseudo elements, an extra div, and after a lot of frustration I was able to achieve this without media queries (since I needed to put my grid inside many different sized elements, media queries wouldn't really work for me).
One caveat: gutters between items are fluid.
Demo: http://codepen.io/anon/pen/OXvxEW
CSS:
.wrapper {
display: flex;
flex-wrap: wrap;
border: 2px solid #ffc0cb;
max-width: 1100px;
margin: 0.5rem auto;
justify-content: center;
}
.wrapper:after {
content: ' ';
flex: 1;
height: 100%;
border: 1px solid #00f;
margin: 0.5rem;
}
.child {
flex: 1;
border: 2px solid #ffa500;
min-width: 300px;
margin: 0.5rem;
text-align: center;
}
.child-contents {
width: 300px;
border: 2px solid #008000;
height: 100px;
margin: 0 auto;
}
HTML:
<div class='wrapper'>
<div class='child'>
<div class='child-contents'></div>
</div>
<div class='child'>
<div class='child-contents'></div>
</div>
<div class='child'>
<div class='child-contents'></div>
</div>
...etc., more .child's...
</div>
The end result is something like this, where the green rectangles are
Solution 6 - Css
To date, the only clean solution for this is with the
CSS Grid Layout Module (Codepen demo)
Basically the relevant code necessary boils down to this:
ul {
display: grid; /* (1) */
grid-template-columns: repeat(auto-fill, 120px); /* (2) */
grid-gap: 1rem; /* (3) */
justify-content: center; /* (4) */
align-content: flex-start; /* (5) */
}
-
Make the container element a grid container
-
Set the grid with an 'auto' amount of columns of width 120px. (The auto-fill value is used for responsive layouts).
-
Set gaps/gutters for the grid rows and columns.
-
and 5) - Similar to flexbox.
body {
margin: 0;
}
ul {
display: grid;
grid-template-columns: repeat(auto-fill, 120px);
grid-gap: 1rem;
justify-content: center;
align-content: flex-start;
/* boring properties: */
list-style: none;
width: 90vw;
height: 90vh;
margin: 2vh auto;
border: 5px solid green;
padding: 0;
overflow: auto;
}
li {
background: tomato;
height: 120px;
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
Codepen demo (Resize to see the effect)
Caniuse
Browser Support -Currently supported by Chrome (Blink) and Firefox, with partial support from IE and Edge (See this post by Rachel Andrew)
Further reading on CSS grids:
Solution 7 - Css
Use flexbox:
.container {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: row;
flex-direction: row;
-webkit-justify-content: flex-start;
justify-content: flex-start;
flex-wrap:wrap;
}
.block {
background-color: #ddd;
border:1px solid #999;
display: inline-block;
height: 100px;
margin: 4px 2px;
width: 100px;
}
Done.
Solution 8 - Css
There is no "normal" solution for your problem, but only the mentioned "workarounds".
The situation is, your container of blocks will fill up available space up to the maximum available/set-up and then break all the inner blocks to the next line, which will cause the container have overflow. Also with other configurations like floating it will be the same behaviour. That's the way the rendering works - everytime greedy in space to calculate the inner elements behaviour.
Maybe the future Flexboxes will make this possible - but I did not read the full specs. Just a guess...