Flexbox - image columns with fixed margins: how to get even image widths - css

I have a design with images in columns with a fixed margin (or gap) between them.
Right now the columns have margins, and because the total margin is different for each column (since there is no left margin on the first column and no right margin on the last), the width of each column image becomes different, causing the height to be different on the middle images.
I tried to divide the margin so that each column uses the same total amount of margin (which seems instinctively over complicated) . I can get that to work, but it doesn't work for 3 columns. You can't make three columns use the same amount of margin I think.
I know there is some "gap" property in css grid, but how do I solve it in flexbox?
See my example code here: example
<template>
<div id="app">
<div class="row">
<div class="col-3">
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
</div>
<div class="col-3">
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
</div>
<div class="col-3">
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
</div>
<div class="col-3">
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "App",
components: {},
};
</script>
<style lang="scss">
#app {
margin: 0 auto;
width: 800px;
border: 3px solid orange;
}
.row {
display: flex;
box-sizing: border-box;
display: flex;
flex-grow: 0;
flex-shrink: 1;
flex-direction: row;
flex-wrap: wrap;
img {
width: 100%;
object-fit: cover;
}
}
.col-3 {
flex: 0 1 25%;
max-width: 25%;
/* width:25%; */
> div {
/* margin-right:25px; */
}
&:first-child > div {
margin-right: 12.5px;
}
&:nth-child(2) > div {
margin-right: 12.5px;
margin-left: 12.5px;
}
&:nth-child(3) > div {
margin-right: 12.5px;
margin-left: 12.5px;
}
&:last-child > div {
margin-left: 12.5px;
}
}
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

Update: I realise now that you're looking for a solution using flex rather than css grid. The other answer provides some options there. If you do want to use grid though this approach is handy as your widths will be automatically calculated with whatever gap you choose.
Use display:grid, and set your container to have four columns with one fractional unit for each column, and a column-gap of the gap you want.
The gap below the image is caused because by default images are inline elements, so they sit with the baseline of text. If you set your images to display:block the gap will disappear.
.row {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-column-gap: 12px;
column-gap: 12px;
border:3px solid yellow;
}
.col-3 {
width: 100%;
}
.col-3 img {
display: block;
width: 100%;
object-fit: cover;
}
<div class="row">
<div class="col-3">
<img src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80" />
</div>
<div class="col-3">
<img src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80" />
</div>
<div class="col-3">
<img src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80" />
</div>
<div class="col-3">
<img src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80" />
</div>
</div>

I have written the code using flexbox and made a little change to your HTML code
<template>
<div id="app">
<div class="row">
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
</div>
</div>
</template>
<style lang="scss">
body {
margin: 0;
.row {
display: flex;
width: 100%;
justify-content: space-between;
div {
padding: 0 10px;
&:nth-child(1) {
padding-left: 0;
}
&:nth-last-child(1) {
padding-right: 0;
}
img {
width: 100%;
}
}
}
}
</style>

Flexbox does have a gap property that will space your items evenly and won't create empty spaces on the right or left. But, based on the code sample you shared, I think your issue is a combination of a couple of things:
Your middle two containers are smaller than your outer two containers because of their extra margin
Your images are set to take the full width of their containers
Because the middle two containers are smaller—and the images are preserving their aspect ratio—you get images that are both narrower and shorter.
If you strip everything down to just using gap, I think you'll be a lot closer to what you're trying to accomplish:
.flexbox {
background-color: #ace;
border: 3px solid orange;
box-sizing: border-box;
display: flex;
flex-flow: row nowrap;
gap: 8px;
width: 800px;
}
.container {
flex: 0 1 25%;
margin: 0;
max-width: 25%;
}
img.full-width {
width: 100%
}
<div class="flexbox">
<div class="container">
<img class="full-width" src="https://picsum.photos/300/200" />
</div>
<div class="container">
<img class="full-width" src="https://picsum.photos/300/200" />
</div>
<div class="container">
<img class="full-width" src="https://picsum.photos/300/200" />
</div>
<div class="container">
<img class="full-width" src="https://picsum.photos/300/200" />
</div>
</div>

Related

Keep side bar full height

