Creating a resizable and scalable image grid using flexbox - html

I am trying to make a resizable/scalable image grid using FlexBox.
The problem is that whenever the image's proportions are too thin due to resizing the window, the FlexBox will place additional images on previous rows to fill out the space.
It looks good under some window proportions. But, if the window is too short in height and too long in width, then images from lower rows will move to upper rows.
I have tried using flexboxes and tables. When using FlexBox, I have also tried to set the width of flex items using the flex property for images. The issue with this is that images will stretch and distort in order to fill up the required space.
Here is code that I am working with:
html, body {
height: 100%;
width: 100%;
margin: 0;
}
#flex-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
background-color: blue;
height: 100%;
}
#flex-container>img {
width: auto;
height: auto;
max-width: 25%;
max-height: 50%;
margin: auto;
}
<div id="flex-container">
<img src="https://via.placeholder.com/150C/O https://placeholder.com/">
<img src="https://via.placeholder.com/150C/O https://placeholder.com/">
<img src="https://via.placeholder.com/150C/O https://placeholder.com/">
<img src="https://via.placeholder.com/150C/O https://placeholder.com/">
<img src="https://via.placeholder.com/150C/O https://placeholder.com/">
<img src="https://via.placeholder.com/150C/O https://placeholder.com/">
<img src="https://via.placeholder.com/150C/O https://placeholder.com/">
<img src="https://via.placeholder.com/150C/O https://placeholder.com/">
</div>
I want to make the images stay on the same row and column, only resizing themselves in accordance to how large the page is.
I don't need to use FlexBox, but it seems to be the best way to accomplish this task.

Flexbox is a good choice for this layout but there are some tricky side effects when images are the flex items. In order to make this more straightforward, I find it very helpful to put the images inside wrapper divs.
Control the number of items per row now with the width, you can use media queries to change the width as the screen gets smaller but you will need to reconsider the 100% fixed height of the container at that point.
I have put comments in the CSS to highlight the important bits.
* {
box-sizing: border-box; /* so borders and padding dont get in the way */
}
html,
body {
height: 100%; /* you didint need width 100% here */
margin: 0;
}
#flex-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
background-color: blue;
height: 100%;
}
.img-wrap {
width: 25%;
height: 50%;
display: flex; /* flexbox layout for the image wrappers, allows for... */
align-items: center; /* ...easy centering */
justify-content: center;
padding: 10px; /* just some spacing so the images can breathe, remove if you want them edge to edge */
border: 1px solid red; /* so you can easily see the bounds of the wrapper divs, remove this */
}
.img-wrap img {
max-width: 100%; /* max dimensions on the images so they always fit */
max-height: 100%;
height: auto; /* maintain aspect ratio */
width: auto;
}
<div id="flex-container">
<div class="img-wrap"><img src="https://picsum.photos/200"></div>
<div class="img-wrap"><img src="https://picsum.photos/300/200"></div>
<div class="img-wrap"><img src="https://picsum.photos/400/800"></div>
<div class="img-wrap"><img src="https://picsum.photos/500/250"></div>
<div class="img-wrap"><img src="https://picsum.photos/450/505"></div>
<div class="img-wrap"><img src="https://picsum.photos/350/520"></div>
<div class="img-wrap"><img src="https://picsum.photos/250/300"></div>
<div class="img-wrap"><img src="https://picsum.photos/550/200"></div>
</div>

Related

Why is my CSS grid-template-columns stacking and not being consistent

