9

I have a container of tiles (or divs) and I want the container to be centered, while the tiles are left justified in the container.

so if the window is small:

..[s][s][s]..
..[s][s].....

If the window is widened a little:

...[s][s][s]...
...[s][s]......

further:

.[s][s][s][s].
.[s]..........

I've tried:

#container's-parent: { display: block; text-align: center; }
#parent: { display: inline-block; text-align: left; }
.tiles: { display: inline-block }

but that doesn't appear to work.

I want this to work in Chrome at least, but I also need to eventually support latest FF, Safari, and IE 10+

Tomas
  • 57,621
  • 49
  • 238
  • 373
Sophie McCarrell
  • 2,831
  • 8
  • 31
  • 64
  • 1
    Take a look at my answer here http://stackoverflow.com/a/16310009/703717 – Danield Jan 20 '14 at 10:35
  • Thanks for the additional information. It's good to know a calc function exists. However it still doesn't solve my problem, unless I use media queries for every possible iteration on number of columns. – Sophie McCarrell Jan 20 '14 at 15:31
  • I think that to date media queries are the only option for this. Regarding having to use 'every possible iteration' - well, true, it's a bit messy, but still, typical screen resolutions usually only go up to about 2000px - so it is doable – Danield Jan 20 '14 at 15:37
  • True Danield, and your less solution is actually quite compact. I really need to get into using less more. Now my only issue is who I'm supposed to give the bounty to ;). Skadi provided the CSS solution and made me realize it was impossibe without manually adding media queries, but Danield provided the least ugly solution and the best answer, because it also references Skadi's. – Sophie McCarrell Jan 21 '14 at 02:32
  • Similar question: [How to center a flex container but left-align flex items](http://stackoverflow.com/q/32802202/3597276) – Michael Benjamin Feb 07 '17 at 15:05
  • With CSS grids this becomes trivial: http://stackoverflow.com/a/43029259/703717 – Danield Mar 26 '17 at 13:23

9 Answers9

9

FWIW: It's now 2017 and the grid layout module does this out of the box (codepen demo). If the browser support suits you - then use grid. If not, then read on....


As mentioned in @Skadi2k3'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)

FIDDLE or CODEPEN (Supports LESS)

Here's how to take advantage of LESS to set up the media queries:

Set up an iteration mixin like this: (You can paste this code into http://less2css.org)

@item-width:100px;
@item-height:100px;
@margin: 5px;
@min-cols:2;
@max-cols:12; //set an upper limit of how may columns you want to write the media queries for


.loopingClass (@index-width) when (@index-width <= @item-width * @max-cols) {
    @media (min-width:@index-width) {
        #content{
            width: @index-width;
        }
    }

    .loopingClass(@index-width + @item-width);
}

.loopingClass (@item-width * @min-cols);

The above mixin will spit out a series of media queries in the form:

@media (min-width: 200px) {
  #content {
    width: 200px;
  }
}
@media (min-width: 300px) {
  #content {
    width: 300px;
  }
}
@media (min-width: 400px) {
  #content {
    width: 400px;
  }
}
...
@media (min-width: 1200px) {
  #content {
    width: 1200px;
  }
}

So with a simple markup like:

<ul id="content">
    <li class="box"></li>
    <li class="box"></li>
    ...
    <li class="box"></li>
</ul>

With remaining CSS (LESS):

 #content {
    margin:0 auto;
    overflow: auto;
    min-width: @min-cols * @item-width;
    max-width: @max-cols * @item-width;
    display: block;
    list-style:none;
    background: aqua;
}
.box {
    float: left;
    height: @item-height - 2 *@margin;
    width: @item-width - 2*@margin;
    margin:@margin;
    background-color:blue;
}

... 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.

So let's say I have items 300px X 100px with a minimum of 2 columns and max 6 columns and a margin of 15px - I just modify the variables like so:

@item-width:300px;
@item-height:100px;
@margin: 15px;
@min-cols:2;
@max-cols:6;

...and voila, I get this CODEPEN