I am pretty bad with css, so I was wondering how I can keep my sidebar full height, even though it doesnt have full content.
This is my css:
#wrapper {
display: flex;
flex-wrap: wrap;
background-color: yellow;
}
#wrapper .sideBar {
background-color: green;
width: 200px;
margin-left: 10px;
overflow-x: hidden;
padding-top: 20px;
}
#wrapper .productsBox{
width: 250px;
margin: 3px;
}
But I get this result
However I want a result that looks like this:
Any idea how I can fix this? And I want to have a responsive view, so when a user use phone to browse it displays a decent view. Thats the reason I use the wrapper.
html:
<div id="wrapper">
<form method="get" id="sortForm">
#Html.DropDownList("sort", new SelectList(ViewBag.sortOptions,"Value","Text"), new {#class="form-control", onchange = "sortingChanged()" })
<div class="sideBar">
<strong>Search for products</strong>
<input type="text" class="searchBar" name="SearchString" value="#ViewData["CurrentFilter"]" placeholder="Search.."/>
<hr />
<h6>Price</h6>
<div>
<input type="number" class="priceRangeInput" name="minPrice" value="#ViewData["MinPriceFilter"]" oninput="validity.valid||(value='');" />
<span>Min</span>
</div>
<div>
<input type="number" class="priceRangeInput" name="maxPrice" value="#ViewData["MaxPriceFilter"]" oninput="validity.valid||(value='');" />
<span>Max</span>
</div>
<br />
<hr>
<h6>Types</h6>
<button type="submit" class="btn customGreen" >Set filters</button>
</form>
<br />
</div>
#{
foreach(var p in #Model)
{
<div class="productsBox">
<div class="card">
<a asp-action="Details" asp-route-id="#p.Id">
<img alt="#p.Name" class="contain" src="#p.FilePath" height="300" style="width: 100%;" />
</a>
<h3>#p.Name</h3>
<p class="price">#p.Price kr/#p.Unit</p>
<a asp-action="Details" asp-route-id="#p.Id">
<button class="customGreen">See more</button>
</a>
</div>
</div>
}
}
</div>
html, body {
height: 100%
}
html,
body {
height: 100%; /* to set height to 100% */
}
body {
margin: 0; /* to remove default margins */
}
#wrapper {
height: 100%; /* to set height to 100% */
}
#wrapper {
display: flex;
background-color: yellow;
}
.sideBar {
background-color: #000;
width: 200px;
color: #fff;
overflow-x: hidden;
padding-top: 20px;
}
.product-wrapper {
width: calc(100% - 200px); /* 100% - (sidebar width) */
}
.productsBox {
display: inline-block;
width: 150px;
height: 150px;
color: #fff;
margin: 3px;
background: #2f910f;
}
<html>
<body>
<div id="wrapper">
<div class="sideBar">
Sidebar
</div>
<div class="product-wrapper">
<div class="productsBox">
Item
</div>
</div>
</div>
</body>
</html>
You should take .sideBar out of the #wrapper container and apply width: calc(100% - 210px) to #wrapper (i.e. the width of the container/window minus sidebar minus its left margin). That way both together will be 100% wide, but the contents of #wrapper will not go under the sidebar.
you should add side bar and another containts in different divs and add this code to it:
.wrapper{
display:flex;
flex-direction:row;
}
.sidebar{
background-color:red;
width:50%;
}
.containts{
width:50%;
background-color:green;
}
<div class="wrapper">
<div class="sidebar">
sidebar
</div>
<div class="containts">
containts
</div>
</div>
Add the following code in css it worked for me
height: 100%;

Expand div when divs nested inside overflow

