CSS to create element with fixed ratio [duplicate] - css

This question already has answers here:
Maintain aspect ratio of div but fill screen width and height in CSS?
(9 answers)
Closed 7 years ago.
I want to create an HTML page with:
A fixed-width, full-height Navigation pane on the left
A square element in the centre of the remaining area
I want this square to be as big as possible, expanding to fill the area not taken up by the navigation pane.
I have a JavaScript solution for this (see below and as a jsFiddle), but I'm hoping that it is possible to do this as a CSS only solution.
<!DOCTYPE html>
<html lang=en>
<head>
<style>
html, body {
margin: 0;
height: 100%;
background-color: #fff;
}
nav {
height: 100%;
width: 96px;
background-color: #666;
}
main {
position: absolute;
background-color: #000;
color: #fff;
}
</style>
</head>
<body>
<nav>
Navigation
</nav>
<main>
This should be square
</main>
<script>
;(function createSquareArea() {
var main = document.querySelector("main")
var nav = document.querySelector("nav")
var navWidth = nav.getBoundingClientRect().width
var debounceDelay = 100
var timeout
window.onresize = windowResized
maintainRatio()
function windowResized() {
if (timeout) {
window.clearTimeout(timeout)
}
timeout = window.setTimeout(maintainRatio, debounceDelay)
}
function maintainRatio() {
timeout = 0
var windowHeight = window.innerHeight
var mainWidth = window.innerWidth - navWidth
var minDimension = Math.min(windowHeight, mainWidth)
var left = (mainWidth - minDimension) / 2 + navWidth
var top = (windowHeight - minDimension) / 2
main.style.left = left + "px"
main.style.top = top + "px"
main.style.width = minDimension + "px"
main.style.height = minDimension + "px"
}
})()
</script>
</body>
</html>

JSFiddle: https://jsfiddle.net/ymzq6zm0/7/
HTML
<nav>
Navigation
</nav>
<main>
<div class="sc">
This should be square
</div>
</main>
CSS
html, body {
margin: 0;
height: 100%;
background-color: #fff;
}
.wrapper {
display: flex;
align-items: center;
height: 100%;
}
nav {
float: left;
height: 100%;
width: 96px;
background-color: #666;
}
main {
float: left;
width: calc(100% - 96px);
height: 100vmin;
max-height: calc(100vw - 96px);
}
.sc {
margin: 0 auto;
background-color: #000;
color: #fff;
height: 100vmin;
width: 100vmin;
max-width: 100%;
max-height: calc(100vw - 96px);
}
How it works
Our main objective here is to:
Center align .sc
Align .sc vertically center
Make sure .sc is always a sqaure
Make .sc responsive
The square is highly responsive as it changes its height and width according to the window's or view port's height and width. We need to use vw (viewport's width) and vmin (lowest value between viewport's height and width). Read more about these units here: https://css-tricks.com/viewport-sized-typography/
To make .sc a square, we need to make sure its width and height are always equal. Since the ratio of height and width of viewport is not always the same, we need to find out the lowest value between these two and assign them to .sc which can be done using the vmin unit mentioned above.
The square should always remain centered in the remaining area after the navigation on the left, never cross the remaining area and resize accordingly.
This can be accomplished the following codes:
nav {
float: left;
height: 100%;
width: 96px;
background-color: #666;
}
main {
float: left;
width: calc(100% - 96px);
height: 100vmin;
max-height: calc(100vw - 96px);
}
.sc {
margin: 0 auto;
background-color: #000;
color: #fff;
height: 100vmin;
width: 100vmin;
max-width: 100%;
max-height: calc(100vw - 96px);
}
main is the remaining area after nav. We make sure of this by using the calc property to subtract the nav width from 100%.
The .sc is placed inside main and we have added the extra max-width and max-height properties to make sure .sc always resizes itself according to main.
max-height: calc(100vw - 96px); property of .sc is always equal to width: calc(100% - 96px); property of main. They both calculate the same values.
By adding max-width: 100%; to .sc we make sure it's maximum width is equal to that of main.
Now since, both the max-height and max-width along with width and height of .sc remain the same, it will always be a square.
At the end we put both nav and main inside .wrapper which is a flexbox and has align-items: center; property. This will ensure that the square is always vertically centered with respect to the nav.