So I have these carousel thumbnails that supposedly keep adding to the right until each of them reached the width of 120px and they'll go into the next row. These thumbnails are wrapped by a wrapper which its width fits its content.
HTML:
<div class="carousel-thumbnail-wrapper">
<div class="carousel-thumbnail mod-active">
<img src="./img/project-slider-1.jpg" alt="">
</div>
<div class="carousel-thumbnail">
<img src="./img/project-slider-2.jpg" alt="">
</div>
</div>
CSS:
.cg-carousel > .carousel-thumbnail-wrapper {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
column-gap: 8px;
width: fit-content; /** the width fits its content... */
max-width: 100%; /** ...but can only grow not more than 100% of its parent's */
}
.cg-carousel > .carousel-thumbnail-wrapper > .carousel-thumbnail {
box-sizing: border-box;
border-radius: var(--border-radius-xs);
cursor: pointer;
position: relative;
margin-bottom: 8px;
max-width: 200px; /** each thumbnail has maximum 200px width */
}
Normally, if everything goes right it will show like this: Images keep adding to the right
It does work like that sometimes. But when I refresh the page or go to another page back to back, for some reason they're stacking, like this: Images suddenly stacking
What did I do wrong?
So your code isn't wrong per se, but your selectors are a little off, and your images need a width of 100%, otherwise they will not resize - their parent will, but they will still remain their original size. So if you change your selectors so that they actually target the elements in your html, and set the width of the images to 100%, it should work perfectly. The javascript in my other answer makes your code fully fool-proof as it combats any css issues older browsers may face. But if old browsers are not a problem, then use this answer. Below is a snippet. Here is a jsfiddle link if you want to resize the viewport, add images or play with the code in general: https://jsfiddle.net/258b9x6e/1/
.carousel-thumbnail-wrapper {width: 100%}
.carousel-thumbnail-wrapper {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
column-gap: 8px;
width: fit-content; /** the width fits its content... */
max-width: 100%; /** ...but can only grow not more than 100% of its parent's */
}
.carousel-thumbnail {
box-sizing: border-box;
border-radius: var(--border-radius-xs);
cursor: pointer;
position: relative;
margin-bottom: 8px;
max-width: 200px; /** each thumbnail has maximum 200px width */
}
img {
width: 100%;
}
<div class="carousel-thumbnail-wrapper">
<div class="carousel-thumbnail mod-active">
<img src="https://via.placeholder.com/200" alt="">
</div>
<div class="carousel-thumbnail">
<img src="https://via.placeholder.com/200" alt="">
</div>
</div>
The following snippet will resize the images, to make them small enough to be able to fit on one row, until they are 120px in width, which is when they will no longer be shrunk, but moved to the next row. This achieves the same results as my other answer, but also works on older browsers, at the expense of having to use JS. My other answer only works on newer browsers but does not need any JS.
Here, I've used 3 imgs but you can use as many as you would like This is also responsive, so you can resize your browser and the images will still fit well.
document.querySelectorAll("img")[document.querySelectorAll("img").length-1].onload = re_calculate_image_width;
function re_calculate_image_width() {
let count = document.querySelectorAll(".img-wrapper").length;
document.querySelector(".outer-wrapper").style.setProperty("--how-many", (count).toString());
}
alert("Important: Read this: \n If you dynamically add more images, you MUST call the ***re_calculate_image_width*** function when the added images have finished ***loading***.");
.outer-wrapper {
width: 100%;
background: rgba(255,255,0,0.6);
--how-many: 5;
}
.img-wrapper {
display: inline-block;
min-width: 120px;
max-width: 200px;
width: calc(90% / var(--how-many));
height: auto;
}
img {
display: inline-block;
margin: 0px;
padding: 0px;
height: 100%;
width: 100%;
box-shadow: 0px 0px 1px 1px #000;
}
<div class="outer-wrapper">
<div class="img-wrapper">
<img src="https://via.placeholder.com/200" alt="">
</div>
<div class="img-wrapper">
<img src="https://via.placeholder.com/200" alt="">
</div>
<div class="img-wrapper">
<img src="https://via.placeholder.com/200" alt="">
</div>
</div>

Justify content does not seem to affect my flexbox children. Is it because of my height/width properties for one of my elements?

