Dynamic allocated row height of css grid gets stretched with growing content - html

Similar to this question I´m struggling with using CSS grid to create a layout with fixed header and footer containing an middle row, which should use the remaining space of the .static or .dynamic dynamic container. So in this case, both oth them should have a complete height of 200px. Subtracting the 40px (2x 20px for header + footer) the remaining space for the content should be 160px. As you can see at the example, the red reference div on the left is clearly smaller than the whole "sandwich" of div containers. The .dynamic div element is to large and will stretch the whole div container. I want to prohibit this!
Here are a few additional conditions I have to fullfill:
The whole layout should be dynamic, so the .wrapper div later wont have a static height, but will fill 100% of the given height. therefore onlydynamic will be used, since this also uses 100% of the height. The showcase with .static is just there to show that it doesn´t even work with fixed heights.
Neither static nor dynamic should work with overflow to create scrollbars or hidden overflowing content. It should just restrict the dynamic area between .header and .footer to an height.
The containing .content container will expand itself to 100% width and should be treatet as a kind of blackbox: every component should be able to be inserted here. The content will always use 100% of the height and should not strech the ambient parent divs. The content will contain an scrollbar on its own, if the height of the content will be heigher than the dynamically allocated space of the .dynamic container
How am I able to solve this issue with the given description?
Please see the provided example and feel free to adapt it as you need to!
.wrapper {
display: flex;
flex-direction: row;
height: 200px;
max-height: 200px;
width: 100%;
}
.measurement {
height: 200px;
max-height: 200px;
min-height: 200px;
min-width: 3px;
background-color: red;
border: 2px solid blue;
padding: 2px;
margin: 1px;
}
.static,
.dynamic {
display: grid;
grid-template-rows: 20px 1fr 20px;
width: 50%;
border: 2px solid blue;
padding: 2px;
margin: 1px;
}
.static {
height: 200px;
max-height: 200px;
/*should NOT have an overflow/scrollbar but fit to the remaining space*/
}
.dynamic {
height: 100%;
max-height: 100%;
/*should NOT have an overflow/scrollbar but fit to the remaining space*/
}
.content {
height: 100%;
width: 100%;
overflow: auto;
/* Blackbox like content, always expands to 100% width and height */
/* could contain content that is larger than the dynamic-height div and will get scrollbar then */
}
.fixed-height {
background-color: green;
}
<div class="wrapper">
<div class="measurement"></div>
<div class="static">
<div class="fixed-height">TOP</div>
<div class="dynamic-height">
<div class="content">
TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST
</div>
</div>
<div class="fixed-height">BOTTOM</div>
</div>
<div class="dynamic">
<div class="fixed-height">TOP</div>
<div class="dynamic-height">
<div class="content">
TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST
</div>
</div>
<div class="fixed-height">BOTTOM</div>
</div>
</div>
EDIT
As you can see in the image below, the TEST and BOTTOM text is beyond the blue borders. I´m not about the few pixels difference between the borders and the red reference but I'm concerned about the overflow over the bottom border.
This is the expected behaviour: there should be a scrollbar inside the content area, no overflow and no scrollbar inside dynamic div

You need to merge the div.content with the div.dynamic-height and set the max-height: 100% property to your .dynamic-height class.
.content doesn't need an height, it's setted by the definition of the grid row.
.wrapper {
display: flex;
flex-direction: row;
height: 200px;
max-height: 200px;
width: 100%;
}
.measurement {
height: 200px;
max-height: 200px;
min-height: 200px;
min-width: 3px;
background-color: red;
border: 2px solid blue;
padding: 2px;
margin: 1px;
}
.static,
.dynamic {
display: grid;
grid-template-rows: 20px 1fr 20px;
width: 50%;
border: 2px solid blue;
padding: 2px;
margin: 1px;
}
.static {
height: 200px;
max-height: 200px;
/*should NOT have an overflow/scrollbar but fit to the remaining space*/
}
.dynamic {
height: 100%;
max-height: 100%;
/*should NOT have an overflow/scrollbar but fit to the remaining space*/
}
.content {
width: 100%;
overflow: auto;
/* Blackbox like content, always expands to 100% width and height */
/* could contain content that is larger than the dynamic-height div and will get scrollbar then */
}
.fixed-height {
background-color: green;
}
.dynamic-height {
max-height: 100%;
}
<div class="wrapper">
<div class="measurement"></div>
<div class="static">
<div class="fixed-height">TOP</div>
<div class="dynamic-height content">
TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST
</div>
<div class="fixed-height">BOTTOM</div>
</div>
<div class="dynamic">
<div class="fixed-height">TOP</div>
<div class="content dynamic-height">
TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST<br>TEST
</div>
<div class="fixed-height">BOTTOM</div>
</div>
</div>

