CSS fit a div inside viewport while keeping aspect ratio - html

I would like to fit a div(red white box) in the center of the page. the black area represent the page layout
the div needs to be height 100% when width cannot be stretched to keep the aspect ratio, or width 100% when height cannot be stretched. I am not sure what property I should use to calculate
It is also important that the div do not overflow the page so no scrollbar will be shown
I have tried: setting height: 100%; width: auto; or width: 100%; height: auto; both can achieve half of the target but I need to combine them together
*update:
I have achieved with 1:1 aspect ratio with #Paulie_D 's answer, but what if the div needs to be 16:9 or 4:3?

You can manage this with vmin
Codepen Demo
vmin
Equal to the smaller of vw and vh.
MDN
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
}
div {
width: 100vmin;
height: 100vmin;
background: white;
margin: auto;
border: 5px solid red;
}
<div></div>

Just create a wrapper div wrapper with a percentage value for padding-bottom, like:
#wrapper {
width: 100%;
padding-bottom: 75%; /** For a 4:3 **/
}
This will cause wrapper to keep a 4:3 aspect ratio.
This is because:
padding percentage is calculated with respect to the width of the
generated box's containing box
Source: w3.org
For any aspect ratio, divide aspect ratio height/aspect ratio width to get the padding-bottom value
aspect ratio / padding-bottom
-----------------------------
16:9 / 56.25%
4:3 / 75%
3:2 / 66.66%
8:5 / 62.5%
Source: w3schools

Related

How do I maintain aspect ratio when height is 100%?

I have an iframe wrapped in a parent div, I know that the parent div and iframe are going to be 100% height of their parent (or the page).
Like this:
<div class="parent"> // will be 100% high
<iframe></iframe> // will be 100% high & wide, pos absolute
</div>
How can I make the width of parent always maintain an aspect ratio?
In the same way that it is common to house iframe videos in a parent div with a padding-top: 56.25%; to maintain a 4:3 aspect ratio - how can I do this with width?
To further elabourate on my comment: if the 100% height you are referring to is the viewport height, then you can easily use vh units to fix the aspect ratio relative to viewport height.
For a 4:3 landscape-orientation ratio, the height is 100vh and the width will be calc(100vh / 3 * 4). You can also calculate it manually as 133vh (technically 133.3333...).
html, body {
margin: 0;
padding: 0;
}
.parent {
background-color: steelblue;
height: 100vh;
width: calc(100vh / 3 * 4);
/* Presentation only */
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-family: Arial, sans-serif;
}
<div class="parent">4:3 ratio</div>

100% height is not appling to body background image on mobile view [duplicate]