Danield
  • 121,619
  • 37
  • 226
  • 255
  • 1
    +1 well that's nice! The neccessity of using rule for each width is a last weakness though. – Tomas Jan 21 '14 at 02:39
  • The same but in SCSS: http://stackoverflow.com/questions/21146847/how-to-get-tiles-centered-and-left-justified-at-the-same-time/43615480#43615480 – Karol Selak Apr 25 '17 at 15:53
  • @KarolSelak - I updated the answer with a note about css grids (it's much easier to do this now) - you can see my post [here](http://stackoverflow.com/a/43029259/703717) for for details. – Danield Apr 26 '17 at 06:33
1

What about this?

http://jsfiddle.net/cHTVd/1/

enter image description here

You have to set display: inline-block for the container too:

body { text-align: center; }

#container { 
    width: 250px; 
    border: solid green 3px; 
    display: inline-block; 
    text-align: left; 
}

.tile { width: 100px; 
    border: solid red 3px;
    display: inline-block;
    margin: 8px;
}

EDIT: Giving container relative width is easy - http://jsfiddle.net/cHTVd/3/

I am afraid that "reverse justify" would have to be done with JS. CSS text-align has only four values: left | right | center | justify. It's trivial to change it to justify - http://jsfiddle.net/cHTVd/4/. For the "reverse justify" you would probably need some javascript work similar to this: http://jsfiddle.net/yjcr7/2/.

