0

TL;DR: codepen here make the items have at least 30px margin-right and no margin in case it is mobile view. Without using what I consider hacky media queries or jQuery. And no horizontal scroll

Why?

I want to use flexbox

following problem:

3 items

for desktop they align in a row. For mobile there is maybe 2, maybe 1. Each one having margin-right: 30px;

now, the last one (of the row, 1, 2 or 3 rows possible) must not have margin-right or at least look as if it does not have a margin. No hacks allowed right. This must be a responsive solution.

this is pretty standard imho:

e.g. desktop:

display 3 items, each with a minimum margin of 30px, or more depending on screen size. If there is less space, flex-wrap to the next line

on small mobiles:

only show one item per row, but centered without the margin-right: 30px

what did I try?

codepen here

HTML

<div class="row no-gutters my-outer">
  <div class="col-12 d-flex justify-content-between flex-wrap">  

  <div class="item"></div>

      <div class="item"></div>
      <div class="item"></div>
            </div>

</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js"></script>

CSS

.item{
  width: 400px;
  height: 400px;
  background-color: cornflowerblue;
  margin-right: 30px;
  margin-top: 30px;

}

.my-outer{
  margin-top: -30px;
  margin-right: -30px;
  xxxxwidth: calc(100% - 30px);
}

negative margin on the parent

the issue is, that it creates a scroll bar

there should be a standard solution for this, no?

using width: calc(100% - 30px); creates other odd issues that are unwanted.

enter image description here

when using overflow-x: hidden on the parent element, you get issues with this: overflow-x:hidden still can scroll

Toskan
  • 13,911
  • 14
  • 95
  • 185

2 Answers2

1

Since the built-in col-12 rule looks like this

.col-12 {
  flex: 0 0 100%;
  max-width: 100%;
}

it prevents it from being wider than 100%, which it needs to for the margins to work. So if to remove it, and as the col-12 element is a flex row item, it will need a width if no wrap occurs, or it won't fill the available space in its parent.

Then, when it comes to add margins like that, it is better to add it on the left side of the item, as left margin generally won't cause a scroll to appear, which right margin often does, and the compensation for that margin should be applied on the items parent, not the outer most element.

So in below samples I removed col-12, added a custom rule, my-inner, and used left margin.


Now, for the mobile layout, since there is no way to detect when the items wrap, you either need a media query or a script, and as media query is really not a hack, and there is only one property that needs to be altered, justify-content, this is the cleanest solution.

Updated codepen

Stack snippet

.item{
  width: 300px;
  height: 300px;
  background-color: cornflowerblue;
  margin-left: 30px;
  margin-top: 30px;
}

.my-inner {
  flex-grow: 1;
  margin-left: -30px;
  margin-top: -30px;
}

