Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using negative margins to control space in flex-box

I have been reading up on flex-boxes in order to solve a problem I have been having. The main article I have read up on /used as research for the problem is this.

I recently brought this topic up in this and this questions and thanks to the help of @Michael_B, it was worked out how to center an element (in this case an <h2>) in a flex-box as long as there are always and only three flex-items in the flex-box (in this layout: <div><h2><div>).

I wanted to expand upon these points as I realized there might be a solution which could solve if there are more or (more realistically) less than 3 total flex-items.

It involves using margins. If you use margin: auto; on a flex item, it acts to center an item on the main axis (see following picture for how a flex-box works).

img {
 width: 100%;
 height: 100%;
}
<img src="http://i.stack.imgur.com/9Oxw7.png" alt="flex-box demo" />

(That was the best way I could figure out how to get a dropdown-photo...)

From what I am understanding, using margin: auto is the current counterpart to the align-selfproperty for the cross axis.

Thus one could do this to center a flex item in a flex box in these ways:

$(document).on('click', 'button', function(e) {
  $this = $(this);
  if ($this.attr('id') === 'button1') {
    $('h2').parent().attr("class", "");
    $('h2').attr("class", "toggle1");
  }
  if ($this.attr('id') === 'button2') {
    $('h2').parent().attr("class", "");
    $('h2').attr("class", "toggle1");
  }
  if ($this.attr('id') === 'button3') {
    $('h2').parent().attr("class", "toggle3");
    $('h2').attr("class", "");
  }
  if ($this.attr('id') === 'button4') {
    $('h2').parent().attr("class", "toggle4");
    $('h2').attr("class", "");
  }
  if ($this.attr('id') === 'button0') {
    $('h2').parent().attr("class", "");
    $('h2').attr("class", "");
  }
});
div:not(.cont),
div:not(.cont) * {
  border: 1px dotted red;
}
div {
  align-items: center;
  display: flex;
}
button {
  margin: 16px;
}
.toggle1 {
  margin: auto;
}
.toggle2 {
  flex: 1;
}
.toggle3 {
  justify-content: center;
}
.toggle4 {
  justify-content: space-between;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<div>
  <h2>I'm an h2</h2>
</div>
<div>
  <span>I'm a span!</span>
  <h2>I'm an h2</h2>
  <span>I'm a span!</span>
</div>
<div>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
  <h2>I'm an h2</h2>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
</div>
<div class="cont">
  <button id="button0">clear</button>
  <button id="button1">margins</button>
  <button id="button2">flex-grow</button>
</div>
<div class="cont">
  <button id="button3">justify-content: center
    <br />(on parent)</button>
  <button id="button4">justify-content: space-around
    <br />(on parent)</button>
</div>

As you can see from the above snippet, it only works if there are the same amount of flex-items on each side of the <h2> (and you can also see that justify-content: space-around does not work with just one object).

But what if there are an unbalanced amount of flex items on each side of the object you are trying to center? - Sure, you could put blank elements there and use flex: 1;, but that seems a bit hacky.

My idea is to make use of negative-margins. I read a great article (here) about using negative-margins and how they can pull objects towards them, etc. (plus, even w3 says they are legitimate code - not hacky).

The idea is to set <h2> to margin: auto; and then somehow (I emphasis somehow as with all the research I have been doing I am starting to lose hope...) take the width of any sibling flex-item and negate that from the <h2> margin: auto;... sounds hacky and hopefully there is a way to do it without JS/jQuery or a CSS compiler.

Does anyone know of a way to negate from the margin of a flex item the width of a sibling flex item in the same flex-box?

Thank you.

P.S./UPDATE

There might be a way to accomplish this with the position property; however, all the testing I have done has produced no results... Maybe someone else has been successful with this method?

like image 424
4lackof Avatar asked Aug 19 '16 19:08

4lackof


People also ask

How can I reduce my flex gap?

To support older browsers that don't support Flex Gap in Flexbox we can use a margin hack to create a close workaround. We can set margin space on the top and left of each item. On the flex parent element, we use negative margins to counter the excess margin on the outer child elements.

How do I reduce space between items in Flexbox?

To set space between the flexbox you can use the flexbox property justify-content you can also visit all the property in that link. We can use the justify-content property of a flex container to set space between the flexbox.

Is it good practice to use negative margins?

Using negative margin is considered bad practice as it makes the code more complex and difficult for developer's understanding. Generally there is no need to use negative margin, unless and until you have made any error elsewhere.


1 Answers

One way could be to take the central element out of the normal flex flow. It is possible by absolute positioning it inside the container. The downside of this is that the flex container do not take into account the height of the absolute positioned element anymore.

codepen

.container {
  position: relative;
  display: flex;
  width: 500px;
  justify-content: flex-start;
  align-items: center;
  outline: 1px solid blue;
}

.item {
  flex: 0 0 auto;
  outline: 1px solid red;
  margin: 0;
}

.item--center {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}

.item--right {
  margin-left: auto;
}
<div class="container">
  <span class="item">I'm a span!</span>
  <span class="item">I'm a span!</span>
  <h2 class="item item--center">I'm an h2</h2>
  <span class="item item--right">I'm a span!</span>
</div>
like image 92
Giacomo Cosimato Avatar answered Oct 06 '22 01:10

Giacomo Cosimato