Sizing width of an element as a percentage of its height or vice versa in a fluid design? [duplicate] - css

This question already has answers here:
Maintain the aspect ratio of a div with CSS
(37 answers)
Closed 6 years ago.
Im making a responsive design, which has to keep the proportions of its elements (height to width) no matter what the screen size is, so i don't know the size in pixels for any of the elements and i can work only in %.
I can set either the width or the height as a % of the browser size, but the i have no idea how to set the other property value.
That using only CSS, making JS calculate the values is not an option.

I came across this problem last year and found an obscure solution after some tedious researching. Unfortunately it involves wrapper DIVs. The structure is simple - you have a parent DIV which contains the contents container and another DIV that sets the proportions. You set the margin-top of the 'proportion' DIV as percent of the width of the parent... Example:
#parent {
position: relative;
}
.proportions {
margin-top: 75%; /* makes parent height to 75% of it's width (4:3) */
}
.container { /* contents container with 100% width and height of #parent */
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
}
http://jsfiddle.net/twpTU/

Use CSS calc() https://developer.mozilla.org/en-US/docs/Web/CSS/calc
It has pretty good adoption for modern browsers http://caniuse.com/calc as a fall back use CSS media queries and have min widths and heights to fall back on.
Obviously you could always just calculate both percentages in advance.
Div1 needs a height of 60% and the width needs to be 1/4th the height. 60% * .25 = width: 15%
div {
position: absolute;
border: 2px solid black
}
#div1 {
width: 40%;
height: calc(40% * 1.2);
}
#div2 {
width: calc(90% / 4);
height: 90%;
}
http://jsfiddle.net/pU4QA/

First, I know that the OP has clearly mentioned that he is not looking for any Javascript approach, however I think it might be useful for couple of people who are still open to use Javascript as well.
Here is the answer; To set the size for both width and height in the same percentage, you can set the width of the parent element to width: 100%; via CSS, then with Javascript you set the height of the parent to the size of width. Then you would have a square-shaped element as the parent.
Now you can apply both width: 10%; height: 10%; to the child elements. Then only things you need to do in order to keep it responsive is to listen for the window resize event and then you apply the height again only to the parent, and all children will be updated.
http://jsfiddle.net/sDzHb/
Something like this will keep it responsive to the browser size:
$(window)
.resize(function() {
var w = $(document).width();
$('.parent').height(w);
})
.trigger( 'resize' );

Related

CSS unit vw includes width of a physical scrollbar, causing problem for height in vw

For example: I have an element which takes the full width of the viewport. I want its min-height to be half of its width, to get a ratio of 1:2.
On a 1600px wide desktop monitor the element's width would be about 1583px (monitor width minus scrollbar width), but its min-height would be 800px, because vw doesn't substract the scrollbar's width. So that would not be a 1:2 ratio.
An easy solution would be padding-top: 50%;, but if there's text within the element, that doesn't work. Alternative: a left floating pseudo element ::before with padding-top: 50%; would create the desired min-height in the ratio of 1:2, but that would be kinda hacky.
Am I missing something? Is there any clean way?
First thing you should do is include the following so the default padding and margins given by the web browser are removed:
* {
padding: 0;
margin: 0;
}
For the element you want to have 100vw and half height:
.half_height{
width: 100vw;
aspect-ratio: 2/1;
}
.container {
Width: calc(100vw - calc(100vw -100%));
Height: 50vh;
}

Prevent 100vw from creating horizontal scroll

