Sophie McCarrell
Sophie McCarrell

Reputation: 2871

How to get tiles centered and left-justified at the same time

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+

Upvotes: 9

Views: 3134

Answers (9)

Danield
Danield

Reputation: 125503

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

Upvotes: 9

Karol Selak
Karol Selak

Reputation: 4774

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.

Upvotes: 1

cregox
cregox

Reputation: 18408

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>

Upvotes: 0

matt forsythe
matt forsythe

Reputation: 3912

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.

Upvotes: 1

user
user

Reputation: 25738

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

Upvotes: 0

2507rkt3
2507rkt3

Reputation: 21802

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

Upvotes: 0

Skadi2k3
Skadi2k3

Reputation: 66

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.

Upvotes: 1

Tomas
Tomas

Reputation: 59505

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

Upvotes: 1

Afzaal Ahmad Zeeshan
Afzaal Ahmad Zeeshan

Reputation: 15860

Try this:

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

No verticall margin, auto margin for horizontal sides!

Upvotes: 0

Related Questions