Related

CSS grow child to parent's max width

There are plenty of questions on how to make a parent's width that of it's child that are suggested as similar questions, this is not what I want.
I am working on theming a piece of software for branding purposes, I do not control the software and only have access to CSS modifications, so JavaScript or modifying the DOM is out of the question or this would be trivial.
Using CSS only, is it possible to achieve the following.
I have a container div that holds two columns, the main content area, and a sidebar. The sidebar contains multiple divs that wrap content for different sidebar elements. The content of these sidebar elements are designed to be scaled to 100% width of it's parent, which works when the parent has a fixed width, which is by default 25% of the container.
What we need is for the sidebar to disappear when it's content is hidden. The content within each sidebar element can be set to display: none, but when all the elements are hidden the sidebar still takes up empty space. The example below using a min-width: fit-content and a max width of the 25% which was previously it's fixed size, which works fine for hiding the sidebar when the content is hidden, but the content in the sidebar doesn't grow. Is there a way to make the content inside of .sidebarElementWrapper below, grow to fit the max-width of #sidebarArea
* {
margin: 0;
box-sizing: border-box;
}
#container {
height: 100%;
display: flex;
flex-direction: row;
}
#contentArea {
width: 100%;
background: #A84652;
}
#sidebarArea {
max-width: 25%;
min-width: fit-content;
background: #4262C2;
}
.sidebarElement {
width: 100%;
padding: 12px;
background: #8b8b8b;
border-bottom: 1px solid #2c2c2c;
}
<div id="container">
<div id="contentArea">
</div>
<div id="sidebarArea">
<div class="sidebarElementWrapper">
<div class="sidebarElement">Sidebar Element 1</div>
</div>
<div class="sidebarElementWrapper">
<div class="sidebarElement">Sidebar Element 2</div>
</div>
</div>
</div>
To hide the content of a sidebar element, the .sidebarElement is set to display: none, not the wrapper, just of note. Since the wrapper still exists, and the content isn't removed, just hidden, the :empty pseudo-selector also doesn't work.
Edit: Just to clarify as I don't think it was clear after reading through this again, when the content is hidden the sidebar should be gone, doesn't matter how whether it's through display:none or width or some other mechanism. When there is sidebar content it should be 25% the width of the container.
Edit again: Because of some comments, I'm going to attempt to explain this again.
The sidebar has a min and max width, it does not have a fixed width, the sidebar will scale to fit the content inside it. Because the sidebar does not have a fixed width, elements basing their width on 100% of the parent do not act like you may expect, instead the sidebar is defaulting to the min-width from what I can tell, which is the minimum width required to fit the content. I am not looking for this, I want the sidebar to extend out to 25% of the container width, which you can see if you copy this code into a file (because the snippet above will run in a smaller pane it may actually be wider than 25% so it may not be representative)
* {
margin: 0;
box-sizing: border-box;
}
body {
width: 100%;
height: 100%;
}
#container {
height: 100%;
display: flex;
flex-direction: row;
}
#contentArea {
width: 100%;
background: #A84652;
}
#sidebarArea {
max-width: 25%;
min-width: fit-content;
background: #4262C2;
}
.sidebarElement {
width: 100%;
padding: 12px;
background: #8b8b8b;
border-bottom: 1px solid #2c2c2c;
}
<div id="container">
<div id="contentArea">
</div>
<div id="sidebarArea">
<div class="sidebarElementWrapper">
<div class="sidebarElement">Sidebar Element 1</div>
</div>
<div class="sidebarElementWrapper">
<div class="sidebarElement">Sidebar Element 2</div>
</div>
</div>
</div>
In the end, I have a div with a min and max width, and a child element using a percent. The percent width (ie. width:100%) does not scale the child to the element's max width, but 100% of the current width, so no, the code as above does not achieve what I need.
I found a way to do it, fill-available.
.sidebarElement {
width: 100vw;
...
max-width: fill-available;
max-width: stretch;
max-width: -webkit-fill-available;
max-width: -moz-available;
}
Once this is done, the min-width on #sidebarArea can be removed.
* {
margin: 0;
box-sizing: border-box;
}
html,body {
width: 100%;
height: 100%;
}
#container {
height: 100%;
display: flex;
flex-direction: row;
}
#contentArea {
width: 100%;
background: #A84652;
}
#sidebarArea {
max-width: 25%;
background: #4262C2;
}
.sidebarElement {
width: 100vw;
padding: 12px;
background: #8b8b8b;
border-bottom: 1px solid #2c2c2c;
max-width: fill-available;
max-width: stretch;
max-width: -webkit-fill-available;
max-width: -moz-available;
}
<body>
<div id="container">
<div id="contentArea">
</div>
<div id="sidebarArea">
<div class="sidebarElementWrapper">
<div class="sidebarElement">Sidebar Element 1</div>
</div>
<div class="sidebarElementWrapper">
<div class="sidebarElement">Sidebar Element 2</div>
</div>
</div>
</div>
</body>

