4

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.

Community
  • 1
  • 1
matteo
  • 2,934
  • 7
  • 44
  • 59
  • attaching a drawing of the expected result would probably help – Fez Vrasta May 16 '16 at 12:22
  • @Fez Vrasta seriously? I think the list of requirements is crystal clear and totally unambiguous. – matteo May 17 '16 at 08:15
  • I think you can judge by yourself looking at the number of answers. If you want a good answer, you need to ask a good question. – Fez Vrasta May 17 '16 at 12:12
  • So, if a question is particularly difficult, or if the actual answer is "you can't do that" (which might be the case here), does it mean it's a "bad" question? – matteo May 19 '16 at 09:37
  • If I had given some vague description of what I want, I would agree that some drawings would help understand that. But I gave a clear, unambiguous list of requirements that is almost pseudocode. Anybody with enough programming skills and knowledge of CSS to answer the question would actually have to translate the example drawings to a list of requirements pretty much like the one I've provided. Somebody that can't understand the question by a strict list of requirements and would with a few drawings is unlikely to be able to solve it. – matteo May 19 '16 at 09:47
  • IF what I ask is impossible to achieve, it's understandable that nobody has answered that yet, as it is usually difficult to have the certainty that something is impossible. When you can't see a way to do something, you usually keep doubting that it might be possible and you just don't know how. If somebody with enough knowledge and experience tells me that this can't be done in CSS only, and can somewhat reasonably back up the claim, I'll be happy to accept it as an answer. – matteo May 19 '16 at 09:52
  • Now @Fez Vrasta, I would be very curious to know which of the requirements you think are not expressed clearly enough. Maybe I can improve that. Let's say I draw an empty 700x300 window. Wouldn't you be able to draw the div inside it by yourself given the requirements? If not, please draw the two or more results that you think may all satisfy the requirements, proving they are ambiguous or unclear. – matteo May 19 '16 at 09:56
  • 1
    Really, you can do as you prefer, mine was just a suggestion. Feel free to ignore it. – Fez Vrasta May 19 '16 at 10:39

2 Answers2

1

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

Community
  • 1
  • 1
Danny Bullis
  • 3,043
  • 2
  • 29
  • 35
  • I appreciate the help, but this solves only the centering and the fixed aspect ratio. It's not constrained by the height of the parent, only the width. Also it somehow generates a lot of extra empty space at the bottom of the page, causing the page to grow and a vertical scrollbar to appear. And also a few pixels of extra space at the right, causing a horizontal scrollbar to appear too. I suspect the latter has something to do with html, body {width: 100%; height: 100%} (body and html have some weirdnesses) – matteo May 08 '16 at 23:26
  • Hmm, actually it does get constrained by the height (a part from a small error due to the scrollbar and/or the body margin) but when it does, it looses the aspect ratio. – matteo May 08 '16 at 23:29
  • 2
    you can do `overflow: hidden` on the `body` and `html`, which will prevent the extra spacing and scroll bars. That will bring the bottom of the container (the very bottom, after the 75% padding) up to the very bottom of the viewport, since it knows its parent will not expand, since its parent overflow is set to hidden. – Danny Bullis May 09 '16 at 19:12
  • Make sure to put `margin: 0; padding: 0;` on your body and html tags so you don't get that extra bit of space at the top of the browser :] – Danny Bullis May 09 '16 at 19:15
  • Still doesn't preserve the aspect ratio when the height is the constraint. – matteo May 13 '16 at 19:32
  • @matteo Ah, I see what I did now. I had `.center` and `.container` on the same element. Note that I added the `.container` element as a child of `.center` and a parent of `.content`. Should get you what you need. Check out the code above, I've edited it accordingly. – Danny Bullis May 16 '16 at 23:07
  • @matteo http://jsbin.com/revagoqemo/edit?html,css,output it doesn't work well in the full-screen preview mode for some reason but appears correctly in the output window. – Danny Bullis May 16 '16 at 23:08
  • I don't think you get all the requirements. When the height of the window is less than 600 and less than 3/4 of the height, it doesn't preserve the aspect ratio (or it just doesn't respect the max height and overflows the window, can't tell - either way it doesn't meet the requirements). Also note that the grey background is not the content: http://jsbin.com/zorisax/edit?html,css,output – matteo May 17 '16 at 08:37
1

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!

Community
  • 1
  • 1
Syren
  • 1,961
  • 2
  • 15
  • 19