What I have set up is a flexbox that contains 3 children: the previous button, a div that contains the slideshow, and the next button. I have the flexbox set to justify-content: center, but the arrows stick to the far sides of the flexbox instead of right next to the slide div on either side. There is a gap between each arrow and the slide that won't go away unless I make the browser window narrower so the flexbox doesn't have any extra room. Setting justify-content to center should be keeping them all together.
I'm having the same issue with the slide container as well. It is also a flexbox and contains 3 children: a title, the image, and numbers at the bottom. When I shrink the window, the slide image shrinks to fit, but the title and numbers both stay stuck to the top and bottom of the flexbox instead of clustered in the middle with the image.
I would like this to be dynamic so I won't have to adjust the CSS as much with media queries for a number of different breaking points.
Things I have tried:
setting the margins to 0px for all children
setting the padding to 0px for all children
putting the buttons inside the slideshow container instead of the
current parent and setting the slideshow container to flex
every other option for justify-content (center, flex-start, flex-end, etc); none of them seemed to have any effect on the children
removing the width attribute from my slideshow container and setting it to auto (this made the slides disappear)
removing the height attribute from my slide container (this did fix the issue, but it also caused the slide to extend past the boundaries of its container)
I am also using some javascript that probably doesn't need to be included, but it sets each new slide's display to block and each previous slide's display to none. In order for this slideshow to work, I am under the impression that the slides need to their position set to absolute, and their parent container set to relative.
Here is a fiddle with the exact code I am using. I initially tried to simplify it to ask this question by removing container1 and the description on the left, but then I couldn't replicate the problem I was having. I am wondering if it has something to do with the height or width attributes of one of elements.
https://jsfiddle.net/kj7wzr5a/
.container1 {
width: 85%;
height: 650px;
display: flex;
justify-content: space-around;
align-items: center;
margin: 0 auto;
text-align: center;
}
.left {
width: 45%;
text-align: center;
margin-left: 30px;
margin-right: 20px;
}
.descrip1 {
margin-top: 20px;
}
.right {
height: 100%;
width: 45%;
display: flex;
justify-content: center;
align-items: center;
}
.flex {
height: 100%;
display: flex;
flex-direction: column;
align-content: center;
}
.slideshow-container {
height: 100%;
width: 360px;
margin-top: 25px;
position: relative;
}
.slide {
height: 90%;
width: 100%;
display: none;
position: absolute;
}
.slide:first-child {display: block}
.slide {
-webkit-transition: 0.5s ease;
transition: 0.5s ease;
}
.slide img {
height: 100%;
width: 100%;
margin: 0px;
object-fit: contain;
}
/* Next & previous buttons */
.buttons {
height: auto;
}
<div class="container1 bodytext">
<div class="left">
<div class="descrip1">
<p>Blah blah text</p>
</div>
</div>
<div class="right">
<div class="buttons">
<a class="slider_nav prev"><</a>
</div>
<div class="slideshow-container">
<div class="slide">
<div class="flex">
<div class="text">New Character</div>
<img src="1.png" style="width:100%">
<div class="numbertext">1 / 10</div>
</div>
</div>
<div class="slide">
<div class="flex">
<div class="text">New Character Stats</div>
<img src="2.png" style="width:100%">
</div>
</div>
</div>
<div class="buttons">
<a class="slider_nav next">></a>
</div>
</div>
</div>
I am fairly new to coding and haven't had any formal training.

css flex 2x2 grid full screen with centered images view crop