@media (max-width: 647px) {
  .my-inner {
    justify-content: space-around !important;
  }
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"/>

<div class="row no-gutters my-outer">
  <div class="d-flex flex-wrap justify-content-between my-inner">
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
  </div>
</div>

My personal recommendation though, is to use space-around, which IMHO align the items much nicer w/o a big gap between items, and with making use of one of the pseudo elements one can keep the 3rd item left aligned.

Updated codepen 2

Stack snippet 2

.my-inner::after,
.item{
  width: 300px;
  height: 300px;
  background-color: cornflowerblue;
  margin-left: 30px;
  margin-top: 30px;
}

.my-inner::after {
  content: '';
  height: 0;
}

.my-inner {
  flex-grow: 1;
  margin-left: -30px;
  margin-top: -30px;
}

@media (min-width: 960px) {
  .my-inner::after {
    display: none;
  }
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"/>

<div class="row no-gutters my-outer">
  <div class="d-flex flex-wrap justify-content-around my-inner">
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
  </div>
</div>

Thanks to Toskan, here is the final version they made out of my above sample.

Asons
  • 84,923
  • 12
  • 110
  • 165
  • @Toskan Yeah, I know they have a lot of classes like that, though I don't use Bootstrap myself, so I don't know them all :) .... about my samples, I have 3 codepens and they are all different, so which 2 is the same for you? – Asons Oct 27 '17 at 20:26
  • sorry for me it is still early :D I think I clicked on the wrong one. Awesome answer man... just to know, if you don't use bootstrap, you code all css yourself always? what tools you use? gulp with autoprefixer I guess? – Toskan Oct 27 '17 at 20:35
  • @Toskan Thanks ... no tools, been doing this now for almost 25 years, so I made my own CSS frame work :) – Asons Oct 27 '17 at 20:37
  • there is an issue with https://codepen.io/anon/pen/BmBOJR if the resolution screen is high res, one of the pseudo elements will jump up into the first row I think – Toskan Oct 27 '17 at 20:38
  • so it was basically the `flex-grow` property together with the `max-width` that was missing that made the inner not expand to full width... damn. Here the version with the overwrites of the bootstrap 4 styles plus the class I mentioned earlier, maybe add it to your answer. https://codepen.io/anon/pen/dZbRpj I am honestly surprised I couldn't find an answer like this before. Because I damn sure was looking – Toskan Oct 27 '17 at 20:44
  • @Toskan That is correct (and it took me a while before I got my eyes on the `max-width` :)...and I updated my answer to fix the issue with the second sample ... and posted a reference to your codepen – Asons Oct 27 '17 at 21:04
0

Try this at the bottom of your style sheet.

  1. Use @media to target the device by width.
  2. Use max-width and % width for responsive views
  3. Use last-child to make sure there is no margin on the last item as oppose to using negative margins

    @media (min-width:800px) { .item { width: 100%; max-width: 400px; }

    .item:last-child { margin-right: 0; }

    .row { max-width: 100%; } }

    @media (max-width:800px) { .item { width: 100%; max-width: 400px; margin: 0 auto; }

    .item:last-child { margin-right: 30px; }

    .row { max-width: 100%; } }

Colin Gell
  • 372
  • 1
  • 13
  • for this to work, I have to write for each case a new set of media queries. I have more than 3 items? rewrite the media queries. The client wants the boxes to be a little bit smaller or a little bit bigger? rewrite the media queries. The client wants to have a bit more margin or a bit less margin? rewrite media queries. The client wants to change the basic layout of the page to be a bit wider or a bit smaller? rewrite media queries. Is there really no better solution? – Toskan Oct 26 '17 at 22:55
  • You only have to add the code you think you might change in the media queries, otherwise leave it out of media queries. You might also want to start using SASS to improve your work flow i.e you can use variables in your sass code so you only need to alter your css in one place. – Colin Gell Oct 26 '17 at 22:59
  • if I use this strategy _everywhere_ in my css code, the slightest change to the basic layout will cause a rats tail of work. Just imagine the client says "meh, I want different basic width of the basic container" well good luck you can look at _everything_ that uses this pattern. And then once you are done he certainly will say "oh well, actually no, make it even smaller". No man, I won't be using this. – Toskan Oct 26 '17 at 23:26
  • suit yourself but SASS or LESS is the way to go to speed up and improve your workflow :/ – Colin Gell Oct 26 '17 at 23:29
  • Also I'd say just get used to using media queries - once you are familiar you'll get quicker at it. :) – Colin Gell Oct 26 '17 at 23:31
  • I also take it you are already familiar with bootstrap!!! as this partly takes care of media queries for you... – Colin Gell Oct 26 '17 at 23:36
  • what about high res displays? for dynamic amount of items? it just doesn't work so well. It's a pity though there is no clean solution. They totally forgot in flexbox to have a class for "last item of row/column" – Toskan Oct 27 '17 at 01:30
  • 1
    but you are right I should use more mixins with my scss for media queries... I actually just wrote some following this recommendations here: https://davidwalsh.name/write-media-queries-sass and made them fit the bootstrap queries. So I will do negative margin approach and just remove the negative margins for mobile I guess? – Toskan Oct 27 '17 at 01:43
  • probably adding as well left and right margin instead of only right, so it looks good enough when in mobile – Toskan Oct 27 '17 at 01:51