Tomas
  • 57,621
  • 49
  • 238
  • 373
  • @Mr.Mayers as you see I didn't (width is 250px) and it works. – Tomas Jan 17 '14 at 20:33
  • Weird. I ran the fiddle in chrome and it stays at 250px no matter how big or small the window is. – Mr. Mayers Jan 17 '14 at 20:36
  • @Mr.Mayers when I run the jsfiddle in Chrome it looks exactly the same as in the screenshot (which was taken in FF). It works. – Tomas Jan 17 '14 at 21:34
  • 1
    I guess I misread the question, then - I thought he wanted the #container to resize with the window so the red .tiles would realign when the window got wide enough. – Mr. Mayers Jan 18 '14 at 02:55
  • @Mr.Mayers how is that a problem? http://jsfiddle.net/cHTVd/3/ you seem to be confused :-) – Tomas Jan 18 '14 at 07:15
  • @Tomas My.Mayers is correct. In the JS fiddle you just posted with a % width, look at the right side of the green outline compared to the red box. The inner boxes are not centred, they are left-aligned. I need the boxes to be centred, while being left-aligned... actually this is known as "Justified". I'll edit my post to clarify the term. Justified is the perfect term. – Sophie McCarrell Jan 20 '14 at 03:13
  • ugh... nevermind. What my boss wants is actually like a reverse justify. So rather than the space increasing between the tiles, the space should increase on the left and right side. my diagram is still correct, but the term "justify" isn't entirely accurate. – Sophie McCarrell Jan 20 '14 at 03:16
  • @JasonMcCarrell OK. I am afraid that "reverse justify" would have to be done with JS. [CSS `text-align`](http://www.w3.org/TR/CSS21/text.html#propdef-text-align) has only four values: left | right | center | justify. It's trivial to change it to justify - http://jsfiddle.net/cHTVd/4/. For the "reverse justify" you would probably need some javascript work similar to this: http://jsfiddle.net/yjcr7/2/. – Tomas Jan 20 '14 at 10:14
  • we're trying to avoid JS, especially considering we can have hundreds of these tiles loaded on the page. The best solution I've found thus far sets an outside container's width dynamically for the number of columns, for each possible column amount. (also, just looking at your JSFiddle, it still doesn't do the centre bit that I'm asking =x) – Sophie McCarrell Jan 20 '14 at 15:35
  • @JasonMcCarrell what "centre bit" do you mean? – Tomas Jan 20 '14 at 15:40
  • @JasonMcCarrell if you change the text-align line to center instead of left, then the red tiles remain centered while the green box grows until there's enough space for the tiles to redo the row. http://jsfiddle.net/275JZ/ – Mr. Mayers Jan 20 '14 at 16:18
  • @JasonMcCarrell as far as I understand your ascii art in the question, there is nothing about centering the tiles inside the container as Mr. Mayers mentions. Please clarify. – Tomas Jan 20 '14 at 16:43
  • from the ascii art, notice that as the width grows larger, it is evenly distributed between the left and the right. first image is 2 dots, the next is 3 dots, both on the left and right. Meanwhile the tiles are next to each other (no space between them), and new rows are started on the left. It's justify, where the extra space is distributed on the left and right padding of the container, instead of in between the tiles. – Sophie McCarrell Jan 21 '14 at 02:25
  • @JasonMcCarrell got this from the ascii art and guess thats exactly what my jsfiddle does: http://jsfiddle.net/yjcr7/3/ but I still don't get what you meant by *"it still doesn't do the centre bit"* and where did you mention that. – Tomas Jan 21 '14 at 02:35
  • Watch the red boxes in relation to the green border while resizing the window. The right space between the right most red box and the right green border increases while the space between the left most red box and the left green border does not increase (ie. it's left aligned). I want that space to be even (ie. centered), BUT with any new tiles in a row to start on the left side. – Sophie McCarrell Jan 21 '14 at 17:55
  • @JasonMcCarrell this is not true for my javascript solution, did you check it? http://jsfiddle.net/yjcr7/4/ – Tomas Jan 22 '14 at 11:44
1

Unless you want to use Javascript you could use the media query (lots of them):

#parent{ width: 100px; margin: 0 auto;padding:0;}
.tile{width: 80px; float:left;padding:10px;outline:2px dashed red;}
@media screen and (max-width:200px)
@media screen and (min-width:201px) and (max-width:300px){
    #parent{ width: 200px;}
}
@media screen and (min-width:301px) and (max-width:400px){
    #parent{ width: 300px;}
}
@media screen and (min-width:401px){
    #parent{ width: 400px;}
}

The problem is that you need to know how many of the tiles fit into the container to set a tight fitting width to the container, but that is a bottom up information and not how cascading works. If you want a more elegant solution you need to use JS on resize events, calculate how many boxes fit into one line and set the width of the container.

Skadi2k3
  • 66
  • 4
  • Hmmm, thank you for your answer. You may be correct, that doing this dynamically is impossible. It's unfortunate CSS doesn't have the power. Your solution is also a great hack if you are willing to write custom CSS for each and every column. This is frustrating, because what I want is VERY close to "justify", but rather than the space being between tiles, it would be at the left and right side. Someone needs to invent a type setting that is like centre-justify or something =P EDIT: oh btw you posted the wrong JSFiddle ;) – Sophie McCarrell Jan 20 '14 at 03:20
  • Well most features might be hacks until they become part of the specification and the browser has to handle it itself. From what i've read these media queries are one of the current css tools of responsive webdesing. In this book [link](http://learningwebdesign.com) they use it to tailor the design to resolutions of common smartphones, tablets and desktop resolutions. P.S.: didn't notice the wrong JSFiddle, thx :-) – Skadi2k3 Jan 20 '14 at 15:37
  • good point about most features are hacks until they become part of the spec. The problem is that this hack requires a lot of hard coding, but that's ok I suppose. – Sophie McCarrell Jan 21 '14 at 02:27
1

I solved this on my own projects in a quick and dirty manner by simply adding several empty "tiles" to the end that are the same width as the regular content tiles, but with a negligible height. These empty tiles serve to "push" the content tiles on the last line to the left where they belong. (The "real" last line often consists of nothing but empty tiles that are invisible to the user.) So, for example, if there some number of tiles on a page, and I expect the width of the browser to be able to accommodate from 1 to 4 (for example) tiles in width, I will add 3 extra empty tiles (designated "e") to the end:

..[s][s][s]..
..[s][s][e]..
...[e][e]....  // this row isn't visible because [e] has no content

If the window is widened a little:

...[s][s][s]...
...[s][s][e]...
....[e][e].....

further:

.[s][s][s][s].
.[s][e][e][e].

or more narrow:

.[s][s].
.[s][s].
.[s][e].
.[e][e].

or really narrow

..[s]..
..[s]..
..[s]..
..[s]..
..[s]..
..[e]..
..[e]..
..[e]..

The empty tiles may pile up at the bottom, but since they are some small height, they don't create a whole lot of extra whitespace.