I am trying to set a <div> to a certain percentage height in CSS, but it just remains the same size as the content inside it. When I remove the HTML 5 <!DOCTYTPE html> however, it works, the <div> taking up the whole page as desired. I want the page to validate, so what should I do?
I have this CSS on the <div>, which has an ID of page:
#page {
padding: 10px;
background-color: white;
height: 90% !important;
}
I am trying to set a div to a certain percentage height in CSS
Percentage of what?
To set a percentage height, its parent element(*) must have an explicit height. This is fairly self-evident, in that if you leave height as auto, the block will take the height of its content... but if the content itself has a height expressed in terms of percentage of the parent you've made yourself a little Catch 22. The browser gives up and just uses the content height.
So the parent of the div must have an explicit height property. Whilst that height can also be a percentage if you want, that just moves the problem up to the next level.
If you want to make the div height a percentage of the viewport height, every ancestor of the div, including <html> and <body>, have to have height: 100%, so there is a chain of explicit percentage heights down to the div.
(*: or, if the div is positioned, the ‘containing block’, which is the nearest ancestor to also be positioned.)
Alternatively, all modern browsers and IE>=9 support new CSS units relative to viewport height (vh) and viewport width (vw):
div {
height:100vh;
}
See here for more info.
You need to set the height on the <html> and <body> elements as well; otherwise, they will only be large enough to fit the content. For example:
<!DOCTYPE html>
<title>Example of 100% width and height</title>
<style>
html, body { height: 100%; margin: 0; }
div { height: 100%; width: 100%; background: red; }
</style>
<div></div>
bobince's answer will let you know in which cases "height: XX%;" will or won't work.
If you want to create an element with a set ratio (height: % of it's own width), use the aspect-ratio property. Make sure height is not explicitly set on the element for it to work. https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio
.square {
width: 100%;
height: unset;
aspect-ratio: 1 / 1;
}
Historically, the best way to do this was to set the height using padding-bottom. Example for square:
<div class="square-container">
<div class="square-content">
<!-- put your content in here -->
</div>
</div>
.square-container { /* any display: block; element */
position: relative;
height: 0;
padding-bottom: 100%; /* of parent width */
}
.square-content {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
}
The square container will just be made of padding, and the content will expand to fill the container. Long article from 2009 on this subject: http://alistapart.com/article/creating-intrinsic-ratios-for-video
In order to use percentage(%), you must define the % of its parent element. If you use body{height: 100%} it will not work because its parent have no percentage in height. In that case in order to work that body height you must add this in html{height:100%}
In other cases to get rid of that defining parent percentage you can use
body{height:100vh}
vh stands for viewport height
You can use 100vw / 100vh. CSS3 gives us viewport-relative units. 100vw means 100% of the viewport width. 100vh; 100% of the height.
<div style="display:flex; justify-content: space-between;background-color: lightyellow; width:100%; height:85vh">
<div style="width:70%; height: 100%; border: 2px dashed red"></div>
<div style="width:30%; height: 100%; border: 2px dashed red"></div>
</div>
Sometimes, you may want to conditionally set the height of a div, such as when the entire content is less than the height of the screen. Setting all parent elements to 100% will cut off content when it is longer than the screen size.
So, the way to get around this is to set the min-height:
Continue to let the parent elements automatically adjust their height
Then in your main div, subtract the pixel sizes of the header and footer div from 100vh (viewport units). In css, something like:
min-height: calc(100vh - 246px);
100vh is full length of the screen, minus the surrounding divs.
By setting min-height and not height, content longer than screen will continue to flow, instead of getting cut off.
With new CSS sizing properties you can get away with not setting exact height on parent. The new block-size and inline-size properties can be used like this:
<!DOCTYPE html>
<style>
#parent {
border: 1px dotted gray;
height: auto; /* auto values */
width: auto;
}
#wrapper {
background-color: violet;
writing-mode: vertical-lr;
block-size: 30%;
inline-size: 70%;
}
#child {
background-color: wheat;
writing-mode: horizontal-tb;
width: 30%; /* set to 100% if you don't want to expose wrapper */
height: 70%; /* none of the parent has exact height set */
}
</style>
<body>
<div id=parent>
<div id=wrapper>
<div id=child>Lorem ipsum dollar...</div>
Resize the browser window in full page mode. I think the values are relative to viewport height and width.
For more info refer: https://www.w3.org/TR/css-sizing-3/
Almost all browsers support it: https://caniuse.com/?search=inline-size

The width must be equal to the height of the block [duplicate]

I need to maintain the width of an element as a percentage of its height. So as the height changes, the width is updated.
The opposite is achievable by using a % value for padding-top, but padding-left as a percentage will be a percentage of the width of an object, not its height.
So with markup like this:
<div class="box">
<div class="inner"></div>
</div>
I'd like to use something like this:
.box {
position: absolute;
margin-top: 50%;
bottom: 0;
}
.inner {
padding-left: 200%;
}
To ensure the box's aspect ratio is maintained according to it's height. The height is fluid because of it's % margin - as the window's height changes, the box's height will too.
I know how to achieve this with JavaScript, just wondering if there's a clean CSS-only solution?
You can use an image that has the desired proportions as to help with proportional sizing (images can be scaled proportionally by setting one dimension to some value and other to auto). The image does not have to be visible, but it must occupy space.
.box {
position: absolute;
bottom: 0;
left: 0;
height: 50%;
}
.size-helper {
display: block;
width: auto;
height: 100%;
}
.inner {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(255, 255, 153, .8);
}
<div class="box">
<img class="size-helper" src="//dummyimage.com/200x100/999/000" width="200" height="100">
<div class="inner">
1. box has fluid height<br>
2. img has 2:1 aspect ratio, 100% height, auto width, static position<br>
2.1 it thus maintains width = 200% of height<br>
2.2 it defines the dimensions of the box<br>
3. inner expands as much as box
</div>
</div>
In the above example, box, inner and helper are all same size.
You can use vh units for both height and width of your element so they both change according to the viewport height.
vh
1/100th of the height of the viewport. (MDN)
DEMO
.box {
position: absolute;
height:50vh;
width:100vh;
bottom: 0;
background:teal;
}
<div class="box"></div>
There is another, more efficient way to achieve constant aspect ratio according to height.
You can place an empty svg so you dont have to load an external image.
HTML code:
<svg xmlns="http://www.w3.org/2000/svg"
height="100"
width="200"
class='placeholder-svg'
/>
CSS code:
.placeholder-svg {
width: auto;
height: 100%;
}
Change width/height to achieve desired aspect ratio.
Keep in mind, the svg might overflow.
http://www.w3.org/2000/svg is just a namespace. It doesn't load anything.
If you change placeholder-svg class to:
.placeholder-svg {
width: 100%;
height: auto;
}
then height is adjusted according to width.
Demo 1 Width is adjusted according to height and 2:1 aspect ratio.
Demo 2 same as above, but you can resize easily (uses React)
The CSS trick you wrote, works pretty well to keep ratio width / height on an element.
It is based on the padding property that, when its value is in percent, is proportional to parent width, even for padding-top and padding-bottom.
There is no CSS property that could set an horizontal sizing proportionally to the parent height.
So I think there is no clean CSS solution.
As of 2021 there is a property called aspect-ratio.
Most browsers support it
div {
border: 1px solid;
margin: 8px;
}
.box {
width: 100px;
min-height: 100px;
resize: horizontal;
overflow: auto;
}
.inner1 {
aspect-ratio: 1/1;
}
.inner2 {
aspect-ratio: 3/1;
}
<div class="box">
<div class="inner1"></div>
<div class="inner2"></div>
</div>
Run this snippet and resize the outer div manually to see the inner divs behavior
I can't find a pure CSS solution. Here's a solution using CSS Element Queries JavaScript library.
var aspectRatio = 16/9;
var element = document.querySelector('.center');
function update() {
element.style.width = (element.clientHeight * aspectRatio) + 'px';
}
new ResizeSensor(element, update);
update();
CodePen demo!