This can be accomplished with viewport units.
HTML
<div class='page-wrapper'>
<nav>
</nav>
<div class='content'>
<div class='square'>
</div>
</div>
</div>
CSS
body, html
{
height: 100%;
}
.page-wrapper
{
display: flex;
height: 100%;
width: 100%;
}
nav
{
height: 100%;
width: 200px;
background-color: #444;
}
.content
{
flex-grow: 1;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
.square
{
width: 100vh;
height: 100vh;
background-color: #111;
max-width: calc( 100vw - 200px );
max-height: calc( 100vw - 200px );
}
See this jsFiddle https://jsfiddle.net/ryannwarner/hugt40cm/

Viewport units and calc can do a lot of this but I'm not sure on your final desired result.
If you want both dimension to recalculate you will need javascript.
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
height: 100%;
background-color: #fff;
}
nav {
height: 100%;
width: 96px;
background-color: #666;
}
main {
position: absolute;
background-color: lightgreen;
color: #fff;
top: 0;
left: 96px;
height: calc(100vw - 96px);
max-height: 100vh;
width: calc(100vw - 96px);
max-width: 100vh;
}
<nav>
Navigation
</nav>
<main>
This should be square
</main>
JSfiddle demo with no max-height...I think you can see the problem with a CSS method.

Related

Fit child div with `aspect-ratio` to parent in Firefox [duplicate]