shrink middle div horizontally on browser resize

I have a 3 column layout which I'm creating using inline-block divs. The left and right columns are fixed widths but the inner column is to hold dynamic content and should expand horizontally as required by it's content width.
That's easy enough... the tricky part is that when the browser window is smaller (horizontally) than the width of the left, right and expanded middle divs, I would like the middle div to scroll and the side columns to stay fixed. In other words, the middle div's size should shrink and grow with window resize but should not grow beyond the available space.
Simply laying out the divs looks like this
https://jsfiddle.net/xzjp5xef/1/
HTML:
<div id="container">
<div id="lcol">
left
</div>
<div id="midcol">
<div id="spacer">
150px spacer
</div>
</div>
<div id="rightcol">
right
</div>
</div>
CSS:
div {
height:200px;
border-style:solid;
display: inline-block;
border-width: 1px;
vertical-align: top;
}
#container{
white-space: nowrap;
}
#lcol {
background-color:blue;
width: 100px;
}
#midcol {
background-color: yellow;
overflow-x: auto;
}
#spacer {
min-width: 150px;
margin: 10px;
height: 20px;
}
#rightcol {
background-color: red;
width:100px;
}
The point of the "spacer" div is to represent the dynamic content which in this case I've fixed to 150px plus padding. So in this case I want the divs to lay out the way they do in the above fiddle, but then when the window is shrunk horizontally, I want the middle div to scroll and the left and right divs to remain fully visible.
That fails because then the window gets a scroll bar but the middle panel remains the same width and the right hand div disappears into the scrolled region.
My next attempt was using absolute positioning
https://jsfiddle.net/n4zrLqh2/
I fixed the left div to the left and the right div to the right and set the middle div's right and left properties. This is a neat trick which allows the middle div to stretch and take up all available space. This works nicely but doesn't create the effect I'm after when the window is big - because I don't want the middle column to expand further than is necessary to contain its content.
In the end I've solved this with javascript but would much prefer a CSS solution.
Edit: To help others see what I'm trying to achieve, here's the complete javascript solution (which I'd prefer to achieve with pure CSS):
HTML:
<div id="lcol">left</div>
<div id="midcol">
<div id="spacer">150px spacer</div>
</div>
<div id="rightcol">right</div>
CSS:
div {
height:200px;
display: inline-block;
vertical-align: top;
margin: 0px;
float:left;
}
body {
white-space: nowrap;
margin:0px;
max-height: 200px;
}
#lcol {
background-color:blue;
width: 100px;
}
#midcol {
background-color: yellow;
overflow-x: auto;
}
#spacer {
min-width: 150px;
height: 20px;
background-color: gray;
margin: 5px;
}
#rightcol {
background-color: red;
width:100px;
}
JAVASCRIPT (with jquery)
function adjustSizes() {
// Sizes of middle divs are dynamic. Adjust once
// built or whenever the viewport resizes
//
var $leftDiv = $('#lcol')
var $milddleDiv = $('#midcol');
var $rightDiv = $('#rightcol');
// 1. Resize middle div to available viewport space
var maxBodyWidth = $(window).innerWidth() - ($leftDiv.outerWidth() + $rightDiv.outerWidth());
$milddleDiv.css('maxWidth', maxBodyWidth);
}
$(window).resize(function () {
adjustSizes();
});
And the fiddle: https://jsfiddle.net/bjmekkgj/2/
I think setting max-width of spacer will solve your problem in case content increases.
Set max-width to calc(100vw - 200px) if all margin and padding are 0. Otherwise adjust the value 200px taking margin, padding into account.
I have created a plunker. Please check if it solves your issue. Try checking after running plunker in spearate window
http://plnkr.co/edit/WG9v0MyiD2hiaZrOA3Yw?p=preview
For the one example you provided, since the left and right columns are positioned absolutely, you should take up the space somehow. I used padding on the middle column, then nested a "content" block inside that represents the visible part of the middle column. Then, I put overflow-x: auto; on the new content block and set a max-width on the overall container to force the new block to shrink.
(In previous edits, I was attempting to do this same thing but with floats instead of absolutely positioned divs)
* { margin: 0px; padding: 0px; }
#container {
box-sizing: border-box;
display: inline-block;
position: relative;
max-width: 100%;
border: 1px solid black;
height: 200px;
}
.column {
box-sizing: border-box;
height: 100%;
}
#left {
position: absolute;
left: 0px;
top: 0px;
bottom: 0px;
width: 100px;
border-right: 1px solid black;
background: blue;
}
#mid {
border: none;
padding: 0px 100px;
}
#mid > .content {
box-sizing: border-box;
background-color: yellow;
overflow-x: auto;
height: 100%;
}
#spacer {
width: 150px;
height: 20px;
}
#right {
position: absolute;
right: 0px;
top: 0px;
bottom: 0px;
width: 100px;
border-left: 1px solid black;
background: red;
}
<div id="container">
<div id="left" class="column">
left
</div>
<div id="mid" class="column">
<div class="content">
<div id="spacer">
150px spacer
</div>
</div>
</div>
<div id="right" class="column">
right
</div>
</div>
...and in JSFiddle form
flexbox can do that.
div {
border-style: solid;
border-width: 1px;
}
#container {
height: 200px;
display: flex;
}
#lcol {
background-color: blue;
width: 100px;
}
#midcol {
background-color: yellow;
flex: 1;
overflow-x: auto;
}
#rightcol {
background-color: red;
width: 100px;
}
<div id="container">
<div id="lcol">
left
</div>
<div id="midcol">
</div>
<div id="rightcol">
right
</div>
</div>
JSfiddle Demo (showing overflow effect).
Support is IE10 and up.
Try setting the middle div to have a max width with a percentage so it will get thinner with the screen size:
.midcol {
max-width: 25%;
}
I put a value for the max-width in there for an example, but you can change the value.