How to adjust the width of a DIV per height using CSS

I have a wrapper div with a max-width of 1000px and max-height of 650px.
I want to dynamically adjust the width and height of this wrapper while maintaining its aspect ratio. If the browser window is re-sized to reduce the width, the height should be adjusted. If the height is reduced, the width should be adjusted accordingly.
I am using HTML5 and CSS3. This is an attempt to create a responsive layout which works on desktop and mobile devices.
It is pretty easy to adjust height to width.
Since padding percentages are relative to parent's width (even for the height yes) you can do something like this:
.smth {
height: 0;
padding: 100% 0 0 0;
}
Represent width and height using %
DEMO
Try this,
HTML:
<div><p>content</p></div>
CSS:
div {
border: 1px solid red;
width: 35%;
padding: 40%;
box-sizing: border-box;
position: relative;
}
p {
position: absolute;
top: 0;
left: 0;
}
Using Jquery,
jQuery Code:
var width = $('#widthHeight').width();
$('#widthHeight').css('height', width);
jqueryDEMO
As you're using CSS3 you can make use of the vmin or vmax units. 1 vmin is equivalent to 1/100 the size of whichever is smallest out of height or width, whereas 1 vmax is equal to 1/100 the size of the larger value. If a user's screen had a resolution of 1000x800px, 100vmin would be equal to 800px and 100vmax would be equal to 1000px, as the height here is the smaller unit and the width is the larger unit.
In your case you can make use of it like this:
div {
height: 65vmin;
width: 100vmin;
}
Or:
div {
height: 65vmax;
width: 100vmax;
}
JSFiddle demo.
Note that you'll have to adjust these values yourself. 100vmin here will set the width to full size of the screen's height or width (whichever is smallest), and this may not be the desired effect.
There are a couple of browser issues with this approach, unfortunately. As they are new units, support is not perfect. Some browsers will not resize the container until the page is reloaded.
How about this:
FIDDLE
div
{
width: 153.8vh; /* 100/65 * 100 */
height: 65vw; /* 100/1.538 = 65 */
background: pink;
max-height: 650px;
max-width: 1000px;
margin: auto;
position: absolute;
top:0;bottom:0; /* vertical center */
left:0;right:0; /* horizontal center */
}

Maintain aspect ratio of div but fill screen width and height in CSS?