I wish to have a div section that fills its div parent as much as possible while maintaining a ratio.
the render result would be like this :
What I do have so far :
html,
body {
height: 100%;
}
.parent {
/* Parent's height and width are unknown,
it could be dynamic, e.g. parent is part of a flex layout. */
height: 80%;
width: 90%;
border-style: solid;
border-width: 2px;
border-color: black;
}
.child {
width: 90vw;
/* 90% of viewport vidth */
height: 50.625vw;
/* ratio = 9/16 * 90 = 50.625 */
max-height: 90vh;
max-width: 160vh;
/* 16/9 * 90 = 160 */
margin: auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #A0522D;
}
<div class="parent">
<div class="child">
content that is not images...
</div>
</div>
This css behaves like the way I want BUT this is using the viewport instead of the parent div which is a problem in real conditions.
I am looking for a way to fill based on the parent div.
Using aspect-ratio: 16 / 9; overflow: hidden; (aspect-ratio MDN docs) should give you the exact result you're looking for without needing to use the padding trick. Make sure the parent is set to display: grid or else it may not scale properly.
The aspect-ratio CSS property is supported by all major browsers (caniuse.com) except Safari, though Safari plans to add support this year. This is the best/correct way to achieve this effect, without having to resort to JavaScript or any hack solutions.
Related questions and answers here on Stack Overflow:
https://stackoverflow.com/a/66786774/3824249 (very similar to my solution)
https://stackoverflow.com/a/20593342/3824249 (another CSS-only alternative, though hackier using position: absolute for element positioning)
Here is my solution in action:
html, body { height: 100%; }
.parent {
display: grid;
resize: both;
height: 50%;
width: 90%;
border: 2px solid #000;
overflow: hidden;
}
.child {
width: 100%;
max-height: 100%;
margin: auto;
aspect-ratio: 16 / 9;
overflow: hidden;
box-sizing: border-box;
position: relative;
background: #a0522d;
text-align: center;
font-size: 20px;
color: white;
/* using the below to center the text, optional */
display: flex;
align-items: center;
justify-content: center;
}
<div style="padding: 5px 10px; margin-bottom: 10px; background: #f00; color: #fff; text-align: center; z-index: 1;">Resize the block below using the resize controls to see this in action.</div>
<div class="parent">
<div class="child">content that is not images...</div>
</div>
maybe add position absolute and it works, by setting top, right ,bottom,left to 0 with margin auto. you could as well use flex to center it or absolute with left 50% and transform -50% too.
body {
padding: 0;
margin: 0;
height: 100vh;
width: 100vh;
}
.child {
width: 90vw;
/* 90% of viewport vidth */
height: 50.625vw;
max-height: 90vh;
max-width: 160vh;
/* Adding this maybe min-width and min-height */
min-height: 90vh;
min-width: 160vh;
/* 16/9 * 90 = 160 */
background: #f7f7f7;
box-shadow: 0px 5px 30px 0px rgba(0, 0, 0, 0.18);
border-radius: 4px;
margin: auto;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parent Child</title>
</head>
<body> <!-- parent -->
<div class="child"></div>
</body>
</html>
Use flexbox and padding. Use media queries to determine if the min-aspect-ratio of the viewport. Then adjust accordingly.
html,
body {
height: 100%;
}
.parent {
height: 100%;
width: 100%;
border-style: solid;
border-width: 2px;
border-color: black;
/* to center */
display: flex;
align-items: center;
justify-content: center;
}
.child {
width: 100vw;
/* 16:9 aspect ratio = 9 / 16 = 0.5625 = padding-bottom*/
padding-bottom: 56.25%;
background: #A0522D;
}
#media (min-aspect-ratio: 16/9) {
.child {
height: 100vh;
/*16:9 aspect ratio = 9 / 16 = 177.77 = width*/
width: 177.77vh;
padding: 0;
margin: 0px;
background: red;
}
}
<div class="parent">
<!-- the viewbox will provide the desired aspect ratio -->
<div class="child">
content that is not images...
</div>
</div>
Here's a fiddle.
So as to make child's dimensions dependent on parent container set position:relative of the parent container.
Normally when we make an element position:absolute it is positioned relative to initial containing block(i.e the <body>) unless any other closest parent container is given a position other than static(which is by default).So, by giving relative position to parent container we positioned .child element relative to .parent which was earlier positioned relative to the document or body.
This will work for you
html,
body {
height: 100%;
}
.parent {
/* Parent's height and width are unknown,
it could be dynamic, e.g. parent is part of a flex layout. */
position:relative;
height: 80%;
width: 90%;
border-style: solid;
border-width: 2px;
border-color: black;
}
.child {
width: 90%;
/* 90% of viewport vidth */
height: 50.625%;
/* ratio = 9/16 * 90 = 50.625 */
max-height: 90%;
max-width: 160%;
/* 16/9 * 90 = 160 */
margin: auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #A0522D;
}
<div class="parent">
<div class="child">
content that is not images...
</div>
</div>

Stretch div all the way to the bottom without overflowing

Can anyone please help me to fill the width of parent container and stretch it all the way to bottom without overflowing. Here is jsfiddle
http://jsfiddle.net/vn50gka2/1/#&togetherjs=p1VVmrhCtA
.toolbar {
position: absolute;
height: 50px;
width: 100%;
top: 0;
left: 0;
background-color: green;
}
.layout {
height: 100%;
width: 400px;
margin: 60px auto
}
.container {
display: flex;
flex: 1;
flex-direction: column;
background-color: red;
}
Container is the div I'm trying to fix. The toolbar and layout can not be changed. And also just because I want to reuse the container I really wish to not use any calculation or any hardcoded height numbers.. I don't know if this is possible
$(document).ready(function(){
var toolbarheight = $(".toolbar").height();
var windowheight = $(this).height();
var divheight = windowheight - toolbarheight -10;
console.log(divheight)
$(".mydiv").height(divheight);
})
*{
margin:0;
paddin:0;}
.toolbar {
position: fixed;
height: 50px;
width: 100%;
top: 0;
left: 0;
background-color: green;
}
.layout {
margin-top: 60px;
height: 100%;
width: 100%;
}
.container {
width: 100%;
display: flex;
flex: 1;
flex-direction: column;
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="toolbar">
Toolbar
</div>
<div class="layout">
<div class="container mydiv">
<div>
stretch to bottom without overflow
</div>
</div>
</div>
well this full height for ur div using jquery!
added -10 is difference between your toolbar height and your layout top margin!

Relation of percent units in css to window size and/or resolution

Why do I have to set the height of div to 88% instead of 94% – (100% - (3% + 3%) padding) and more importantly, why the body (indicated by its background-color), shows up when I make the window smaller? I have two screens with different resolutions and it behaves the same on both of them. I'd like the space to be filled with div but without vertical scrollbar and it should not matter what the size of the window is...
html {
height: 100%;
}
body {
height: 100%;
margin: 0;
background-color: tomato;
}
<div style="padding: 3%; background-color: orange; height: 88%;">
</div>
The padding: 3% means 3% of the width is used for all 4 padding values - in most cases that would be more than 3% of the height - this div won't size properly from one screen to the next.
One solution might be to use box-sizing: border-box:
(setting box-sizing to border-box on an element means the padding and border are included in the dimensions, so we could safely use height: 100% with any padding value)
html {
height: 100%;
}
body {
height: 100%;
margin: 0;
background-color: tomato;
}
<div style="padding: 3%; background-color: orange; height: 100%; box-sizing: border-box;">
</div>
the background property is set to the whole inner content
the padding is assigned to the inner content properties,
so you must set margin property instead of padding :)
html {
height: 100%;
}
body {
position: fixed;
height: 100%;
width: 100%;
margin: 0;
background-color: tomato;
}
.container {
position: relative;
margin: 3%;
background-color: orange;
height: 88%;
}
<div class="container">
</div>