If an element is set to width: 100vw; and there is a vertical scrollbar the width of the element will be equal to the viewport plus the width of the scrollbar.
Is it possible to prevent this?
Is it possible to prevent this without disabling horizontal scrolling on the entire page? Aside from changing my css/markup to make the element 100% of the body width I can't think of anything.
Tested in Chrome Version 43.0.2357.81 m & FF 36.0.1 & Opera 20.0.1387.91 on Windows 8.1
Here is the code as requested:
Example
html
<div class="parent">
<div class="box"></div>
</div>
<div class="tall"></div>
css
body { margin: 0; }
html { box-sizing: border-box; }
*, *::before, *::after {
box-sizing: inherit;
position: relative;
}
.parent {
background: rgba(0, 0, 0, .4);
height: 100px;
width: 5rem;
margin-bottom: 25px;
}
.box {
position: absolute;
top: 0;
left: 0;
background: rgba(0, 0, 0, .4);
height: 50px;
width: 100vw;
}
.tall {
height: 100rem;
}
Basically the answer is no, if you have a vertical scrollbar there is no way to make 100vw equal the width of the visible viewport. Here are the solutions that I have found for this issue.
warning: I have not tested these solutions for browser support
tl;dr
If you need an element to be 100% width of the visible viewport(viewport minus scrollbar) you will need to set it to 100% of the body. You can't do it with vw units if there is a vertical scrollbar.
1. Set all ancestor elements to position static
If you make sure that all of .box's ancestors are set to position: static; then set .box to width: 100%; so it will be 100% of the body's width. This is not always possible though. Sometimes you need one of the ancestors to be position: absolute; or position: relative;.
Example
2. Move the element outside of non-static ancestors
If you can't set the ancestor elements to position: static; you will need to move .box outside of them. This will allow you to set the element to 100% of the body width.
Example
3. Remove Vertical Scrollbar
If you don't need vertical scrolling you can just remove the vertical scrollbar by setting the <html> element to overflow-y: hidden;.
Example
4. Remove Horizontal Scrollbar
This does not fix the problem, but may be suitable for some situations.
Setting the <html> element to overflow-y: scroll; overflow-x: hidden; will prevent the horizontal scrollbar from appearing, but the 100vw element will still overflow.
Example
Viewport-Percentage Lengths Spec
The viewport-percentage lengths are relative to the size of the
initial containing block. When the height or width of the initial
containing block is changed, they are scaled accordingly. However,
when the value of overflow on the root element is auto, any scroll
bars are assumed not to exist. Note that the initial containing
block’s size is affected by the presence of scrollbars on the
viewport.
It appears that there is a bug because vw units should only include the scrollbar width when overflow is set to auto on the root element. But I've tried setting the root element to overflow: scroll; and it did not change.
Example
This is a more full-fledged approach to the bug since it still exists in modern browsers. Setting overflow-x: hidden can cause problems or be undesirable in many situations.
A full example is available here: http://codepen.io/bassplayer7/pen/egZKpm
My approach was to determine the width of the scroll bar and use calc() to reduce the 100vw by the amount of the scroll bar. This is a little more complicated because in my case, I was pulling the width of the content out from a box that had a defined with so I needed to declare the margin as well.
A few notes regarding the code below: first, I noticed that 20px seems to be a rather broad magic number for the scroll bars. I use a SCSS variable (it doesn't have to be SCSS) and code outside of #supports as a fallback.
Also, this does not guarantee that there will never be scroll bars. Since it requires Javascript, users that don't have that enabled will see horizontal scroll bars. You could work around that by setting overflow-x: hidden and then adding a class to override it when Javascript runs.
Full SCSS Code:
$scroll-bar: 20px;
:root {
--scroll-bar: 8px;
}
.screen-width {
width: 100vw;
margin: 0 calc(-50vw + 50%);
.has-scrollbar & {
width: calc(100vw - #{$scroll-bar});
margin: 0 calc(-50vw + 50% + #{$scroll-bar / 2});
}
#supports (color: var(--scroll-bar)) {
.has-scrollbar & {
width: calc(100vw - var(--scroll-bar));
margin: 0 calc(-50vw + 50% + (var(--scroll-bar) / 2));
}
}
}
Convert the above to plain CSS just by removing #{$scroll-bar} references and replacing with the px value
Then this Javascript will set the CSS Custom Property:
function handleWindow() {
var body = document.querySelector('body');
if (window.innerWidth > body.clientWidth + 5) {
body.classList.add('has-scrollbar');
body.setAttribute('style', '--scroll-bar: ' + (window.innerWidth - body.clientWidth) + 'px');
} else {
body.classList.remove('has-scrollbar');
}
}
handleWindow();
As a side note, Mac users can test this by going to System Preferences -> General -> Show Scroll Bars = Always
Using max-width attribute with width:100vw and it solved my problem.
Here's what i used.
.full{
height: 100vh;
width: 100vw;
max-width: 100%;
}
Basically what it does is it fixes the max width to the viewport so the horizontal scroll gets eliminated.
More # https://www.w3schools.com/cssref/pr_dim_max-width.asp
The max-width property defines the maximum width of an element.
If the content is larger than the maximum width, it will automatically
change the height of the element.
If the content is smaller than the maximum width, the max-width
property has no effect.
Paddings and borders can interfere. So can margin. Use box-sizing to calculate width including these attributes. And maybe remove margin (if any) from the width.
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
margin: 0; /* interferes with 100vw */
}
.parent {
width: 100vw;
max-width: 100%; /* see below */
}
.box {
width: 100%; /* For those good old-fashioned browsers with no vw or calc() support */
width: -webkit-calc(100vw - [your horizontal margin, if any]);
width: -moz-calc(100vw - [your horizontal margin, if any]);
width: calc(100vw - [your horizontal margin, if any]);
max-width: 100%
}
It seems you have to add max-width: 100%; if there is a reflow which is causing the scrollbar to appear after the initial viewport width calculation. This does not seem to happen in browsers without an interfering scrollbar (iOS, OS X, IE 11 Metro), but can affect all other browsers.
I was also struggling with this, and I also thought of CSS variables as the solution. CSS variables aren't supported in IE11 though so I tried something else:
I fixed it by calculating the width of the scroll bar: subtracting the width of the body (not including scroll bar) from the width of the window (including the scroll bar). I use this to add it to the 100% of the body, see plusScrollBar variable.
JS:
// calculate width of scrollbar and add it as inline-style to the body
var checkScrollBars = function() {
var b = $('body');
var normalw = 0;
var scrollw = 0;
normalw = window.innerWidth;
scrollw = normalw - b.width();
var plusScrollBar = 'calc(' + '100% + ' + scrollw + 'px)'
document.querySelector('body').style.minWidth = plusScrollBar;
}();
CSS:
html{
overflow-x: hidden;
}
Why I like this: it takes in consideration that not all scrollbars are the same width or considered as conserved space at all. :)
I had the same issue and it was fixed when I added:
html, body { overflow-y: auto; }
I added the comment:
/* this fixes a 100vw issue, removing the horizontal scrollbar when it's unneeded */
It works at least with Firefox, Chrome, Opera and Internet Explorer 11 (I used browserling for IE).
I don't know why it works and if it works in all cases, though.
EDIT:
The horizontal scrollbar disappeared, but I noticed it's still horizontally scrollable using the cursor keys and touch screens...
overflow-x: clip; does the job.
I had the same problem. When I changed the vw units to percentage, horizontal scrollbar disappeared.
If you're working in a framework (ASP.NET for example) where there's possibly a parent element wrapping around the html, then setting the html's max-width to 100% will solve the problem without using the "band-aid" solution overflow-x: hidden.
html {
max-width: 100%;
}
An element with width: 100vw only causes horizontal scrollbars when one of it's parents has a horizontal padding. Otherwise it should fit in well.
Check this fiddle: http://jsfiddle.net/1jh1cybc/ The .parent2 has a padding, which causes the inner .box to break out of it's parent width.
Edit:
In your case I guess your body has a margin. Check this fiddle out with you code and try to remove the body css rule: http://jsfiddle.net/1jh1cybc/1/
Here's what I do:
div.screenwidth{
width:100%; /* fallback for browsers that don't understand vw or calc */
width: calc(100vw - 17px); /* -17 because vw is calculated without the scrollbar being considered & 17px is width of scrollbars */
position:relative; /* use this if the parent div isn't flush left */
right: calc((100vw - 17px - 100% )/2);
}
I fixed this on my site by adding body{overflow-x:hidden} to the page in question.
Works on your example too.
Here's my solution to the problem of 100vw adding a horizontal scroll:
html {
width: calc(100% + calc(100vw - 100%));
overflow-x: hidden;
}
.box {
width: calc(100% + calc(100vw - 100%));
}