I am trying to figure out how to expand a div when the children contain divs that overflow (to show overlapping images);
div.container {
position:relative
}
div.column {
display: inline-block; // in my case I want to avoid wrapping
width: 180px;
}
div.item-contains-long-image {
display: block;
height: 25px;
overflow: visible;
position: relative;
}
I would like the container to expand vertically to contain the images overflowing from the inner div. I could add padding to the bottom of the div equivalent to the image height, but am looking for a better way.
#Teobis i took your answer as base for a flex example, hope you dont mind :)
div.container { /* this has been rewritten */
display: flex;
flex-direction: row;
}
div.column {
border:1px solid blue; /*just to show the size*/
display: inline-block; /* in my case I want to avoid wrapping */
width: 180px;
vertical-align:top;
}
div.item-contains-long-image {
display: inline-block;
height: 25px;
/* overflow: visible; no needed, default property*/
position: relative;
}
<div class="container">
<div class="column">
<div class="item-contains-long-image">
<img src="http://via.placeholder.com/180x270">
</div>
</div>
<div class="column">
<div class="item-contains-long-image">
<img src="http://via.placeholder.com/180x310">
</div>
</div>
<div class="column">
<div class="item-contains-long-image">
<img src="http://via.placeholder.com/180x110">
</div>
</div>
</div>
Is this structure what you are looking for??
First of all, you need white-space:nowrap; on the parent of display: inline-block;
or you could use flexbox;
div.container {
position:relative;
border:1px solid red; /*just to show the size*/
white-space:nowrap; /*Necesary for no wrap on .column*/
min-height:150px; /* minimum height */
}
div.column {
border:1px solid blue; /*just to show the size*/
display: inline-block; /* in my case I want to avoid wrapping */
width: 180px;
vertical-align:top;
}
div.item-contains-long-image {
display: inline-block;
height: 25px;
/* overflow: visible; no needed, default property*/
position: relative;
}
<div class="container">
<div class="column">
<div class="item-contains-long-image">
<img src="http://via.placeholder.com/180x270">
</div>
</div>
<div class="column">
<div class="item-contains-long-image">
<img src="http://via.placeholder.com/180x310">
</div>
</div>
<div class="column">
<div class="item-contains-long-image">
<img src="http://via.placeholder.com/180x110">
</div>
</div>
</div>
Hope this helps

Nested Flex container not growing with padding

I've built a row layout with Flexbox and within each row (.card), there are three columns, the last (.card-links) of which is also a Flex container. When these three columns stack as rows on mobile, the last doesn't grow with its content + padding and its height is only its padding.
I've found two workarounds where I can set it as margin instead, or display it as a block on mobile, but I'd really like to keep using padding as I have everywhere else and understand why this is happening.
HTML structure:
<div class="card">
<div class="card-img four-three-img">
<img src="" alt="">
</div>
<div class="card-info">
<div class="event-headings">
<h1>Heading</h1>
<h2>Subheading</h2>
</div>
<p>
</p>
<p>Text</p>
</div>
<div class="card-links">
<div class="button-cont">
<a class="pass-button button" href="" target="_blank" role="button">Button Text</a>
</div>
</div>
</div>
Relevant CSS:
.card {
display: flex;
flex-direction: column;
}
.card-img,
.card-info,
.card-links {
flex-basis: 33.3%;
position: relative;
padding: 15px;
}
.card-info {
flex-basis: 40%;
}
.card-links {
display: flex;
justify-content: center;
align-items: center;
flex-basis: 26.7%
}
#media screen and (max-width: 768px) {
.card-links {
padding: 15px 15px 45px;
}
}

fullscreen height with vertically centered content