CSS calc works with vh but not %

I have the following:
body {
overflow-x: hidden;
overflow: hidden;
height: 100%;
min-height: 100%;
}
html {
height: 100%;
min-height: 100%;
}
.navbar-header {
background: green;
}
.sidebar {
background-color: #333;
border-right: 1px solid #000;
color: #fff;
position: fixed;
width: 178px;
height: 100%;
}
.content {
border: 1px solid #ddd;
height: calc(100% - 150px);
background-color: #ffffff;
overflow: scroll;
overflow-x: hidden;
margin-left: 178px;
}
.footer {
border: 1px solid #ddd;
min-height: 78px;
margin-top: 10px;
padding: 20px;
background-color: #f6f9fb;
position: fixed;
bottom: 0;
width: calc(100% - 178px);
left: 185px;
}
<div class="navbar-header">
header
</div>
<nav class="sidebar">
sidebar
</nav>
<div class="content">
content
</div>
<div class="footer">
footer
</div>
But in my project to make the calc work I changed % per vh to make it work, I am sorry I can't provide my local code, but I would like understand why my code just worked with vh instead of %.
height: calc(100vh - 150px);
height: -moz-calc(100vh - 150px);
height: -webkit-calc(100vh - 150px);
A percentage value for height (also in calc) only works if either the parent element has a fixed height, or if all parents, grandparents etc. up to the body (or up to an ancestor with fixed px width) have a percentage height (i.e. no auto height).
vhis the viewport height, the area where your elements render. Setting your elements to 100vh is basically telling them to occupy the whole height of your view port.
Using % in the other hand is telling your elements to use all the space they need. If your divis empty, then your viewport will be empty.

Fill a parent div while maintaining a ratio