I have a very simple markup of 4 images, where the goal is to get a 2x2 responsive image grid that will:
Maintain 2x2 fit screen on all screens regardless of size, using only flex.
Keep the images centered and maintain aspect ratio( "cropping" them from
view if needed according to screen )
Be able to transform on hover.
Not change the markup ( and don't ask why )
Try to achieve a situation where a single property ( or min number of ) could
transform the grid to 4 x whatever ( 4 in a row ) meaning not change markup to galley rows of 2..
the markup is :
<div class="container">
<div class="gallery">
<img class="item" src="https://source.unsplash.com/random/" alt="Example image">
<img class="item" src="https://source.unsplash.com/random/" alt="Example image">
<img class="item" src="https://source.unsplash.com/random/" alt="Example image">
<img class="item" src="https://source.unsplash.com/random/" alt="Example image">
</div>
</div>
And the css :
/** Add basic reset **/
.container {
display: flex;
/* flex-wrap: wrap; */
/* justify-items: center; */
justify-content: center;
/* max-height: 100vh; */
height: 100vh;
}
img {
margin: 5px;
transition: all 2s;
flex-basis: 49%;
max-height: 49vh;
align-self: center;
/* max-width: calc(49vh - 10px); */
}
.gallery {
display: flex;
flex-wrap: wrap;
flex: 1 1 49%;
align-items: center;
overflow: hidden;
}
img:hover {
transform: scale (1.1);
}
adding :
img {
object-fit: cover;
}
kind of works, but it has a "hack-ey" feels to it and also have 2 major problems (1) it will indeed "crop" the images - but they are not centered and (2) on hover they will "spill" from container.
I am quit sure that this is NOT the right way to achieve what I need, but not so sure as to what flex-property I am missing, or using wrong here.
Edit I to be more clear attached an image of what I am trying to achieve, where each represents a different size screen / viewport without any scroll.
Images need to be always centered and cropped in edges according to screen ( somewhat like a full-screen background usually works )
Perhaps this may help, although I'm not entirely sure if I fully understand your request.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.container {
display: flex;
flex-flow: row wrap;
height: 100vh;
width: 100vw;
overflow: hidden;
}
.child {
/* FOR 4 IN A GRID */
flex: 1 0 50%;
max-width: 50%;
max-height: 50%;
/* FOR YOUR 4 IN 1 ROW */
/* flex: 1 0 25%;
max-width: 25%; */
/* FOR BOTH */
padding: 5px;
object-fit: none;
}
.child:hover {
transform: scale(1.1);
}
</style>
</head>
<body>
<div class="container">
<img src="https://images-na.ssl-images-amazon.com/images/I/81XYWLSWlOL._SX466_.jpg" class="child">
<img src="https://images-na.ssl-images-amazon.com/images/I/81XYWLSWlOL._SX466_.jpg" class="child">
<img src="https://images-na.ssl-images-amazon.com/images/I/81XYWLSWlOL._SX466_.jpg" class="child">
<img src="https://images-na.ssl-images-amazon.com/images/I/81XYWLSWlOL._SX466_.jpg" class="child">
</div>
</body>
</html>
Updated Version
It's up to you to to add in media queries to set the size of the container itself.

Responsive CSS Image size effects row sizes

I know this sounds like it's been asked before but I've played around with a lot of techniques I've found from other questions and nothing seems to get the desired effect I need.
I'm trying to make something that will be responsive like this:
Responsive Example gif
I basically need an image to be centered, where the image is at 100% size.
Here is what I tried to get this effect:
I first made a div containing three child divs for "columns". Then inside the center column I made three child divs for "rows". Now I need the image to fill the max width it's allowed while still maintain that square aspect ratio. As well the height of the image should determine the height of the top and bottom rows.
Then it should just be a matter of having the text inside the top and bottom row align to the bottom and top of their divs respectively.
This would look something like this:
HTML Visualization of columns
HTML Visualization of center rows
The issue I'm running into is I can't seem to get the center image to determine the heights of the rows above and below it.
I've tried...
Flexbox
using vh (view height)
and a bit of using calc() but to no luck
Setting aspect ration with padding-top: 100%
What the code looks like
/* .row & .col from materialize.css */
.full {
height: 100vh;
}
.art_top {
height: 10vh;
/* I Don't actually want this fixed though */
padding-bottom: 10px;
display: flex;
}
.art_center {
height: 80vh;
/* I Don't actually want this fixed though */
}
.art_bottom {
height: 10vh;
/* I Don't actually want this fixed though */
padding-top: 10px;
display: flex;
}
#cover_art {
width: 100%;
height: 100%;
background: center / cover no-repeat;
}
#song_name {
align-self: flex-end;
}
#artist_name {
align-self: flex-start;
}
<div class="row">
<div class="col s2 m3 full"></div>
<div class="col s8 m6 full">
<div class="row art_top">
<a id="song_name" class="bold-title"></a>
</div>
<div class="row art_center">
<div id="cover_art"></div>
</div>
<div class="row art_bottom">
<a id="artist_name" class="bold-title"></a>
</div>
</div>
<div class="col s2 m3 full"></div>
</div>
Flexbox makes this kind of layout very straightforward. The trick is selectively allowing items to flex or shrink.
The flex property shorthand takes 3 values for flex-grow, flex-shrink, and flex-basis (the initial width or height depending on flex direction). Just keep clear which divs are serving as flex containers as you get into the details in the layout. It is very common to have divs that are both flex containers and flex items themselves too.
I also recommend using an img element instead of applying the image as a background so you dont have trouble with the aspect ratio in responsive window sizes.
A very nice resource: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
/* .row & .col from materialize.css */
body {
margin: 0;
}
.full {
height: 100vh;
}
.row {
display: flex;
}
.column {
flex: 1 1 auto;
}
.column2 {
background: #b4c2cf;
display: flex;
flex-direction: column;
}
.column1 {
background: #cbb3cc;
}
.column3 {
background: #cbb2b2;
display: flex;
align-items: center;
justify-content: center;
}
.art_top {
flex: 1 0 10vh;
display: flex;
justify-content: flex-start
align-self: flex-end;
}
.art_center {
flex: 1 1 auto;
display: flex;
align-items: center;
justify-content: center;
}
.art_bottom {
flex: 1 0 10vh;
text-align: right;
}
#cover_art {
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
}
#song_name {
align-self: flex-end;
}
#artist_name {
align-self: flex-start;
}
.bold-title {
display: block;
font-weight: bold;
padding: 10px;
}
.small-box {
background: #8f588c;
height: 100%;
max-height: 70px;
width: 100%;
max-width: 70px;
}
<div class="row full">
<div class="column column1"></div>
<div class="column column2">
<div class="art_top">
<a id="song_name" class="bold-title">My Album Title</a>
</div>
<div class="art_center">
<img id="cover_art" src="https://picsum.photos/400" />
</div>
<div class="art_bottom">
<a id="artist_name" class="bold-title">Artist Name</a>
</div>
</div>
<div class="column column3">
<div class="small-box"></div>
</div>
</div>

