CSS - Responsive grid with equal margins and fixed

2019-02-18 04:08发布

问题:

I'm trying to push the limits of CSS to replicate what would be a common grid-layout in print.

Requirements :

  1. Margins between blocks and between blocks and edge of the container must be equal.
  2. The layout must be responsive and the number of blocks on each row must adapt to the size of the window.
  3. The last row must be left aligned
  4. the width/height of the blocks is fixed
  5. no use of empty non-semantic HTML elements
  6. pure CSS solution, no JS

So, I have markup that looks like this:

<ul>
    <li>
       <img src="thumbnail.jpg">
       <span>Introduction and Curriculum</span>
    </li>
    <li>
       <img src="thumbnail.jpg">
       <span>Equipment and Workspace Prep</span>
    </li>
    ...
</ul>

Here is a mock-up of what I'm going for.

回答1:

You can use the CSS calc() function. Although it won't prevent from using media queries, It can calculate the margin between elements and container edges.

DEMO

This demo uses :

  1. calc() CSS function. In this context, it will be supported by IE9+. You might want to add the -webkit- prefix for some webkit browsers. for more info, see canIuse.
  2. 4 media queries to change the number of elements displayed in one row and the margins accordingly.
  3. inline-block elements. This involves dealing with the white space (in the demo I used the font-size technique but you may use an other one, see here).

Explanation :

Media query break points :

They are calculated according to the width of the elements. As each element is 200px wide I should have chosen break points at screen width = 400px/600px/800px/1000px but as media queries include the scrollbar, with those values, the elements would not have enough space and overlap each other.

Scrollbars don't have the same width on each browser so I chose a higher value to be sure that the overlapping doesn't occur.

Here is an example of this behaviour with "logical" media query break points.

Margin calculation :

First of all percent margins and padding are always calculated according to the remaining width of the container (exception) so the top and bottom margins / paddings have the same calculation as the left/right ones.

Basically, the calculation of the size of margins is :

(remaining width (=100%) - the sum of grid elements width) / number of gaps 

But

Left and top gaps are padding from the container and the other gaps are margin right and bottom on the block element. The margin calculation for the blocks must take that into account and the division is by the number of gaps -1.


HTML :

<ul id="container">
    <li class="block">...</li>
    <li class="block">...</li>
    ...
</div>

CSS :

#container{
    font-size:0;    
    padding-top: calc((100% - 1000px)/6);
    padding-left:calc((100% - 1000px)/6);}

.block {
    font-size:20px;
    width: 200px;
    height: 200px;
    display:inline-block;
    margin-right: calc((100% - 1000px)/5);
    margin-bottom: calc((100% - 1000px)/5);
}

@media screen and (max-width: 430px) {
    .block {
        margin: calc(50% - 100px);
    }
}

@media screen and (min-width: 431px) and (max-width: 630px) {
    #container{
        padding-top: calc((100% - 400px)/3);
        padding-left:calc((100% - 400px)/3);
    }
    .block {
        margin-right: calc((100% - 400px)/2);
        margin-bottom: calc((100% - 400px)/2);
    }
}
@media screen and (min-width: 631px) and (max-width: 830px) {
    #container{
        padding-top: calc((100% - 600px)/4);
        padding-left:calc((100% - 600px)/4);
    }
    .block {
        margin-right: calc((100% - 600px)/3);
        margin-bottom: calc((100% - 600px)/3);
    }
}
@media screen and (min-width: 831px) and (max-width: 1030px) {
    #container{
        padding-top: calc((100% - 800px)/5);
        padding-left:calc((100% - 800px)/5);
    }
    .block {
        margin-right: calc((100% - 800px)/4);
        margin-bottom: calc((100% - 800px)/4);
    }
}


回答2:

Nth-child and implementation at the respective breakpoints would probably be the best practice method and the most lightweight solution.

There are a few other options such as 1. Measuring screen width with jQuery and assigning styles at those different breakpoints 2. Assigning a class to the container using negative margins, so you wouldn't need to touch the actual margins

...etc.