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.
Related
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>
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>
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>
I have a reusable component to display two horizontal sections shared with same shape. The two sections are distributed evenly based on there content.
I post the component in https://jsfiddle.net/kezhuw/7pzmgz6c/2/.
I post a modified version in https://jsfiddle.net/kezhuw/7pzmgz6c/3/ with the desired vision, but it set direct restriction max-height on the img tag.
The max-height restriction should be set on component level, served as API.
Some notes:
The two sections in the component has flex: 1 1 auto;, it is a method to achieve "distributed evenly based on there content".
With max height of the component restricted, the title of section two should be the longest horizontal content in this component. Space should be distributed around this. But it is not, due to object-fit: contain;. Without object-fit: contains;, the image fill its parent div wholly. You can see it in https://jsfiddle.net/kezhuw/7pzmgz6c/4/.
HTML:
<-- language: lang-html -->
<div class="device">
<div class="app">
<div class="component">
<div class="section section1">
<div class="logo">
<img class="image" src="https://www.w3schools.com/css/img_lights.jpg" />
</div>
<div class="title">Section One</div>
</div>
<div class="section section2">
<div class="logo">
<img class="image" src="https://www.w3schools.com/css/img_fjords.jpg" />
</div>
<div class="title">Section Two: Another Title Here, Longer than first</div>
</div>
</div>
<div class="another-component">
Another component
</div>
</div>
</div>
CSS:
.device {
width: 600px;
height: 1200px;
}
.app {
display: flex;
flex-flow: column nowrap;
border: thick solid purple;
}
.app > * {
min-height: 0;
}
.component {
/*
* max-height is an API exposed by this component,
* used by client to restrict max height of component.
*
* This API is exposed as raw css style, not an Vue/React prop.
*
* As an Vue/React Prop, it is possible to calculate max-height of image
* from this prop. I think this calculation process is not maintainable and the result
* may not accurate.
*
* Another way to solve this problem is to treat this max-height as the restriction
* of image, not the component. But I think that may not be friend to consumer.
*/
max-height: 120px;
display: flex;
border: thick solid lightseagreen;
}
.component > * {
min-width: 0;
flex: 1 1 auto;
}
.section1 {
background-color: yellow;
}
.section2 {
background-color: green;
}
.section {
display: flex;
flex-flow: column nowrap;
/* Align from bottom to top, to fix title at the bottom,
* even if there is no logo for this section.
*/
justify-content: flex-end;
}
.section > * {
min-height: 0;
text-align: center;
}
.title {
/* Don't shrink title */
flex: 0 0 auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.logo {
opacity: 0.5;
display: flex;
flex-flow: column nowrap;
align-items: center;
}
.logo > * {
min-height: 0;
min-width: 0;
}
.image {
max-height: 100%;
max-width: 100%;
object-fit: contain;
/* max-height: 70px; */
}
.another-component {
font-size: 30px;
min-height: 50px;
background-color: darkslateblue;
}
I have found some very simple masonry layout now the problem is its displaying it on horizontal i want it to display on vertical. tried to change the masonry height but still display horizontally not so sure how
display: flex
behaves with the other flex properties.
This is the layout I would like to achieve
Fiddle is here
HTML:
<div id="masonry">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/1.jpg">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/2.jpg">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/3.jpg">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/4.jpg">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/6.jpg">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/8.jpg">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/9.jpg">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/7.jpg">
</div>
CSS:
body {
margin: 0; background: #131212;
width: 100vw
}
div#masonry {
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 100vw;
font-size: 0;
}
div#masonry img {
width: 50%;
transition: .8s opacity;
}
div#masonry:hover img { opacity: 0.3; }
div#masonry:hover img:hover { opacity: 1; }
->some guide<-
flex-direction: row | row-reverse | column | column-reverse;
in the:
div#masonry img - you can play with width and/or height also, hope that helps :)