Cross-Axis Length Ignores Margins - css

One really cool thing about flexbox is that, if there is room, the flex items' margins are included in the width of the flex container, even if I tell it to take up the whole space. For example, if I have an 100px-wide flex container, and give it a single child that takes up its whole width, then give that child 10px margins on both sides, the child actually becomes 80px wide, as the container allows the child to be completely encapsulated, including the margins. This ignores concerns about overflow, which can be worked around quite easily (min-width: 0; usually handles this).
flex-container {
width: 100px;
height: 100px;
display: flex;
background-color: dodgerblue;
}
flex-item {
display: block;
background-color: darkgray;
margin-left: 10px;
margin-right: 10px;
width: 100%;
height: 20px;
margin-top: 20px;
}
<flex-container>
<flex-item>
</flex-item>
</flex-container>
However, this trick does not seem to extend to the cross-axis (height normally, width with flex-direction: column). If I now give my flex item 10px margins on the top and bottom, instead of the left & right, and give it the whole height instead of the whole width like before, it starts to extend past the bottom edge of the container. Why is that?
flex-container {
width: 100px;
height: 100px;
display: flex;
background-color: dodgerblue;
}
flex-item {
display: block;
background-color: darkgray;
margin-top: 10px;
margin-bottom: 10px;
height: 100%;
width: 20px;
margin-left: 20px;
}
<flex-container>
<flex-item>
</flex-item>
</flex-container>
I have seen this behavior on Google Chrome as well as Firefox. It is probably also he case on Internet Explorer/Edge, but I have not had the ability to test.
Why does the flex item stay inside margins on the main axis, but not on the cross axis?

The reason is that in the direction flex is set, and since flex-grow's default is 0, it is not allowed to grow, but it is allowed to shrink, (flex-shrink default is 1), so it does.
Here is the same test using column direction, where you will get the reverse behavior.
Your first sample, but with column direction
flex-container {
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
background-color: red;
}
flex-item {
display: block;
background-color: green;
margin-left: 10px;
margin-right: 10px;
width: 100%;
height: 20px;
margin-top: 20px;
}
<flex-container>
<flex-item>
</flex-item>
</flex-container>
Your second sample, but with column direction
flex-container {
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
background-color: red;
}
flex-item {
display: block;
background-color: green;
margin-top: 10px;
margin-bottom: 10px;
height: 100%;
width: 20px;
margin-left: 20px;
}
<flex-container>
<flex-item>
</flex-item>
</flex-container>
Updated based on a comment.
If you want a similar behavior, where the flex item shrinks and doesn't overflow its parent, simply remove the given width/height
Sample for row direction
flex-container {
width: 100px;
height: 100px;
display: flex;
background-color: green;
}
flex-item {
display: block;
background-color: orange;
margin-top: 10px;
margin-bottom: 10px;
width: 20px;
margin-left: 20px;
}
<flex-container>
<flex-item>
</flex-item>
</flex-container>
Sample for column direction
flex-container {
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
background-color: green;
}
flex-item {
display: block;
background-color: orange;
margin-left: 10px;
margin-right: 10px;
height: 20px;
margin-top: 20px;
}
<flex-container>
<flex-item>
</flex-item>
</flex-container>

An initial setting of a flex container is flex-shrink: 1.
This means that flex items will shrink when necessary to avoid overflowing the container.
The flex-shrink property (along with flex-grow, flex-basis and flex) works only on the main axis. That's why you see a difference in your width vs. height examples.
When you add top and bottom margins to a flex item in a row-direction container, the flex-shrink: 1 feature does not apply (because you're now in the cross axis), so the item will not shrink to accommodate the added margins.
If you disable flex-shrink, your flex item with horizontal margins will overflow the container:
flex-container {
width: 100px;
height: 100px;
display: flex;
background-color: red;
}
flex-item {
flex-shrink: 0; /* new */
margin-left: 10px;
margin-right: 10px;
width: 100%;
height: 20px;
margin-top: 20px;
background-color: green;
}
<flex-container>
<flex-item></flex-item>
</flex-container>

It will respect the margins when you define the flexbox as a column and remove the width: 100% for the child.
flex-container {
width: 100px;
height: 100px;
display: flex;
background-color: red;
}
flex-item {
display: block;
background-color: green;
margin-left: 10px;
margin-right: 10px;
width: 100%;
margin-top: 20px;
margin-bottom: 20px;
}
<flex-container>
<flex-item>
</flex-item>
</flex-container>

Related

Image making a div grow unnecessarily when added

I've got a wrapper div that's vertically and horizontally centered, and then two more divs inside it that are intended to share the space of the wrapper in a 50/50 split. When I add an image ('fireplace') to the topmost div ('wall'), even though the image should have no trouble fitting in the allocated space, the wall div is expanding its height vertically and ends up taking more than the intended amount of space in the wrapper. Here's the CSS code for the divs and the image in question:
.wrapper {
display: grid;
place-items: center;
height: 85vh;
width: 85vw;
}
#container {
background-color: antiquewhite;
z-index: 0;
height: 100%;
width: 100%;
border-bottom-left-radius: 15px;
border-bottom-right-radius: 15px;
}
#wall {
background-color: darkred;
z-index: 1;
width: 100%;
height: 100%;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
border-top-left-radius: 15px;
border-top-right-radius: 15px;
display: flex;
justify-content: center;
align-items: flex-end;
}
.fireplace {
height: 20vh;
width: auto;
}
Setting the height of the container and the wall to be 42.5vh (half of the wrapper size) seems to fix this behavior.

