Here is the minimal reproducible example:
body {
margin: 0;
}
div {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
height: 100%; /* has to be 100%, no px or em heights */
}
div img {
max-height: 100%;
max-width: 100%;
object-fit: contain;
}
<div>
<img src="https://dummyimage.com/300x225/000/fff" srcset="https://dummyimage.com/300x225/000/fff 600w, https://dummyimage.com/768x568/000/fff 768w">
</div>
The problem I have is that the image stretches beyond it's natural dimensions and I need to prevent that.
The aim is to:
Parent element of the image in full width and height
Image can't be as background-image (SEO)
Image can't have fixed width and height
Looking only for CSS solutions.
Related
I'm trying to show an image in a "lightbox" style so that it will fill the available area on screen, in this case 90% of the width of the page and 70% of the height.
Using object-fit: contain; seems to be the de facto way to do that but it's not quite working with border-radius. Is it possible to use object-fit on an <img> and still have the border radius applied as intended?
You'll need to resize your browser window to see what happens when you run the below snippet. I've got the same code running in JSFiddle, as per the below video.
div {
margin: auto;
width: 90vw;
height: 70vh;
background-color: DeepSkyBlue;
}
img {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 50px;
background-color: Crimson;
}
<div>
<img src="https://images.freeimages.com/images/large-previews/773/koldalen-4-1384902.jpg">
</div>
Contain isn't really helping here.
Instead, set the max width and height of the img to 100%. The system will fit it in either totally top to bottom or side to side, but the img element will have whatever dimensions it needs so the border radius will work on it OK.
To center the img, if that is what you want, you can display: flex the parent div and justify/align items to the center.
div {
margin: auto auto;
width: 90vw;
height: 70vh;
display: flex;
justify-content: center;
align-items: center;
background-color: DeepSkyBlue;
}
img {
max-width: 100%;
max-height: 100%;
border-radius: 50px;
background-color: Crimson;
}
<div>
<img src="https://images.freeimages.com/images/large-previews/773/koldalen-4-1384902.jpg">
</div>
As commented, setting max-width and max-height seems to be what you need or expect:
div {
margin: auto;
width: 90vw;
height: 70vh;
display:grid;
background-color: DeepSkyBlue;
}
img {
max-width: 100%;
max-height: 100%;
margin:auto;/* x,y center if inside a grid or flex box */
object-fit: contain;/* useless by now, img should keep its ratio */
border-radius: 50px;
border-radius: calc( 5vw + 5vh); /* will scale, maybe you find this usefull */
background-color: Crimson;
}
<div>
<img src="https://images.freeimages.com/images/large-previews/773/koldalen-4-1384902.jpg">
</div>
Use object-fit: cover; instead of contain
Here is a link to the working pen: https://codepen.io/Adam0410/pen/OBqRRN
HTML
<div id="imgWrapper">
<img src="https://thestoryengine.co/wp-content/uploads/2017/11/The-crossroads-Section-images-02.png">
</div>
CSS
#imgWrapper {
display: flex;
justify-content: center;
width: 100%;
padding: 0 20px;
}
#imgWrapper img {
width: 100%;
max-width: 1660px;
height: auto;
}
I don't want the image to exceed its original width (hence max-width) but I want it to scale down to the width of the viewport while maintaining its aspect ratio. Currently, the height does not scale with the width, why is this?
Why do you need display flex to imgWrapper?
Change it to block and it will work as expected. In case you need flex in there add it to other wrapper.
#imgWrapper {
display: block;
}
What about using the vm unit to your #imgWrapper making it always responsive:
#imgWrapper {
display: flex;
justyfy-content: center;
vm: 100%;
padding: 0 20px;
}
Say we have the following layout structure
<div class="full-height>
<nav>....</nav>
<section class="hero>...</section>
</div>
<section class="other-content>....</section>
.full-height {
height: 100vh;
display: flex;
flex-direction: column;
.nav {
padding: 2rem 5rem;
}
.hero {
background-image: .....;
background-size: cover;
flex: 1;
}
}
I want the nav and .hero sections to initially take the full height of the viewport, but when I resize the browser, it should make room for the other section to come in below it.
If I wrap nav and .hero within a parent div and set that divs height to 100vh, it works as expected, but it will always take 100% height of the viewport on resize.
I have a flex with 3 images and I would want them resize if a window is too small right now when window gets smaller first they reorder so they stack vertically and when window gets even smaller the picture gets squeezed instead of resized I would want to keep the image with proper aspect ratio.
.images {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.images div {
display: flex;
margin: 1rem;
}
.images img {
height: 16rem;
max-width: 100%;
}
<div class="images">
<div>
<img src="http://placehold.it/150x200">
</div>
<div>
<img src="http://placehold.it/150x150">
</div>
<div>
<img src="http://placehold.it/150x100">
</div>
</div>
First of all the basic thing to keep in mind to maintain aspect ratio (I'm sure you already know this) is to restrict only one dimension of an image. (Read this)
You are already breaking this in your code- resulting in the 'squeeze' at smaller screen widths:
.images img {
height: 16rem;
max-width: 100%;
}
When window gets smaller first they reorder so they stack vertically
and when window gets even smaller the picture gets squeezed instead of
resized I would want to keep the image with proper aspect ratio.
So here are your options:
So I guess you should remove max-width: 100% and keep width adjust depending on the 16rem height.
.images {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.images div {
display: flex;
margin: 1rem;
}
.images img {
height: 16rem;
/*max-width: 100%;*/
}
<div class="images">
<div>
<img src="http://placehold.it/150x200">
</div>
<div>
<img src="http://placehold.it/150x150">
</div>
<div>
<img src="http://placehold.it/150x100">
</div>
</div>
Well, for small widths you would have horizontal scroll. According to the particular case, if needed you can use some media queries to adjust height at small screen widths.
Let me know your feedback on this. Thanks!
.images img{
height: 16rem;
max-width: 100%;
object-fit: contain;
}
For the aspect ratio fix, you can just run with the CSS3 object-fit property. CSS3 Object-Fit
Set it on your image as:
.images img {
object-fit: contain;
}
That should do the trick of keeping the aspect ratio of the image.
As for the Wrapping that takes place within the flex container, just take out the flex-wrap property in your code so they'll all stay on the same row, rather than wrapping as the container size gets smaller.
EDIT
Try adding a align-self CSS Property to the .images img, see if that's what you're looking for:
.images {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.images div {
display: flex;
margin: 1rem;
}
.images img {
width: 100%;
height: auto;
object-fit: contain;
align-self: flex-start;
}
Hope this helps!
Is there a way to achieve this in CSS-only, without JavaScript?
I want a div with the following requirements:
it ALWAYS maintains a given aspect ratio (e.g. 4:3)
its width never exceeds a given fixed maximum in px, e.g. 800px
its height never exceeds a given fixed maximum, e.g. 600px
its dimensions never exceed those of the viewport
it's as big as it can get given the abovementioned constraints
it's centered both vertically and horizontally
In other words, it should fill either the viewport width or height, whichever is more constraining, while maintaining the given aspect ratio; unless this exceeds the fixed maximum width and/or height, in which case it would be constrained by that.
I don't care how many wrappers one inside another are needed.
I know this answer provides the solution to the fixed aspect ratio constrained by the parent's width, and it's easy to add a fixed-max-width constrain with an additional wrapper; but I can't seem to see a way to adapt that to add the height constraints.
I don't think max-height is doable but I was able to get the rest to work. http://codepen.io/syren/pen/PNvorN
#mixin aspect-ratio($width, $height) {
position: relative;
max-width:400px;
margin: 0 auto;
display: block;
&:before{
display: block;
content: " ";
width: 100%;
padding-top: ($height / $width) * 100%;
}
> .content {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
}
.sixteen-nine {
#include aspect-ratio(16,9);
}
You may be able to do some media query magic where you change the way the aspect ratio works on devices where the top and bottom would get cut off. You can't do max-height when you set the height to be 0, but maybe this would work if you set the width to 0 and did padding-left instead of padding-top or bottom, and inverted the math. You could could that through a media query. I would totally try it out right now but I have to get back to work.
I found a couple more resources that might lead you somewhere as well.
Maintaining aspect ratio with min/max height/width?
https://dev.opera.com/articles/css3-object-fit-object-position/
Good luck!
I would recommend the css flex property. It gives you the ability to center the child elements of a parent element which has the display: flex property. Very nifty. You can achieve horizontal/vertical centering as follows.
Here's another post which has instructions for maintaining aspect ratio:
Maintain the aspect ratio of a div with CSS
html, body {
height: 100%;
width: 100%;
overflow: hidden;
margin: 0;
padding: 0;
}
.center {
display: flex;
justify-content: center;
align-content: center;
align-items: center;
align-self: center;
flex: 1 0 auto;
height: 100%;
width: 100%;
}
.center > div {
align-self: auto;
background-color: #aaa;
}
.container {
width: 100%;
padding-bottom: 75%; /* achieves 4:3 aspect ratio */
margin: 0 20px;
background-color: yellow;
}
.centered {
max-width: 800px;
max-height: 600px;
width: 100%;
height: 100%;
}
<!-- ... -->
<div class='center'>
<div class='container'>
<!-- ensure your .centered div has
some specified height & width -->
<div class='centered'>
Your content
</div>
</div>
</div>
<!-- ... -->
JSBin Demo Here