I have a site to put together that has a fixed aspect ratio of approximately 16:9 landscape, like a video.
I want to have it centred and expand to fill the available width, and the available height, but never to grow larger on either side.
For example:
A tall and thin page would have the content stretching the full width while maintaining a proportional height.
A short wide page would have the content stretching the full height, with a proportional width.
There are two methods I've been looking at:
Use an image with the right aspect ratio to expand a container div, but I couldn't get it to behave the same way across major browsers.
Setting a proportional bottom padding, but that only works relatively to the width and ignores the height. It just keeps getting bigger with the width and displays vertical scroll bars.
I know you could do this with JS quite easily, but I'd like a pure CSS solution.
Any ideas?
Use the new CSS viewport units vw and vh (viewport width / viewport height)
FIDDLE
Resize vertically and horizontally and you'll see that the element will always fill the maximum viewport size without breaking the ratio and without scrollbars!
(PURE) CSS
div
{
width: 100vw;
height: 56.25vw; /* height:width ratio = 9/16 = .5625 */
background: pink;
max-height: 100vh;
max-width: 177.78vh; /* 16/9 = 1.778 */
margin: auto;
position: absolute;
top:0;bottom:0; /* vertical center */
left:0;right:0; /* horizontal center */
}
* {
margin: 0;
padding: 0;
}
div {
width: 100vw;
height: 56.25vw;
/* 100/56.25 = 1.778 */
background: pink;
max-height: 100vh;
max-width: 177.78vh;
/* 16/9 = 1.778 */
margin: auto;
position: absolute;
top: 0;
bottom: 0;
/* vertical center */
left: 0;
right: 0;
/* horizontal center */
}
<div></div>
If you want to use a maximum of say 90% width and height of the viewport: FIDDLE
* {
margin: 0;
padding: 0;
}
div {
width: 90vw;
/* 90% of viewport vidth */
height: 50.625vw;
/* ratio = 9/16 * 90 = 50.625 */
background: pink;
max-height: 90vh;
max-width: 160vh;
/* 16/9 * 90 = 160 */
margin: auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
<div></div>
Also, browser support is pretty good too: IE9+, FF, Chrome, Safari- caniuse
Just reformulating Danield's answer in a LESS mixin, for further usage:
// Mixin for ratio dimensions
.viewportRatio(#x, #y) {
width: 100vw;
height: #y * 100vw / #x;
max-width: #x / #y * 100vh;
max-height: 100vh;
}
div {
// Force a ratio of 5:1 for all <div>
.viewportRatio(5, 1);
background-color: blue;
margin: 0;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
}
Use a CSS media query #media together with the CSS viewport units vw and vh to adapt to the aspect ratio of the viewport. This has the benefit of updating other properties, like font-size, too.
This fiddle demonstrates the fixed aspect ratio for the div, and the text matching its scale exactly.
Initial size is based on full width:
div {
width: 100vw;
height: calc(100vw * 9 / 16);
font-size: 10vw;
/* align center */
margin: auto;
position: absolute;
top: 0px; right: 0px; bottom: 0px; left: 0px;
/* visual indicators */
background:
url(data:image/gif;base64,R0lGODlhAgAUAIABAB1ziv///yH5BAEAAAEALAAAAAACABQAAAIHhI+pa+EPCwA7) repeat-y center top,
url(data:image/gif;base64,R0lGODlhFAACAIABAIodZP///yH5BAEAAAEALAAAAAAUAAIAAAIIhI8Zu+nIVgEAOw==) repeat-x left center,
silver;
}
Then, when the viewport is wider than the desired aspect ratio, switch to height as base measure:
/* 100 * 16/9 = 177.778 */
#media (min-width: 177.778vh) {
div {
height: 100vh;
width: calc(100vh * 16 / 9);
font-size: calc(10vh * 16 / 9);
}
}
This is very similar in vein to the max-width and max-height approach by #Danield, just more flexible.
My original question for this was how to both have an element of a fixed aspect, but to fit that within a specified container exactly, which makes it a little fiddly. If you simply want an individual element to maintain its aspect ratio it is a lot easier.
The best method I've come across is by giving an element zero height and then using percentage padding-bottom to give it height. Percentage padding is always proportional to the width of an element, and not its height, even if its top or bottom padding.
W3C Padding Properties
So utilising that you can give an element a percentage width to sit within a container, and then padding to specify the aspect ratio, or in other terms, the relationship between its width and height.
.object {
width: 80%; /* whatever width here, can be fixed no of pixels etc. */
height: 0px;
padding-bottom: 56.25%;
}
.object .content {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
padding: 40px;
}
So in the above example the object takes 80% of the container width, and then its height is 56.25% of that value. If it's width was 160px then the bottom padding, and thus the height would be 90px - a 16:9 aspect.
The slight problem here, which may not be an issue for you, is that there is no natural space inside your new object. If you need to put some text in for example and that text needs to take it's own padding values you need to add a container inside and specify the properties in there.
Also vw and vh units aren't supported on some older browsers, so the accepted answer to my question might not be possible for you and you might have to use something more lo-fi.
I understand that you asked that you would like a CSS specific solution. To keep the aspect ratio, you would need to divide the height by the desired aspect ratio. 16:9 = 1.777777777778.
To get the correct height for the container, you would need to divide the current width by 1.777777777778. Since you can't check the width of the container with just CSS or divide by a percentage is CSS, this is not possible without JavaScript (to my knowledge).
I've written a working script that will keep the desired aspect ratio.
HTML
<div id="aspectRatio"></div>
CSS
body { width: 100%; height: 100%; padding: 0; margin: 0; }
#aspectRatio { background: #ff6a00; }
JavaScript
window.onload = function () {
//Let's create a function that will scale an element with the desired ratio
//Specify the element id, desired width, and height
function keepAspectRatio(id, width, height) {
var aspectRatioDiv = document.getElementById(id);
aspectRatioDiv.style.width = window.innerWidth;
aspectRatioDiv.style.height = (window.innerWidth / (width / height)) + "px";
}
//run the function when the window loads
keepAspectRatio("aspectRatio", 16, 9);
//run the function every time the window is resized
window.onresize = function (event) {
keepAspectRatio("aspectRatio", 16, 9);
}
}
You can use the function again if you'd like to display something else with a different ratio by using
keepAspectRatio(id, width, height);
There is now a new CSS property specified to address this: object-fit.
https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
The feature is widely supported by now (http://caniuse.com/#feat=object-fit).
Danield's answer is good, but it can only be used when the div fills the whole viewport, or by using a bit of calc, can be used if the width and height of the other content in the viewport is known.
However, by combining the margin-bottom trick with the method in the aforementioned answer, the problem can be reduced to just having to know the height of the other content. This is useful if you have a fixed height header, but the width of the sidebar, for example, is not known.
body {
margin: 0;
margin-top: 100px; /* simulating a header */
}
main {
margin: 0 auto;
max-width: calc(200vh - 200px);
}
section {
padding-bottom: 50%;
position: relative;
}
div {
position:absolute;
background-color: red;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
<main>
<section>
<div></div>
</section>
</main>
Here it is in a jsfiddle using scss, which makes it more obvious where the values come from.
Based on Daniel's answer, I wrote this SASS mixin with interpolation (#{}) for a youtube video's iframe that is inside a Bootstrap modal dialog:
#mixin responsive-modal-wiframe($height: 9, $width: 16.5,
$max-w-allowed: 90, $max-h-allowed: 60) {
$sides-adjustment: 1.7;
iframe {
width: #{$width / $height * ($max-h-allowed - $sides-adjustment)}vh;
height: #{$height / $width * $max-w-allowed}vw;
max-width: #{$max-w-allowed - $sides-adjustment}vw;
max-height: #{$max-h-allowed}vh;
}
#media only screen and (max-height: 480px) {
margin-top: 5px;
.modal-header {
padding: 5px;
.modal-title {font-size: 16px}
}
.modal-footer {display: none}
}
}
Usage:
.modal-dialog {
width: 95vw; // the modal should occupy most of the screen's available space
text-align: center; // this will center the titles and the iframe
#include responsive-modal-wiframe();
}
HTML:
<div class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">Video</h4>
</div>
<div class="modal-body">
<iframe src="https://www.youtube-nocookie.com/embed/<?= $video_id ?>?rel=0" frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
<div class="modal-footer">
<button class="btn btn-danger waves-effect waves-light"
data-dismiss="modal" type="button">Close</button>
</div>
</div>
</div>
</div>
This will always display the video without those black parts that youtube adds to the iframe when the width or height is not proportional to the video. Notes:
$sides-adjustment is a slight adjustment to compensate a tiny part of these black parts showing up on the sides;
in very low height devices (< 480px) the standard modal takes up a lot of space, so I decided to:
hide the .modal-footer;
adjust the positioning of the .modal-dialog;
reduce the sizes of the .modal-header.
After a lot of testing, this is working flawlessly for me now.
here a solution based on #Danield solution, that works even within a div.
In that example, I am using Angular but it can quickly move to vanilla JS or another framework.
The main idea is just to move the #Danield solution within an empty iframe and copy the dimensions after iframe window size changes.
That iframe has to fit dimensions with its father element.
https://stackblitz.com/edit/angular-aspect-ratio-by-iframe?file=src%2Findex.html
Here a few screenshots: