Reputation: 4232
Why does setting margin
to auto
break flexbox's baseline alignment?
I want to align a heading with what I'll call accent text according to the baseline. It works fine using display: flex
and align-items: baseline
on the container.
I also want to remove the margin on the left side of the heading, set the margin on the right side of the heading to a specific value, and leave the margins at the top and bottom of the heading to the default (user-agent stylesheet) value. For example, h1 { margin: auto 1em auto 0; }
This is where it breaks and the item alignment appears to revert to the default of stretch.
Now, developer tools shows me that when I set the top and bottom values of the margin to auto
, it actually ends up with a margin of 0 instead of what I expected. However, when I actually set the top and bottom margins to 0 (e.g.: margin: 0 1em 0 0
), the problem doesn't occur.
I've already seen how to work around this problem by just setting margin-left
and margin-right
individually without using the shorthand property, but I'm hoping to get a better understanding of why this is happening.
header {
background-color: #ffd;
display: flex;
justify-content: flex-start;
align-items: baseline;
margin: 0 1em 0 0;
}
h1 {
margin: auto 1em auto 0;
}
<header>
<h1>Heading</h1>
<div class="accent">Accent Text</div>
</header>
Upvotes: 3
Views: 1532
Reputation: 371799
The first thing to note is that align-items
on the flex container is ignored by flex items having an auto
margin set in the cross axis. (justify-content
would be ignored by flex items with auto
margins set in the main axis).
8.1. Aligning with
auto
marginsIf free space is distributed to auto margins, the [keyword] alignment properties [such as
align-items
andjustify-content
] will have no effect in that dimension because the margins will have stolen all the free space left over after flexing.
The second thing to note is that the align-items
property sets the default align-self
on flex items. align-items: baseline
means align-self: baseline
on all flex items, unless you override align-self
for a particular item.
8.3. Cross-axis Alignment: the
align-items
andalign-self
propertiesFlex items can be aligned in the cross axis of the current line of the flex container, similar to
justify-content
but in the perpendicular direction.
align-items
sets the default alignment for all of the flex container's items, including anonymous flex items.
align-self
allows this default alignment to be overridden for individual flex items.
For an example of an align-self
override see this post:
In terms of your code...
header {
display: flex;
justify-content: flex-start;
align-items: baseline;
margin: 0 1em 0 0;
background-color: #ffd;
}
h1 {
margin: auto 1em auto 0;
}
<header>
<h1>Heading</h1>
<div class="accent">Accent Text</div>
</header>
... here's what's happening:
align-items: baseline
/ align-self: baseline
are ignored by the h1
because it has auto
margins in the cross axis. align-items: baseline
/ align-self: baseline
is respected by the div because it has no auto
margins.The question then becomes, why is the div aligned to the top of the container?
Because in a group of flex items with baseline alignment, the item with the largest distance between the container's cross-start edge (the top edge, in this case) and the item's cross-start margin (the top margin, in this case), is placed flush against the cross-start edge.
Since there is only one item with baseline alignment in the container, it shoots straight to the top.
8.3. Cross-axis Alignment: the
align-items
andalign-self
properties
baseline
The flex item participates in baseline alignment: all participating flex items on the line are aligned such that their baselines align, and the item with the largest distance between its baseline and its cross-start margin edge is placed flush against the cross-start edge of the line.
For a clear illustration of this behavior see this post:
Upvotes: 2
Reputation: 33
Margin: auto;
interacts in a very special way with flexbox. What it does is take all remaining empty space on its axis and adds it to the element.
Your problem is coming from the fact that you setting both margin-top and margin-bottom to auto. Doing so causes all of the empty space to be allocated evenly between the top and bottom margins of the element.
This is also why margin: auto will perfectly center an element in flexbox.
You can use this jsfiddle to see more how the margin:auto works with flexbox.
https://jsfiddle.net/kaoLkpgz/2/
Replace any of the margin: 0; values of .child with auto and you can see how each interacts with the others.
Upvotes: 0
Reputation: 87251
auto margin
's override the align-items
/justify-content
property set on the flex container.
So what happens is, the default top/bottom margin will be removed, and then the h1
will be vertically centered in its parent, instead of aligned at the baseline.
If you give the header
a height, as I did in this fiddle demo, you'll see what goes on.
To keep the default top/bottom value, only adjust its left/right value.
Stack snippet
header {
background-color: #ffd;
display: flex;
justify-content: flex-start;
align-items: baseline;
margin: 0 1em 0 0;
}
h1 {
margin-right: 1em; /* changed */
margin-left: 0; /* changed */
}
<header>
<h1>Heading</h1>
<div class="accent">Accent Text</div>
</header>
Upvotes: 3