I have a section containing a picture tag. The picture tag is done with a srcSet of images for responsive design and using multiple pictures without downloading the pictures that are not being in use.
We need to center the text on said container, so I have a div with display: grid inside. This is absolute positioned and using height: 100% and width: 100%.
Using align-self: center on the child works perfectly on Chrome and Firefox, but to not my surprise, Safari decided to not cooperate. I discovered that if I set the section (or the grid) to a set height (ie: height: 5000px) it actually aligns itself to the center of the div, which makes me think Safari does not want to align-self: center and a unkown sized div.
There are multiple ways to solve this, but we a using our own library that is using grid, that's why I would like to solve it using align-self: center as we can just pass the prop to the React Component.
Do you guys know a workaround?
Here I left a fiddle. If you visit it on Chrome or Firefox, it works, but if you visit it on Safari, it does not. I also commented a line where I set a specific height to prove that it works with that on Safari.
Thanks!
.container {
position: relative;
}
.grid {
position: absolute;
height: 100%;
/* height: 1000px; */
width: 100%;
top: 0;
background-color: rgba(255, 255, 255, 0.75);
display: grid;
grid-template-columns: repeat(12, 1fr);
}
.cell {
grid-column: 1 / span 1;
align-self: center;
}
img {
width: 100%
}
<div class='container'>
<img src='https://cdn.contexttravel.com/image/upload/c_fill,q_60,w_2600/v1553227874/production/city/hero_image_27_1553227874.jpg' />
<div class='grid'>
<div class='cell'>
Hello
</div>
</div>
</div>
The problem is that Safari does not recognize a percentage height unless the parent has a defined height. That's old school CSS. Other browsers have evolved past that requirement, now accepting other references for percentage heights.
So, if you're going to use a percentage height on .grid, the parent needs a fixed height for it to work on Safari. Or, set percentage heights on the ancestors all the way up to the root element.
/* NEW */
html, body, .container, img {
height: 100%;
}
.container {
position: relative;
}
.grid {
position: absolute;
height: 100%;
/* height: 1000px; */
width: 100%;
top: 0;
background-color: rgba(255, 255, 255, 0.75);
display: grid;
grid-template-columns: repeat(12, 1fr);
}
.cell {
grid-column: 1 / span 1;
align-self: center;
}
img {
width: 100%
}
<div class='container'>
<img src='https://cdn.contexttravel.com/image/upload/c_fill,q_60,w_2600/v1553227874/production/city/hero_image_27_1553227874.jpg' />
<div class='grid'>
<div class='cell'>
Hello
</div>
</div>
</div>
More details:
Chrome / Safari not filling 100% height of flex parent
Working with the CSS height property and percentage values
Related
I want to show a canvas, possibly larger than the viewport.
The layout is common: navigation on the left, canvas, controls etc... on the right.
When the canvas is large, I want scrollbars around the canvas where needed. That's what the overflow=auto on the canvas div wrapper is for.
html {
height: 100%;
margin: 0;
}
body {
height: 100%;
margin: 0;
background: yellow;
}
.container {
display: grid;
grid-template-columns: fit-content(200px) 1fr;
height: 100%;
overflow: hidden;
background-color: #b30a96;
}
.navigation {
background-color: darkkhaki;
}
.right {
display: flex;
height: 100%;
flex-direction: column;
background-color: red;
}
.canvas-wrapper {
overflow: auto;
background-color: #DE8D00;
}
#canvas {
display: block;
background-color: #0bb314;
}
.rest {
background-color: aquamarine;
}
<div class="container">
<div class="navigation">
navigation
</div>
<div class="right">
<div class="canvas-wrapper">
<canvas id="canvas" width="2500" height="1800"></canvas>
</div>
<div class="rest">
rest...
</div>
</div>
</div>
With only the flexbox, I see scrollbars just fine. As soon as I embed the flexbox in the grid, no more scrollbars.
A number of related posts shed some light but got me no closer to solving the problem.
I tried various permutations of width, height, max-width, max-height, min-width, min-height on the wrapper, the flexbox, the grid, with no satisfying result.
Before resorting to JavaScript and forcing the size of the div wrapper, I would like to know whether this can be solved in pure CSS.
after setting
grid-template-columns: fit-content(200px) minmax(0,1fr);
in class .container and adding
min-height: 0;
to class .right, the scrollbars are finally visible around the canvas.
As I understand it, the problem was that the div wrapper didn't have a size, and setting a minimum of zero somehow helps.
I have an image in a flex container (let's call it child).
The container itself has flex: 1 because it also belongs to a flex parent with flex-direction: column and child behaves as expected (takes 100% height of parent) before introducing the image situation.
On adding an image to child, the image extends child to accommodate its height, effectively child pushing parent.
Using object-fit: cover on the image could not help in this situation!
Looks like this is normal behaviour in Chrome, Firefox, and Safari.
Here's the weird part tho: adding a height property to child fixes the issue on Chrome and Safari but not Firefox. This height's value doesn't matter much as long as it's not greater than the computed height of child - child is contained to 100% height of parent together with the image - even if the height value of child is set to 1px. Surprisingly height: 100% on child doesn't produce the same behaviour.
Any ideas around this will be much appreciated. My goal is to make the image always take up 100% of child - regardless of the width of the viewport.
Here's some code for illustration
#parent {
height: 100vh;
display: flex;
flex-direction: column;
}
#child {
flex: 1;
height: 1px;
/* the weird hack for chrome and safari - ff ignores it */
}
#child img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div id="parent">
<div id="child">
<img src="https://i.picsum.photos/id/1005/400/1000.jpg" />
</div>
</div>
I also added some images for more context. These are crops of the entire page. The first image is from a smaller viewport (width shared with devtools) and the second from the entire page (devtools hidden). The second image shows that the image has extended child to eventually push the container with a green background beyond the fold - causing a scrollbar to appear.
child is the image wrapper, and parent is the component with a white background and some padding. You realize parent has a sibling (green background) and their parent (container with a green border and border radius) has a display: grid and grid-template-rows: 1fr auto;.
It should also be noted that the most top-level element has a height of 100vh and all its children are rendered within that. Essentially there should not be a scrollbar.
I hope you get the idea.
Is this what you are looking to do?
You can also see it here: https://codepen.io/teanbiscuits/pen/GRJmPgo
#parent {
height: 100vh;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr auto;
border:2px solid green;
border-radius:20px;
overflow:hidden;
}
#child {
position:relative;
}
#child img {
position:absolute;
width: 100%;
height: 100%;
object-fit: contain;
}
#text {
background-color:green;
}
<div id="parent">
<div id="child">
<img src="https://i.picsum.photos/id/1005/400/1000.jpg" />
</div>
<div id="text">
<h2>some title here</h2>
<p>Some description here</p>
</div>
</div>
Please try this. Just select preferred object-fit.
html, body{
height: 100%;
margin: 0;
padding: 0;
}
#parent {
height: calc(100vh - 20px);
display: flex;
padding: 10px;
flex-direction: column;
}
#child {
flex: 1;
text-align: center;
overflow: hidden;
}
#child img {
width: 100%;
height: 100%;
object-fit: contain;
}
<div id="parent">
<div id="child">
<img src="https://i.picsum.photos/id/1005/400/1000.jpg" />
</div>
<div id="text">
<h2>Some title here</h2>
<p>Some description here</p>
</div>
</div>
I'm trying to achieve a website design which basically has two parts. The top-part, where the menu of the site is and the content-part, with the information.
.wrap {
position: absolute;
width: 100%;
height: 100%;
}
.box {
display: flex;
flex-direction: column;
height: 100%;
}
.top {
flex: 0 1 auto;
}
.content {
flex: 1;
position: relative;
height: 100%;
}
<div class="wrap">
<div class="box">
<div class="top"></div>
<div class="content"></div>
</div>
</div>
The menu-div should be as big as needed for the menu-content to be displayed and the content-div should fill the rest of the site. Both together should fill 100% in width and 100% in height.
So, this construct works fine on desktop PCs, however - on mobile browsers for Chrome, Firefox, and Safari, the site's height gets extended by the menu's height.
The content is at 100% plus the menu. Can someone please explain to me what I'm doing wrong here?
The Basics
In order to give elements percentage heights, their parents need a defined height. What can be confusing, is that these parents include <html> and <body> Consider this example:
html,body {
height: 100%;
margin: 0;
}
div {
height: 100%;
background: lightgreen;
}
<div></div>
Here the <html>, <body> and <div> elements have both been given height: 100%. This trickles down to each child element:
<html> is 100% of the browser viewport
<body> is 100% of its <html> parent
<div> is 100% of its <body> parent
We can simplify this and give the <body> element 100% height using a viewport height unit. This reduces complexity as we no longer need to worry about <html>. Consider this example:
body {
height: 100vh;
margin: 0;
}
div {
height: 100%;
background: lightgreen;
}
<div></div>
Here the <body> takes up 100% of the viewport height and its children can be given percentage heights. We don't need to define a height for <html>.
What You Want to Achieve
With this in mind, we can strip your example down to the basics. Here is exactly what you have described using as few elements as needed:
The <body> can be the flex container. It gets:
display: flex and flex-direction: column
margin: 0 to remove the default body margin.
height: 100vh to stretch it to 100% of the viewport height.
The <div class="top"> is your header that will contain your menu. We don't need to give it a flex property as it will shrink to fit its contents with the initial flex values it is given.
The <div class="content"> is given flex: 1 to grow and fill the rest of the available space.
body {
display: flex;
flex-direction: column;
height: 100vh;
margin: 0;
}
.top {
background: pink;
}
.content {
flex: 1;
background: lightgreen;
}
<div class="top">Menu Content<br>Menu Content<br>Menu Content</div>
<div class="content"></div>
I'm facing a strange issue that might have link with flexbox misbehaving with max-height, but so far I didn't find any pure css solution.
I made a plunker to summarize the problem. If you resize your window to reduce its height, at some point you should have a scrollbar in the first block, but if you get back to a higher height, even if there is enough space, the scrollbar won't disappear unless you put your mouse over it (which feels very bugy) : https://plnkr.co/edit/VsJ7Aw8qZdSM1iJeL7Bj?p=preview
I have a main container (in flex) containing 2 blocks (also in flex).
The main container has its height set to 100%, allowing it to resize itself following the window size.
Both children have a fixed content and an overflow-y set to auto.
The first child has a max-height in % to let more height to the second child.
The issue seems to come from this max-height rule. If you remove it, then there's no problem, but I need this max-height...
I don't want to use something like:
.max { flex: 1 1 auto; }
.all { flex: 3 1 auto; }
because it would make my first block higher than its content depending on the window size. I want the first block to have at most its content height.
So my question is: Is it an implementation issue in many browsers (maybe all, but I only tested it in Chrome, IE10 and IE11), or is something wrong in my logic ?
Thank you.
UPDATE: I used a fixed height for my content in this example, but in my project it's a list of n elements in it. So I can't really set my max-height with px value.
UPDATE2: I can't use vh in .max max-height property because it takes 100vh as 100% of viewport height (basically your browser window height). But in my context, .main is already in other containers. Those containers have already their heights defined and are smaller than my window height.
/* Styles go here */
html {
height: 100%;
}
body {
height: calc(100% - 16px);
}
.main {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
.max,
.all {
display: flex;
flex-direction: column;
width: 100%;
overflow-y: auto;
}
.max {
flex: 0 1 auto;
min-height: 103px;
max-height: 40%;
background-color: green;
}
.all {
flex: 2 1 auto;
min-height: 235px;
background-color: blue;
}
.content {
flex: 0 0 auto;
box-sizing: border-box;
height: 200px;
margin: 5px;
border: 1px dashed black;
background-color: white;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="main">
<div class="max">
<div class="content"></div>
</div>
<div class="all">
<div class="content"></div>
</div>
</div>
</body>
</html>
It is a bug, in Chrome, a test in FF and Edge, it works fine.
Since you use full viewport height, change the max-height: 40%; to max-height: 40vh;.
Another way, as in below sample, is to change the 100% in height: 100% to 100vh.
I guess this works better because viewport units like vh is a fixed unit, which percent is not.
Plnkr demo: https://plnkr.co/edit/66W4a2lOI58XLudCmkw9?p=preview
html {
height: 100vh;
}
body {
height: calc(100vh - 16px);
}
.main {
display: flex;
flex-direction: column;
height: 100vh;
width: 100%;
}
.max,
.all {
display: flex;
flex-direction: column;
width: 100%;
overflow-y: auto;
}
.max {
flex: 0 1 auto;
min-height: 103px;
max-height: 40%;
background-color: green;
}
.all {
flex: 1 1 auto;
min-height: 235px;
background-color: blue;
}
.content {
flex: 0 0 auto;
box-sizing: border-box;
height: 200px;
margin: 5px;
border: 1px dashed black;
background-color: white;
}
<div class="main">
<div class="max">
<div class="content"></div>
</div>
<div class="all">
<div class="content"></div>
</div>
</div>
Yes it feels buggy. If you increase the height of the window the height of the first box does not get updated unless:
you decrease the height again
"put your mouse over it" (did not quite get your meaning here)
IMHO this is a browser bug.
If you set flex-grow to anything greater 0 for the first box, the height gets updated correctly, if you increase the height of the window (as you would expect) But using flex-grow isn't an option as the box could potentially grow bigger than its content.
Rather than using max-height:40% you should use the exact same height as you use for .content and use flex-grow: 1 as well to circumvent the "browser bug"
How can I achieve the following structure without using tables or JavaScript? The white borders represent edges of divs and aren't relevant to the question.
The size of the area in the middle is going to vary, but it will have exact pixel values and the whole structure should scale according to those values. To simplify it, I'd need a way to set "100% - n px" width to the top-middle and bottom-middle divs.
I'd appreciate a clean cross-browser solution, but in case it's not possible, CSS hacks will do.
Here's a bonus. Another structure I've been struggling with and end up using tables or JavaScript. It's slightly different, but introduces new problems. I've been mainly using it in jQuery-based windowing system, but I'd like to keep the layout out of the script and only control the size of one element (the middle one).
New way I've just stumbled upon: css calc():
.calculated-width {
width: -webkit-calc(100% - 100px);
width: -moz-calc(100% - 100px);
width: calc(100% - 100px);
}
Source: css width 100% minus 100px
You can use nested elements and padding to get a left and right edge on the toolbar. The default width of a div element is auto, which means that it uses the available width. You can then add padding to the element and it still keeps within the available width.
Here is an example that you can use for putting images as left and right rounded corners, and a center image that repeats between them.
The HTML:
<div class="Header">
<div>
<div>This is the dynamic center area</div>
</div>
</div>
The CSS:
.Header {
background: url(left.gif) no-repeat;
padding-left: 30px;
}
.Header div {
background: url(right.gif) top right no-repeat;
padding-right: 30px;
}
.Header div div {
background: url(center.gif) repeat-x;
padding: 0;
height: 30px;
}
While Guffa's answer works in many situations, in some cases you may not want the left and/or right pieces of padding to be the parent of the center div. In these cases, you can use a block formatting context on the center and float the padding divs left and right. Here's the code
The HTML:
<div class="container">
<div class="left"></div>
<div class="right"></div>
<div class="center"></div>
</div>
The CSS:
.container {
width: 100px;
height: 20px;
}
.left, .right {
width: 20px;
height: 100%;
float: left;
background: black;
}
.right {
float: right;
}
.center {
overflow: auto;
height: 100%;
background: blue;
}
I feel that this element hierarchy is more natural when compared to nested nested divs, and better represents what's on the page. Because of this, borders, padding, and margin can be applied normally to all elements (ie: this 'naturality' goes beyond style and has ramifications).
Note that this only works on divs and other elements that share its 'fill 100% of the width by default' property. Inputs, tables, and possibly others will require you to wrap them in a container div and add a little more css to restore this quality. If you're unlucky enough to be in that situation, contact me and I'll dig up the css.
jsfiddle here: jsfiddle.net/RgdeQ
Enjoy!
You can make use of Flexbox layout. You need to set flex: 1 on the element that needs to have dynamic width or height for flex-direction: row and column respectively.
Dynamic width:
HTML
<div class="container">
<div class="fixed-width">
1
</div>
<div class="flexible-width">
2
</div>
<div class="fixed-width">
3
</div>
</div>
CSS
.container {
display: flex;
}
.fixed-width {
width: 200px; /* Fixed width or flex-basis: 200px */
}
.flexible-width {
flex: 1; /* Stretch to occupy remaining width i.e. flex-grow: 1 and flex-shrink: 1*/
}
Output:
.container {
display: flex;
width: 100%;
color: #fff;
font-family: Roboto;
}
.fixed-width {
background: #9BCB3C;
width: 200px; /* Fixed width */
text-align: center;
}
.flexible-width {
background: #88BEF5;
flex: 1; /* Stretch to occupy remaining width */
text-align: center;
}
<div class="container">
<div class="fixed-width">
1
</div>
<div class="flexible-width">
2
</div>
<div class="fixed-width">
3
</div>
</div>
Dynamic height:
HTML
<div class="container">
<div class="fixed-height">
1
</div>
<div class="flexible-height">
2
</div>
<div class="fixed-height">
3
</div>
</div>
CSS
.container {
display: flex;
}
.fixed-height {
height: 200px; /* Fixed height or flex-basis: 200px */
}
.flexible-height {
flex: 1; /* Stretch to occupy remaining height i.e. flex-grow: 1 and flex-shrink: 1*/
}
Output:
.container {
display: flex;
flex-direction: column;
height: 100vh;
color: #fff;
font-family: Roboto;
}
.fixed-height {
background: #9BCB3C;
height: 50px; /* Fixed height or flex-basis: 100px */
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
}
.flexible-height {
background: #88BEF5;
flex: 1; /* Stretch to occupy remaining width */
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
}
<div class="container">
<div class="fixed-height">
1
</div>
<div class="flexible-height">
2
</div>
<div class="fixed-height">
3
</div>
</div>
The usual way to do it is as outlined by Guffa, nested elements. It's a bit sad having to add extra markup to get the hooks you need for this, but in practice a wrapper div here or there isn't going to hurt anyone.
If you must do it without extra elements (eg. when you don't have control of the page markup), you can use box-sizing, which has pretty decent but not complete or simple browser support. Likely more fun than having to rely on scripting though.
Maybe I'm being dumb, but isn't table the obvious solution here?
<div class="parent">
<div class="fixed">
<div class="stretchToFit">
</div>
.parent{ display: table; width 100%; }
.fixed { display: table-cell; width: 150px; }
.stretchToFit{ display: table-cell; vertical-align: top}
Another way that I've figured out in chrome is even simpler, but man is it a hack!
.fixed{
float: left
}
.stretchToFit{
display: table-cell;
width: 1%;
}
This alone should fill the rest of the line horizontally, as table-cells do. However, you get some strange issues with it going over 100% of its parent, setting the width to a percent value fixes it though.
We can achieve this using flex-box very easily.
If we have three elements like Header, MiddleContainer and Footer. And we want to give some fixed height to Header and Footer. then we can write like this:
For React/RN(defaults are 'display' as flex and 'flexDirection' as column), in web css we'll have to specify the body container or container containing these as display: 'flex', flex-direction: 'column' like below:
container-containing-these-elements: {
display: flex,
flex-direction: column
}
header: {
height: 40,
},
middle-container: {
flex: 1, // this will take the rest of the space available.
},
footer: {
height: 100,
}
what if your wrapping div was 100% and you used padding for a pixel amount, then if the padding # needs to be dynamic, you can easily use jQuery to modify your padding amount when your events fire.
I had a similar issue where I wanted a banner across the top of the screen that had one image on the left and a repeating image on the right to the edge of the screen. I ended up resolving it like so:
CSS:
.banner_left {
position: absolute;
top: 0px;
left: 0px;
width: 131px;
height: 150px;
background-image: url("left_image.jpg");
background-repeat: no-repeat;
}
.banner_right {
position: absolute;
top: 0px;
left: 131px;
right: 0px;
height: 150px;
background-image: url("right_repeating_image.jpg");
background-repeat: repeat-x;
background-position: top left;
}
The key was the right tag. I'm basically specifying that I want it to repeat from 131px in from the left to 0px from the right.
In some contexts, you can leverage margin settings to effectively specify "100% width minus N pixels". See the accepted answer to this question.