Responsive image container (with max-width and max-height defined) + svg icon inside the container

I have an image container (.section-first-video) that should be responsive with min-height: 187px + min-width: 328px and max-height: 240px + max-width: 420px.
There is also a 'play' icon inside the div that should be placed in the center and should not change its size.
Here is the DEMO:
https://github.com/meri-maki/stackoverflow-demo-ingrad
https://meri-maki.github.io/stackoverflow-demo-ingrad/
The main issue that I currently have is that max-height: 240px doesn't work and the container keeps getting larger in height.. There should be a workaround but i can't think of anything..
HTML
<section>
<div class="section-first-video responsive-wrapper">
<img src="./resources/youtube-cover.png" alt="youtube-cover">
<img class="play-icon" src="./resources/icon-play.svg" alt="play icon">
</div>
</section>
</section>
CSS
section {
display: grid;
grid-template-columns: repeat(12, [col-start] 1fr);
column-gap: 20px;
align-items: start;
margin: 5% 4.4444%;
}
.section-first-video {
position: relative;
border-radius: 2rem;
margin-bottom: 2rem;
grid-column: col-start 1 / span 12;
max-height: 240px;
max-width: 420px;
min-width: none;
padding-bottom: 57%;
overflow: hidden;
}
.section-first-video img{
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
position: absolute;
}
.section-first-video img.play-icon{
object-fit: none;
}
You are using padding-bottom: 57%; to control the "aspect-ratio" of the .section-first-video box, which will ignore the min/max properties you have set up.
Most browsers now support aspect-ratio these days to actually set the container aspect ratio you want, which doesn't require the padding trick, but will follow the min/max values you set. In this case, you would set your widths, and then set the aspect-ratio property to what you'd like for a height.
section {
display: grid;
grid-template-columns: repeat(12, [col-start] 1fr);
column-gap: 20px;
align-items: start;
margin: 5% 4.4444%;
}
.section-first-video {
position: relative;
border-radius: 2rem;
margin-bottom: 2rem;
grid-column: col-start 1 / span 12;
overflow: hidden;
min-width: 328px;
max-width: 420px;
height: auto;
aspect-ratio: 16/9;
}
.section-first-video img{
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
position: absolute;
}
.section-first-video img.play-icon{
object-fit: none;
}
Link to JSFilddle - https://jsfiddle.net/uf0tq2e4/1/

Position left-floated elements at the center of the page