Prevent background layout from stretching

I'm trying to make container size stretch to size of its items. But browser always stretches containers background width to 100%. Is there a way to prevent layout from stretch?
Link to JSFiddle
.content {
margin-left: 50px;
background-color: grey;
}
.item {
margin: 20px;
display: inline-block;
background-color: #BFC5C7;
width: 250px;
height: 400px;
}
<div class="content">
<div class="item">one</div>
<div class="item">two</div>
<div class="item">three</div>
<div class="item">four</div>
</div>
You can't control the background 'width'. The only thing you could do here would be to set a width or a max-width onto your content div, e.g.
.content{
max-width:290px;
margin-left: 50px;
background-color: grey;
}
The other alternative, if you don't want to set a width on this element would be to give it a background image with the maximum width/height you expect your background to be and set it to no-repeat, e.g.
.content{
margin-left: 50px;
background:url(mySizedImage.png) no-repeat top left;
}

Fluid three column layout with fixed width center column

I want to obtain this layout:
<------------ Browser Width 100% ------------>
[left][----- center: fixed width -----][right]
The center column has a fixed pixel width
The left and right columns fill in the remaining viewport width equally
The example below breaks when the viewport width is not wide enough, and getting the correct percentage width is hard because of the fixed width center column.
div {
display: inline-block;
background: #F90;
height: 100px;
width: 20%;
}
.center {
width: 500px;
background: #FF0;
}
<div class="left">left (fill available space)</div>
<div class="center">fixed width</div>
<div class="right">right (fill available space)</div>
Three ways to achieve a fluid / fixed column layout
Method #1 - with display: table
This is one of the easiest methods and has good browser support.
Compatibility: IE8 + and all modern browsers
body gets display: table - this could also be applied to a div wrapper instead.
table-layout: fixed ensures the middle column remains fixed width
the direct div children of body get display: table-cell
the body gets a min-width to ensure the left and right columns do not get too small
the middle column is fixed at your desired width (500px in this example)
the left and right columns inherit the remaining page width
#1 - Working Example
html {
height: 100%;
}
body {
display: table;
height: 100%;
margin: 0;
width: 100%;
table-layout: fixed;
min-width: 800px;
}
body > div {
display: table-cell;
}
.left {
background: #000;
}
.middle {
background: #F00;
width: 500px;
}
.right {
background: #F90
}
<div class="left">
</div>
<div class="middle">
</div>
<div class="right">
</div>
Method #2 - with display: inline-block and width: calc(x - y)
Compatibility: Calc is compatible in IE 9 + and most modern browsers. There are javascript fallbacks available as well.
The direct div children of body are given display: inline-block and vertical-align: top. They will align themselves to the top of the browser window on the same line
The middle column gets its fixed width
The left and right columns are given calc(50% - 250px); this calculates 50% of the page width minus half of the width of the fixed middle column.
box-sizing: border-box incorporates padding and borders into the width and height
#2 - Working Example
Note how the closing and opening divs tags have no gaps between them; this is to prevent an inline gap between elements.
* {
box-sizing: border-box;
}
html,
body {
height: 100%;
margin: 0;
}
body {
min-width: 800px;
}
body > div {
display: inline-block;
width: calc(50% - 250px);
height: 100%;
vertical-align: top;
}
.left {
background: #000;
}
.middle {
background: #F00;
width: 500px;
}
.right {
background: #F90
}
<div class="left"></div><div class="middle"></div><div class="right"></div>
Method #3 - with display: flex
This is a really awesome method, but is not supported with older browsers :(
Compatibility: IE11 and most modern browsers
The body gets display: flex and height: 100vh (100% of the viewport height)
The direct children get flex: 1 and will grow and shrink
The middle column gets its fixed width and flex: 0 0 auto; it will not grow or shrink
Here is a useful guide to Flexbox.
#3 - Working Example
body {
display: flex;
height: 100vh;
margin: 0;
min-width: 800px;
}
body > div {
flex: 1;
}
.left {
background: #000;
}
.middle {
background: #F00;
width: 500px;
flex: 0 0 auto;
}
.right {
background: #F90
}
<div class="left">
</div>
<div class="middle">
</div>
<div class="right">
</div>

css flex layout fit child into flex box

I want to have a child fill the exactly entire flex box of a flex layout.
If I use the following HTML:
<div class="parent">
<div class="child1">
should have 100px height
</div>
<div class="child2">
<div class="intermediatechild2">
<div class="subchild2">should have 200px height and padding</div>
</div>
</div>
</div>
and apply the following css:
.parent {
display: flex;
flex-direction : column;
height: 300px;
width: 200px;
}
.child1 {
height: 100px;
background: #008800;
flex: 0 0 auto;
}
.child2 {
height: 100%;
background: #003300;
flex: 0 0 auto;
}
.subchild2 {
height : 100%;
background: #ff0000;
}
.intermediatechild2 {
padding: 20px;
height : 100%;
width : 100%;
box-sizing: border-box;
border: 2px solid blue;
}
I get an overflowing intermediatechild. The height 100% seems to be relative to .parent
A fiddle can be found here:
http://jsfiddle.net/8znFV/4/
I did not understand exactly what you want, but if what you want is only leave. Subchild2 100% and follow the father's height (intermediatechild2), you'll have to add the father's height (intermediatechild2) with px and remove the height. child2.
Recalling that, you have to count the padding in father's height (intermediatechild2), so if you want. Subchild2 has 200px in height, will have to leave her father (intermediatechild2) with 240px, leaving 20 padding-top and 20 padding-bottom height of more than 200.
A note, only work in chrome as your css code is nonstandard, if you want I can breastfeed him at another time =)
Hope it helps
Here's an example: http://zip.net/bsmZgF
Just Remove height:100% from .child2and it will work. this will give 100% height to child2 element so it's going outside of parent.
It should be auto adjusted that's the purpose of flexbox and 100% height is giving more height(same as parent) to child2.
I fixed the problem. The solution lies in staying "display:flex". Once you started flex layout, you seem not to be able to step back to "display:block" with "height:100%".
<div class="parent">
<div class="child1">
should have 100px height
</div>
<div class="child2">
<div class="intermediatechild2">
<div class="subchild2">should have 200px height including padding</div>
</div>
</div>
</div>
css:
.parent {
display: flex;
flex-direction : column;
height: 300px;
width: 200px;
}
.child1 {
height: 100px;
background: #008800;
}
.child2 {
background: #003300;
flex: 1;
display: flex;
}
.subchild2 {
background: #ff0000;
flex: 1;
}
.intermediatechild2 {
padding: 20px;
display : flex;
box-sizing: border-box;
border: 2px solid blue;
}
working fiddle : http://jsfiddle.net/8znFV/6/