Adding space between flexbox items

I'm trying to create a horizontally scrollable div with flexbox. So far, I have most of it. However, the only problem I am facing is that I'm trying to add space to my items, but for some reason, nothing seems to be working. I've tried adding margin, padding, justifying content, etc. Here's a jsfiddle of what I'm trying to achieve.
.grid {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
margin-bottom: 20px;
justify-content: space-between;
}
/*Each item is one column*/
.item {
width: 50%;
}
.article-scroll-mobile {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
flex-wrap: nowrap;
text-align: center;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
/*For iOS smooth scroll effect*/
}
<div class="grid article-scroll-mobile">
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
</div>
There a few things you have to consider.
First of all; with justify-content you define how remaining space is handled. By using space-between your items will be aligned so that the space between them is equal, by setting it to center the remaining space will be around all items, with all items stuck together.
In your case though, there is no remaining space, because your items actually stretch the div. So that doesn't help you.
Next; you've set the width of an item to 50%. Which is fine, your item's will be 50% of the viewport. That's because your grid will implicitly be 100% of the viewport. But because your image overflows the box, you can set margins if you want, and they will put the items further apart, but you need big-ass margins to actually see them. Bigger then the overflowing of your image.
So, to fix this, you make the images responsive by making them as width as the item;
.item img { display: block; height: auto; width: 100%; }
But that poses another problem; flexbox tries to size it's flex items to fit it all into the flex container. So you'll see that it automatically resizes your items so they will all fit in. To fix this, you have to explicitly force the width of your items;
.item { flex: 0 0 50%; }
Which is a shorthand for;
.item { flex-grow: 0; flex-shrink: 0; flex-basis: 50%; }
So basically you say; make my item 50% of it's container, and don't use your awesome algorithm to try to make it bigger or smaller.
Now you've got what you want, and you can use margin-right: 20px for example to create a 20px space between your items.
Full snippet;
.grid { display: flex; width: 100%; }
.item { flex: 0 0 50%; margin-right: 20px; }
.item img { display: block; height: auto; width: 100%; }
.article-scroll-mobile {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
flex-wrap: nowrap;
text-align: center;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
/*For iOS smooth scroll effect*/
}
<div class="grid article-scroll-mobile">
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
</div>
There is column-gap to adjust row gaps between elements.
.form-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: stretch;
column-gap: 0.875rem;
}