resize both width and height of a div while window resize using css

is there any way to resize both width and height of a div in correlation with the browser resize using css? I have already achieved width resize but can't find how to resize the height in correlation.
Use viewport-percentage lengths:
5.1.2. Viewport-percentage lengths: the ‘vw’, ‘vh’, ‘vmin’, ‘vmax’ units
The viewport-percentage lengths are relative to the size of the initial containing block. When the height or width of the initial containing block is changed, they are scaled accordingly.
If you wanted to create a square that maintains an aspect ratio, you could use:
Example Here
.maintain-aspect-ratio {
width: 25%;
height: 25vw;
}
These lengths are supported in most modern browsers - support can be found here.
If you want an alternative with more browser support, you could always use the padding-top trick to maintain the aspect ratio.
Alternative Example
.maintain-aspect-ratio {
position: relative;
width: 25%;
background: red;
}
.maintain-aspect-ratio:before {
content: '';
display: block;
padding-top: 100%; /* 1:1 ratio */
}

Inconsistent percentage margin calculation for absolutely positioned item

I have a situation where I have a 'bottom' content div within a general container. This div should stay at the bottom (with absolute positioning), but have a percentage gap to base of the container. The percentage should be relative to the width of the container.
We can't use 'bottom:5%' because as the position props define this is relative to the height. How about margin? Yes! It works in Chrome .. and Firefox. Ah, but not in Safari. It seems Chrome and Safari calculate it based on the container width and Safari on the container height.
See this fiddle in Chrome and Safari and you'll see the inconsistency. CSS styles:
.container {
background: #990000;
width: 345px;
height: 200px;
position: relative;
}
.bottom {
background: #000;
width: 100%;
height: 40px;
position: absolute;
bottom: 0;
margin-bottom: 5%;
}
Anybody know where the bug lies here - with Safari? Chrome/Firefox? The spec?
A quick check shows that padding might work consistently, but it's not ideal for those who would want to use margin (i.e. when a background comes into play).
The problem lies with Safari. W3C Standards state that margins that are defined using percentages should be calculated with respect to the width of the containing block (not the height).
Check it: http://www.w3.org/TR/2011/REC-CSS2-20110607/box.html#margin-properties
So basically, you're stuck with the bug. However, I'd suggest using some JS to target Safari, get the width of the container and apply a margin as a percentage of that width.
For example:
var width = $('.container').width(); // get the width of div.container
var bottomMargin = width * 0.05; // multiply the width by 0.05 to get 5% of the container width
// look to see if the browser is Safari
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
$('.bottom').css('margin-bottom', bottomMargin+'px'); // apply our margin to div.bottom
}
else {
};;
I had some trouble implementing this in JS Fiddle so have created a page here.
Hope that helps!
I had the same problem with Android Browsers and was able to solve it by putting the margin on a child element.
.bottom {
position: absolute;
bottom: 0;
width: 100%;
height: 40px;
}
.bottom-child {
height:100%;
margin-bottom: 5%;
background: #000;
}
The key is not to put the margin on the absolutely positioned element.

Why doesn't height work in CSS when I use percentages?

So, I am trying to set an image to be 100% of the height and width of the html element (really the browser window is what I'm going for). I have the CSS set as
html{
width: 100%;
height: 100%;
}
img{
width: 100%;
height: 100%;
z-index: 0%;
}
And the width behaves right, but the height does not change. I tried setting it to height: 2% and it stayed the same height. I don't want to use px to set the height because I want this to work on mobile devices, but HEIGHT, Y U NO WORK?
You also need to set height: 100% on body.
Going with your exact example, you could do:
html, body, img {
width: 100%;
height: 100%;
}
However, it looks like you're possibly trying to get a fullscreen background image (because you used z-index - by the way z-index does not use %, just a plain number).
In that case, you should instead use one of the methods from here:
http://css-tricks.com/perfect-full-page-background-image/
That is because the image element is not the direct child of the html element. You have to specify the height for the body element also, and any other element containing the image element.

Resources