I have the following example: https://jsfiddle.net/fbwv8jhp/
with the following styles:
.menus {
height: 200px;
width: auto;
margin: auto;
}
.menu{
width: 200px;
height: 30px;
float: left;
border: 2px solid red;
margin-left: 10px;
margin-bottom: 10px;
}
Here, menu elements are aligned to left. But the additional desired behavior is to make sure that regardless the screen width (i.e. the quantity of menu divs shown in each row), they are displayed at the center. This means that in each row the distance between left menu and left screen border and right menu and right screen border should be the same, and all menus centered.
Couldn't make it, so maybe someone knows how this can be achieved.
On the image below, distances 1 and 2 should be equal.
Try using css flex-box:
.menus {
height: 200px;
width: auto;
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.menu{
width: 200px;
height: 30px;
float: left;
border: 2px solid red;
margin-left: 10px;
margin-bottom: 10px;
}

Display inline-block not working

So I have html like this
<div class="search-form-wrapper">
</div>
<div class="results-view-wrapper">
</div>
<div class="quick-visualization-wrapper"/>
This is the CSS for them -
.search-form-wrapper {
border-right: solid 1px #d1d2d4;
display: inline-block;
float: left;
height: 100%;
max-width: 350px;
min-height: 900px;
min-width: 300px;
position: relative;
width: 30%;
}
.results-view-wrapper {
display: inline-block;
height: 100%;
position: absolute;
padding-left: 10px;
}
.quick-visualization-wrapper {
display: inline-block;
}
The first two divs are displayed next to each other, but the last div appears behind the results-view-wrapper, (so next to the search-form-wrapper). I thought it might be because results-view-wrapper is position absolute, but when I took that out the div just moved downwards and was still behind results-view-wrapper.
How do I make it so that it appears next to the results-view wrapper?
You are not specifying the width of the second and third divs. You need to do it.
Why you have position:absolute on that div ? Also, don't use float on an element with display:inline-block.
http://plnkr.co/edit/6wLokBiZUw33SKmZtjiC?p=preview
Give this css a try. It has to do with your float and absolute position. Also the last div didn't have a width, so it was easily visible.
.search-form-wrapper {
border-right: solid 1px #d1d2d4;
display: inline-block;
height: 100%;
max-width: 350px;
min-height: 900px;
min-width: 300px;
position: relative;
width: 30%;
background-color:red;
}
.results-view-wrapper {
display: inline-block;
min-height: 900px;
height: 100%;
padding-left: 10px;
background-color:green;
}
.quick-visualization-wrapper {
display: inline-block;
background-color:black;
min-height: 900px;
height: 100%;
width:10px;
}

Multi-column issue with horizontal scroll

I have a parent div (main) with the horizontal overflow set to auto. I then have a child element (columns) that has all of my column properties on it. The problem is that once the content goes further than the viewport I can no longer control the right hand margins or padding as the column only seems to go as far as the viewport. For example when I put a background color on "columns" the background only goes as far as the edge of the viewport even though the content scrolls further than that.
.main {
overflow-x: visible;
overflow-y: hidden;
width: 100%;
}
.columns {
background: red;
column-fill: auto;
column-width: 670px;
column-gap: 80px;
height: 120px;
padding: 20px;
width: auto;
}
<div class="main">
<div class="columns">
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
</div>
</div>
Here is an example: http://jsfiddle.net/fyG24/
New Answer: Use Pseudo-Elements to Help
Based on your comments, here is new the fiddle that I believe meets your desires. It adds an extra div wrapping .columns I labeled .scroller, and the following css:
html {
height: 100%;
}
body {
margin: 0;
padding: 0;
height: 100%;
}
.main {
background: yellow;
overflow: hidden;
width: 100%;
height: 100%;
position: relative;
}
.main:after {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
height: 120px; /* match columns */
background: red;
z-index: 0;
}
.scroller {
overflow-y: hidden;
overflow-x: auto;
height: 100%;
position: relative;
z-index: 1;
}
.columns {
-webkit-column-fill: auto;
-webkit-column-width: 300px;
-webkit-column-gap: 40px;
-moz-column-fill: auto;
-moz-column-width: 300px;
-moz-column-gap: 40px;
height: 120px;
padding: 0 20px;
text-align: justify;
width: auto;
}
.columns > p:last-of-type:after {
content: '';
display: block;
width: 20px;
height: 1px;
float: right;
margin-right: -20px;
margin-top: -1px;
}
I use a pseudo-element in .main to give the background for .columns set to the explicit height you intend for those, then I also use another pseudo-element in the last p to force rendering the final 20px of the column. It should work no matter how long or what part it takes up, and at height: 1px and margin-top: -1px it should not generate a new column if it falls right at the end of a column of text.
Original Answer: Move Overflow and Set a Right Margin
To get the background to transfer, you need to change some CSS, namely:
.main {
overflow: hidden;
width: 100%;
}
.columns {
overflow-x: auto;
}
This seems to be because the .column background is being limited by the 100% width on the .main which is in control of the horizontal scroll bar in your original code. By making .main purely hidden, then setting overflow-x: auto on .columns, the scroll is now controlled by the .columns div, and allows its background to be seen.
To fix the absence of padding on the far right side, the only think I could come up with was to add the following:
.columns > p:last-of-type {
margin-right: 20px;
}
This puts a right margin on the last p element of the immediate children of .columns which then gave the look I assume you are going for.
Here's the fiddle modified (tested only in Firefox).
I achieved better results just by dividing up your columns css as follows:
.columns {
-webkit-column-fill: auto;
-webkit-column-width: 300px;
-webkit-column-gap: 40px;
-moz-column-fill: auto;
-moz-column-width: 300px;
-moz-column-gap: 40px;
height: 120px;
}
.columns p {
background: red;
height: 120px;
padding: 0 20px;
text-align: justify;
width: auto;
}

Resources