I wish to have a div section that fills its div parent as much as possible while maintaining a ratio.
the render result would be like this :
What I do have so far :
html,
body {
height: 100%;
}
.parent {
/* Parent's height and width are unknown,
it could be dynamic, e.g. parent is part of a flex layout. */
height: 80%;
width: 90%;
border-style: solid;
border-width: 2px;
border-color: black;
}
.child {
width: 90vw;
/* 90% of viewport vidth */
height: 50.625vw;
/* ratio = 9/16 * 90 = 50.625 */
max-height: 90vh;
max-width: 160vh;
/* 16/9 * 90 = 160 */
margin: auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #A0522D;
}
<div class="parent">
<div class="child">
content that is not images...
</div>
</div>
This css behaves like the way I want BUT this is using the viewport instead of the parent div which is a problem in real conditions.
I am looking for a way to fill based on the parent div.
Using aspect-ratio: 16 / 9; overflow: hidden; (aspect-ratio MDN docs) should give you the exact result you're looking for without needing to use the padding trick. Make sure the parent is set to display: grid or else it may not scale properly.
The aspect-ratio CSS property is supported by all major browsers (caniuse.com) except Safari, though Safari plans to add support this year. This is the best/correct way to achieve this effect, without having to resort to JavaScript or any hack solutions.
Related questions and answers here on Stack Overflow:
https://stackoverflow.com/a/66786774/3824249 (very similar to my solution)
https://stackoverflow.com/a/20593342/3824249 (another CSS-only alternative, though hackier using position: absolute for element positioning)
Here is my solution in action:
html, body { height: 100%; }
.parent {
display: grid;
resize: both;
height: 50%;
width: 90%;
border: 2px solid #000;
overflow: hidden;
}
.child {
width: 100%;
max-height: 100%;
margin: auto;
aspect-ratio: 16 / 9;
overflow: hidden;
box-sizing: border-box;
position: relative;
background: #a0522d;
text-align: center;
font-size: 20px;
color: white;
/* using the below to center the text, optional */
display: flex;
align-items: center;
justify-content: center;
}
<div style="padding: 5px 10px; margin-bottom: 10px; background: #f00; color: #fff; text-align: center; z-index: 1;">Resize the block below using the resize controls to see this in action.</div>
<div class="parent">
<div class="child">content that is not images...</div>
</div>
maybe add position absolute and it works, by setting top, right ,bottom,left to 0 with margin auto. you could as well use flex to center it or absolute with left 50% and transform -50% too.
body {
padding: 0;
margin: 0;
height: 100vh;
width: 100vh;
}
.child {
width: 90vw;
/* 90% of viewport vidth */
height: 50.625vw;
max-height: 90vh;
max-width: 160vh;
/* Adding this maybe min-width and min-height */
min-height: 90vh;
min-width: 160vh;
/* 16/9 * 90 = 160 */
background: #f7f7f7;
box-shadow: 0px 5px 30px 0px rgba(0, 0, 0, 0.18);
border-radius: 4px;
margin: auto;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parent Child</title>
</head>
<body> <!-- parent -->
<div class="child"></div>
</body>
</html>
Use flexbox and padding. Use media queries to determine if the min-aspect-ratio of the viewport. Then adjust accordingly.
html,
body {
height: 100%;
}
.parent {
height: 100%;
width: 100%;
border-style: solid;
border-width: 2px;
border-color: black;
/* to center */
display: flex;
align-items: center;
justify-content: center;
}
.child {
width: 100vw;
/* 16:9 aspect ratio = 9 / 16 = 0.5625 = padding-bottom*/
padding-bottom: 56.25%;
background: #A0522D;
}
#media (min-aspect-ratio: 16/9) {
.child {
height: 100vh;
/*16:9 aspect ratio = 9 / 16 = 177.77 = width*/
width: 177.77vh;
padding: 0;
margin: 0px;
background: red;
}
}
<div class="parent">
<!-- the viewbox will provide the desired aspect ratio -->
<div class="child">
content that is not images...
</div>
</div>
Here's a fiddle.
So as to make child's dimensions dependent on parent container set position:relative of the parent container.
Normally when we make an element position:absolute it is positioned relative to initial containing block(i.e the <body>) unless any other closest parent container is given a position other than static(which is by default).So, by giving relative position to parent container we positioned .child element relative to .parent which was earlier positioned relative to the document or body.
This will work for you
html,
body {
height: 100%;
}
.parent {
/* Parent's height and width are unknown,
it could be dynamic, e.g. parent is part of a flex layout. */
position:relative;
height: 80%;
width: 90%;
border-style: solid;
border-width: 2px;
border-color: black;
}
.child {
width: 90%;
/* 90% of viewport vidth */
height: 50.625%;
/* ratio = 9/16 * 90 = 50.625 */
max-height: 90%;
max-width: 160%;
/* 16/9 * 90 = 160 */
margin: auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #A0522D;
}
<div class="parent">
<div class="child">
content that is not images...
</div>
</div>

Resources