I have a page that should never scroll and always be the full width and height of the screen.
All content that is in this page should be vertically centered. There should be no scrolling.
Here is a bootply of the code. I can add the actual code here as well if required but seems easier just to supply it this way.
http://www.bootply.com/tDsB8L0er7
If you resize the preview window, you will see that you are still able to scroll some even though the height should be 100% and no content exceeds the height.
I would make .wrapper have a 100vh height, use overflow: hidden to keep any scrollbars from showing up should your content bleed outside of the viewport, then apply display: flex; justify-content: center; align-items: center so that it's child, .container will be centered in the middle
body {
margin: 0;
}
section {
margin-top: 30px;
text-align: center;
}
.message {
font-weight: 900;
font-size: 52px;
font-family: 'Lora', serif;
}
.dim-10 {
opacity: 0.1;
filter: alpha(opacity=10);
}
.input-group-lg > .form-control, .input-group-lg > .input-group-btn > .btn {
font-size: 40px;
}
.awards img {
padding-left: 5px;
padding-right: 5px;
}
.wrapper {
height: 100vh;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<div class="wrapper">
<div class="container">
<section class="logo">
<img class="img-fluid" src="" alt="Missing Image">
</section>
<section class="message">
Some message here
</section>
<section class="user-input">
<div class="row justify-content-sm-center">
<div class="col-10">
<div class="card card-inverse">
<div class="card-block">
<div class="input-group input-group-lg">
<input id="name" type="text" class="form-control" placeholder="Some place holder" aria-describedby="">
<span class="input-group-btn">
<button class="btn btn-secondary btn-success" type="button">Button</button>
</span>
</div>
</div>
</div>
</div>
</div>
</section>
<section class="awards">
<img class="img-fluid" src="" alt="Missing Image">
<img class="img-fluid" src="" alt="Missing Image">
</section>
</div>
</div>
one thing to recommend. be careful with the 100vh value. if you take a look as example on the iOS mobile browser safari, you can see if you scroll down, the viewheigt is changing dynamically. this could mess up with the 100vh value because your site will stretch and shrink with the mobile browsers viewport on scroll.
here is an example how i would do this:
https://codepen.io/joelstuedle/pen/gmomGe
what I'm doing there is the following:
at least two elements are needed to make vertical centering possible. I took the html and the body element.
the css needed for those elements looks like this:
html {
display: table;
width: 100%;
height: 100%;
}
body {
display: table-cell;
vertical-align: middle;
width: 100%;
height: 100%;
}

Align <div> elements side by side

I know this is a rather simple question, but I can't figure it out for the life of me. I have two links which I've applied a background image to. Here's what it currently looks like (apologies for the shadow, just a rough sketch of a button):
However, I want those two buttons to be side by side. I can't really figure out what needs to be done with the alignment.
Here's the HTML
<div id="dB"}>
Download
</div>
<div id="gB">
Gallery
</div>
Here's the CSS
#buyButton {
background: url("assets/buy.png") 0 0 no-repeat;
display:block;
height:80px;
width:232px;
text-indent:-9999px;
}
#buyButton:hover{
width: 232px;
height: 80px;
background-position: -232px 0;
}
#buyButton:active {
width: 232px;
height: 80px;
background-position: -464px 0;
}
#galleryButton {
background: url("images/galleryButton.png") 0 0 no-repeat;
display:block;
height:80px;
width:230px;
text-indent:-9999px;
}
#galleryButton:hover{
width: 230px;
height: 80px;
background-position: -230px 0;
}
#galleryButton:active {
width: 230px;
height: 80px;
background-position: -460px 0;
}
Beware float: left… 🤔
…there are many ways to align elements side-by-side.
Below are the most common ways to achieve two elements side-by-side…
Demo: View/edit all the below examples on Codepen
Basic styles for all examples below…
Some basic css styles for parent and child elements in these examples:
.parent {
background: mediumpurple;
padding: 1rem;
}
.child {
border: 1px solid indigo;
padding: 1rem;
}
Using the float solution my have unintended affect on other elements. (Hint: You may need to use a clearfix.)
html
<div class='parent'>
<div class='child float-left-child'>A</div>
<div class='child float-left-child'>B</div>
</div>
css
.float-left-child {
float: left;
}
html
<div class='parent'>
<div class='child inline-block-child'>A</div>
<div class='child inline-block-child'>B</div>
</div>
css
.inline-block-child {
display: inline-block;
}
Note: the space between these two child elements can be removed, by removing the space between the div tags:
html
<div class='parent'>
<div class='child inline-block-child'>A</div><div class='child inline-block-child'>B</div>
</div>
css
.inline-block-child {
display: inline-block;
}
html
<div class='parent flex-parent'>
<div class='child flex-child'>A</div>
<div class='child flex-child'>B</div>
</div>
css
.flex-parent {
display: flex;
}
.flex-child {
flex: 1;
}
html
<div class='parent inline-flex-parent'>
<div class='child'>A</div>
<div class='child'>B</div>
</div>
css
.inline-flex-parent {
display: inline-flex;
}
html
<div class='parent grid-parent'>
<div class='child'>A</div>
<div class='child'>B</div>
</div>
css
.grid-parent {
display: grid;
grid-template-columns: 1fr 1fr
}
Apply float:left; to both of your divs should make them stand side by side.
keep it simple
<div align="center">
<div style="display: inline-block"> <img src="img1.png"> </div>
<div style="display: inline-block"> <img src="img2.png"> </div>
</div>
.section {
display: flex;
}
.element-left {
width: 94%;
}
.element-right {
flex-grow: 1;
}
<div class="section">
<div id="dB" class="element-left" }>
Download
</div>
<div id="gB" class="element-right">
Gallery
</div>
</div>
or
.section {
display: flex;
flex-wrap: wrap;
}
.element-left {
flex: 2;
}
.element-right {
width: 100px;
}
<div class="section">
<div id="dB" class="element-left" }>
Download
</div>
<div id="gB" class="element-right">
Gallery
</div>
</div>

Resources