A hackish solution for sure, but it does work, is reliable on different browsers, and doesn't require a mound of code.

matt forsythe
  • 3,863
  • 1
  • 19
  • 29
  • Clever, and works well. In my case, I allow anywhere from 3 tiles to 10 tiles, so added my `
    ` 9 times at the end of my normal tiles, and styled these with `visibility: hidden; height: 0; width: ; margin: 0 ;`
    – matb33 Oct 08 '14 at 15:39
1

Answer the same like Danield's, but in SCSS:

*
{
    margin:0;padding:0;
}

$item-width:200px;
$item-height:100px;
$margin: 15px;
$min-cols:2;
$max-cols:6; //set an upper limit of how may columns you want
             //to write the media queries forss to css

@for $i from $min-cols to $max-cols + 1 {
  $index-width: $i*$item-width;
  @media (min-width:$index-width) {
    #content {
      width: $index-width;
    }
  }
}


#content {
    margin:0 auto;
    overflow: auto;
    min-width: $min-cols * $item-width;
    max-width: $max-cols * $item-width;
    display: block;
    list-style:none;
    background: aqua;
}
.box {
    float: left;
    height: $item-height - 2 *$margin;
    width: $item-width - 2*$margin;
    margin:$margin;
    background-color:blue;
}

It generates the same CSS like his solution.

Working codepen example here.

Community
  • 1
  • 1
Karol Selak
  • 4,248
  • 6
  • 35
  • 65
0

Try this:

.tiles: { 
  display: inline-block;
  margin: 0 auto;
  max-width: 90%;
}

No verticall margin, auto margin for horizontal sides!

Afzaal Ahmad Zeeshan
  • 15,669
  • 12
  • 55
  • 103
  • nope. If you think you have a solution, perhaps try it in JSfiddle. If you can show it's working there, then I know I could get it to work just the same. Thanks for the suggestion though. – Sophie McCarrell Jan 15 '14 at 20:55
0

This is pretty easy with flexbox.

.container { display: flex; flex-wrap: wrap; }

You can customize how the tiles fit as well, such as giving them a starting point (flex-basis) or how much the tile can grow or shrink

.tile { flex-grow: 0; flex-shrink: 0; flex-basis: 3em;

Check out the codepen: http://codepen.io/anon/pen/uexbf

2507rkt3
  • 21,311
  • 1
  • 18
  • 15
  • Thanks for the suggestions, but this still doesn't accomplish what i want. In fact I tried flexbox, it's really cool, but even though it provides so much power, it still didn't allow me to do what I wanted. – Sophie McCarrell Jan 24 '14 at 20:02
0

Looks like there is solution (though it is described in Russian and I haven't found a translation).
Demo can be seen here.

user
  • 23,260
  • 9
  • 113
  • 101
0

No idea how to do it with CSS, if it's even possible. But with jQuery it's rather simple:

$('button').click(function(){

  $('nav ul').each(function(){
    
    $parent = $(this).parent();
    
    $parent.width( $(this).width() );
    
  });
});
nav {
  display: inline-block;
  text-align: left; /* doesn't do anything, unlike some might guess */
}
ul {
  display: inline;
}

/* needed style */
ul {
  padding: 0;
}
body {
  width: 420px;
}

/* just style */
body {
  background: #ddd;
  margin: 1em auto;
}
button {
  display: block;
}
nav {
  background: #bbb;
  margin: 1rem auto;
  padding: 0.5rem;
}
li {
  display: inline-block;
  width: 40px;
  height: 20px;
  border: solid thin #777;
  margin: 4px;
  background: #999;
  text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<button>fix</button>

<nav>
  <ul>
    <li>3</li>
    <li>.</li>
    <li>1</li>
    <li>4</li>
  </ul>
</nav>

<nav>
  <ul>
    <li>3</li>
    <li>.</li>
    <li>1</li>
    <li>4</li>
    <li>1</li>
    <li>5</li>
    <li>9</li>
    <li>2</li>
    <li>6</li>
    <li>5</li>
    <li>3</li>
    <li>5</li>
  </ul>
</nav>
Community
  • 1
  • 1
cregox
  • 17,674
  • 15
  • 85
  • 116