Fix width of a flex column based on another - css

I have a wrapper of 500px and 2 columns. The second column is 200px width (flex: 0 0 200px).
If in the first there is an element > 300px the first column will expand according to this element.
How can I stop the first column from growing, basing only the width of the wrapper and the second column?
Fiddle here: https://jsfiddle.net/b58aatdr/3/
#hello {
display: flex;
width: 500px;
}
#hello > div {
height: 50px;
}
#hello > div:first-child {
background-color: yellow;
}
#hello > div:last-child {
background-color: red;
flex: 0 0 200px;
}
#baddiv {
width: 400px;
height: 20px;
background-color: purple;
}
<div id="hello">
<div>
<div id="baddiv"></div>
</div>
<div></div>
</div>

Set max-width: 300px to your first div if you want it to adjust itself up to 300px and use width: 300px; if you want it to always be 300px even if content is less wide.
Update based on comment
The 2:nd div group uses another trick, position: absolute, where one doesn't need to set any width, it uses the parent and the right div to restrict the left div from growing beyond the 300px.
Note also, this is a normal behavior how element works, if they don't have a fixed/max width set, they grow (or in some cases wrap) to fit their content.
Update 2 based on comment
The 3:rd div group uses display: table instead of flex, where one doesn't need to set any width.
.hello {
display: flex;
width: 500px;
}
.hello > div {
height: 50px;
}
.hello > div:last-child {
background-color: red;
flex: 0 0 200px;
}
.baddiv {
width: 400px;
height: 20px;
background-color: purple;
}
/* alt. 1 */
.hello.nr1 > div:first-child {
background-color: yellow;
max-width: 300px;
}
/* alt. 2 */
.hello.nr2 > div:first-child {
flex: 1;
position: relative;
background-color: lime;
}
.inner {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
/* alt. 3 */
.hello.nr3 {
display: table;
table-layout: fixed;
width: 500px;
}
.hello.nr3 > div {
height: 50px;
display: table-cell;
}
.hello.nr3 > div:first-child {
background-color: cyan;
}
.hello.nr3 > div:last-child {
background-color: red;
width: 200px;
}
<div class="hello nr1">
<div>
<div class="baddiv"></div>
</div>
<div></div>
</div>
<br>
<div class="hello nr2">
<div>
<div class="inner">
<div class="baddiv">
</div>
</div>
</div>
<div></div>
</div>
<br>
<div class="hello nr3">
<div>
<div class="baddiv"></div>
</div>
<div></div>
</div>

Related

Why isn't `height: 100%` and absolute positioning working in flexbox?

I'm trying to make a child div of a flexbox layout fill its parent. In any other context setting the width/height to 100% causes a div to fill its parent... I only wish to use flexbox for my top level layout.
Problems
#map-container div will not fill #col1 even though it has height 100% set.
#controls div appears outside #col1 completely. I've previously used absolute layout to align boxes to corners without problems. Being inside a flexbox grand-parent seems to cause issues.
What I'm expecting is #map-container and #map to fill #col1 and #controls to align to bottom right-hand corner of #map.
.wrapper, html, body {
height:100%;
margin:0;
}
#col1 {
display: flex;
}
#map-container {
background-color: yellow;
width: 100%;
height: 100%;
}
#map {
background-color: purple;
width: 100%;
height: 100%;
}
#controls {
background-color: orange;
position: absolute;
right: 3px;
bottom: 3px;
width: 100px;
height: 20px;
}
.wrapper {
display: flex;
flex-direction: column;
}
#row1 {
background-color: red;
}
#row2 {
flex:2;
display: flex;
}
#col1 {
background-color: green;
flex: 1 1;
}
#col2 {
background-color: blue;
flex :0 0 240px;
}
<div class="wrapper">
<div id="row1">Header</div>
<div id="row2">
<div id="col1">
<div id="map-container">
<div id="map">
Map
</div>
<div id="controls">Controls</div>
</div>
</div>
<div id="col2">Sidebar</div>
</div>
</div>
#map-container div will not fill #col1 even though it has height 100% set.
It won't work that way, because for a percentage unit to work, it needs to have height set on its parent all the way up. This fights against the flex model, where the flex-items are distributed and arranged by the flex-box layout and have no dimensions set. Why use a flex layout when all your elements are 100%? Either do a 100% on all your element all the way up, or do a flex on all containers.
If you stick to flex layout, then you will have to get into nested flex. Otherwise, you will get #map-container to fill-up, but not the #map.
This fiddle http://jsfiddle.net/abhitalks/sztcb0me illustrates that problem.
#controls div appears outside #col1 completely. I've previously used absolute layout to align boxes to corners without problems. Being
inside a flex-box grand-parent seems to cause issues.
The only issue is that you are positioning it absolutely, but in relation to what? You need to position your #map-container relatively for that to work.
Here is how:
Fiddle: http://jsfiddle.net/abhitalks/sztcb0me/1/
Snippet:
* { box-sizing: border-box; padding: 0; margin: 0; }
html, body, .wrapper { height:100%; width: 100%; }
.wrapper { display: flex; flex-direction: column; }
#row1 { flex: 0 1 auto; background-color: red; }
#row2 { flex: 2 0 auto; display: flex; }
#col1 { flex: 1 0 auto; display: flex; background-color: green; }
#col2 { flex: 0 0 240px; background-color: blue; }
#map-container {
flex: 1 0 auto; display: flex;
position: relative; background-color: yellow;
}
#map { flex: 1 0 auto; background-color: purple; }
#controls {
background-color: orange;
position: absolute;
right: 3px; bottom: 3px;
width: 100px; height: 20px;
}
<div class="wrapper">
<div id="row1">Header</div>
<div id="row2">
<div id="col1">
<div id="map-container">
<div id="map">
Map
</div>
<div id="controls">Controls</div>
</div>
</div>
<div id="col2">Sidebar</div>
</div>
</div>
the problem is that it's wrapped in #map-container
.wrapper, html, body {
height:100%;
margin:0;
}
#col1 {
display: flex;
}
#map-container {
background-color: yellow;
width: 100%;
height: 100%;
}
#map {
background-color: purple;
width: 100%;
/* height: 100%; */
display: flex;
flex-wrap: wrap-reverse;
justify-content: space-between;
}
#controls {
background-color: orange;
position: relative;
/*right: 3px;
bottom: 3px;*/
width: 100px;
height: 20px;
}
.wrapper {
display: flex;
flex-direction: column;
}
#row1 {
background-color: red;
}
#row2 {
flex:2;
display: flex;
}
#col1 {
background-color: green;
flex: 1 1;
}
#col2 {
background-color: blue;
flex :0 0 240px;
}
<div class="wrapper">
<div id="row1">Header</div>
<div id="row2">
<div id="col1">
<!-- <div id="map-container"> -->
<div id="map">
Map
<div id="controls">Controls</div> <!-- add it here -->
</div>
<!-- <div id="controls">Controls</div> -->
<!--</div> -->
</div>
<div id="col2">Sidebar</div>
</div>
</div>
adding an absolute position controls in that way is not optimal (in the snippet I commented it out) ; you can place the controlsnested in #map (and use flex properties for correcting placement)

First div fixed, second full width. Cant change structure of html

Have problem. I have this code.
<div class="main">
<div class="sidebar"></div>
<div class="content"></div>
</div>
I need to make two colums.
"Sidebar" must have fixed width 200px;
And "content" all remaining width to fullscreen.
I cant change the structure of html code, just css.
if absolute position is ok, you can use it to say left:200px; right:0 and get all the space you need
fiddle : http://jsfiddle.net/h2udmqhn/
.main {
position: relative;
height: 300px;
width: 300px;
border: 1px solid black;
}
.sidebar {
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 200px;
background: red;
}
.content {
position: absolute;
top: 0;
left: 200px;
right: 0;
height: 200px;
background: blue;
}
<div class="main">
<div class="sidebar"></div>
<div class="content"></div>
</div>
Use float: left for .sidebar and left margin for .content:
.sidebar {float: left; width: 200px; background: red;}
.content {background: green; margin: 0 0 0 200px;}
http://jsfiddle.net/orty5qtj/1/
Another option is to use calc, which is unsupported in IE8. The solution above works fine in all browsers.
Try this :
.sidebar {
float: left;
min-height: 50px;
background: red;
width: 200px;
}
.content {
background : yellow;
margin-left: 200px;
min-height: 50px;
}
https://jsfiddle.net/Saiyam/5krmkkkx/3/
There a couple of simple ways to do this without the need for calc, margins or absolute positioning. Both of the following ways have the added bonus of keeping the columns the same height as each other
Using display table (compatible to back ie8)
.main {
display: table;
width: 100%;
}
.main > div {
display: table-cell;
}
.sidebar {
width: 200px;
background: blue;
}
.content {
background: red;
}
<div class="main">
<div class="sidebar">200px</div>
<div class="content">the rest</div>
</div>
Using flex (for newer browsers only unless used with the browser prefix):
.main {
display: flex;
width:100%;
max-width:100%;
}
.sidebar {
width: 200px;
flex: 0 0 200px;
background-color:blue;
}
.content {
background-color:red;
flex: 1 0 auto;
}
<div class="main">
<div class="sidebar">200px</div>
<div class="content">the rest</div>
</div>

position sticky automatic "top" position

Is there a way for stickies to take into account other stickes on the page?
For example:
body {
display: flex;
min-height: 2000px;
flex-direction: column;
}
#header {
height: 40px;
flex: 0 0 auto;
position: sticky;
top: 0;
background: yellow;
}
#footer {
flex: 0 0 auto;
height: 20px;
}
#main {
display: flex;
flex: auto;
background: blue;
}
#side {
width: 200px;
background: red;
}
#side > div {
position: sticky;
top: 0px;
}
<div id="header">header</div>
<div id="main">
<div id="side">
<div>side</div>
</div>
<div id="content">
content
</div>
</div>
<div id="footer">footer</div>
Notice that if I scroll down the header will overlap the sidebar because they have the same top position.
To fix I have to make the top position of the sidebar take the value of the header height
body {
display: flex;
min-height: 2000px;
flex-direction: column;
}
#header {
height: 40px;
flex: 0 0 auto;
position: sticky;
top: 0;
background: yellow;
}
#footer {
flex: 0 0 auto;
height: 20px;
}
#main {
display: flex;
flex: auto;
background: blue;
}
#side {
width: 200px;
background: red;
}
#side > div {
position: sticky;
top: 40px;
}
<div id="header">header</div>
<div id="main">
<div id="side">
<div>side</div>
</div>
<div id="content">
content
</div>
</div>
<div id="footer">footer</div>
But what if the header has variable height? Can I tell the browser somehow to position the stickies so they dont overlap others?
I think you would need to use javascript for this. First to get the height of the header and then set the top position of your side div using that value. I am not aware of any pure css way of doing it I am afraid.
If you are using jQuery it is simply using the .height() method if not you can use this:
var clientHeight = document.getElementById('myDiv').clientHeight;
var offsetHeight = document.getElementById('myDiv').offsetHeight;
The offset method gets the height with any padding and borders.

How to position header, sidebox and mainbox?

Demo: http://jsfiddle.net/zz1ou0bv/
HTML:
<div class="header">header</div>
<div class="sidebox">sidebox</div>
CSS:
.header {
background-color: red;
height:60px;
}
.sidebox {
width: 210px;
background-color: blue;
color: black;
position: absolute;
bottom: 0;
top: 0;
}
How do I position the blue box just under the red box? I could for example add top: 68px; to the .sidebox to fix the problem but is there any other way to position it automatically, I would like to change the header height without being forced to change the top tag in sidebox to make it fit.
How do I position a brand new div that takes up the WHOLE white area under the red box and besides blue box? This should be automatic in case header/sidebox changes height/width. The green content should be replaced with this new div: http://i.gyazo.com/a41107cb7c1844b439f045ad85d40aec.png
If you always want the sidebar to occupy 100% of the window you could try this approach:
html, body { height: 100%; }
.header {
background-color: red;
height:60px;
}
.sidebox {
width: 210px;
background-color: blue;
color: black;
bottom: 0;
top: 0;
min-height: 100%;
}
http://jsfiddle.net/zz1ou0bv/2/
Here's my attempt: http://jsfiddle.net/rL8z9bm0/ .
Regarding your second question you should always have some kind of relation between the blue and yellow boxes, either pixels or percentage (better)
.sidebox {
width: 30%;
...
}
.content {
left:30%;
...
}
Seems like a good use case for the flexbox here:
Fiddle with Flexbox
Here's the relevant source:
HTML
<div class="wrapper">
<div class="header">header</div>
<div class="sidebox">sidebox</div>
<div class="content">content</div>
</div>
CSS
body {
height: 100vh;
margin: 0;
}
.wrapper {
display: flex;
flex-flow: row wrap;
height: 100%;
}
.header {
background-color: red;
height:60px;
flex: 1 100%;
}
.sidebox {
flex: 1 auto;
background-color: blue;
color: white;
}
.content {
flex: 4 auto;
background-color: sienna;
color: white;
height: calc(100% - 60px);
}
Hope that helps.

how to make three column layout like this in css3

So there are 3 columns all with 100% vertical height.
left column has fixed width of 80px.
middle column and right column fill in the available space by ratio of 80% to 20%. So middle takes up 80% space, and right one takes 20% space.
right column width however if is less than 100px that it becomes fixed to 100px. min-width is 100px and max-width is 20% of available space.
I know right now there is no way to refer available vertical or horizontal space, or choose what percentage refers to...and that's why i am lost.
I can't use flexbox, and don't want to use javascript (but be sure it's not possible with css first).
This can be easily achieved using display: table and display: table-cell.
http://jsfiddle.net/Mgzbq/
HTML
<div class="table main">
<div class="cell left">left</div>
<div class="cell">
<div class="table inner">
<div class="cell center"></div>
<div class="cell right"></div>
</div>
</div>
</div>
CSS
.table {
display: table;
}
.row {
display: table-row;
}
.cell {
display: table-cell;
vertical-align: middle;/* Keep this as top|middle|bottom, otherwise the container table of center and right div will have "vertical-align: baseline" and doesn't position properly if there is no content in center and right div*/
}
.left {
width: 80px;
background-color: #E07749;
}
.center {
width: 80%;
background-color: #E0DD49;
}
.right {
width: 20%;
min-width: 100px;
background-color: #49E0AE;
}
.main {
width: 100%;
height: 200px;
}
.inner {
width: 100%;
height: 100%;
}
Here is the demo what you want.
CSS
*{
margin: 0
}
body, html, .outer, .leftCol, .rightSec, .innerCnt{
height: 100%;
}
.leftCol {
min-height:100px;
width: 80px;
float: left;
background: red
}
.rightSec {
margin-left:80px
}
.innerCnt {
display: table;
width: 100%;
}
.midCol {
width: 80%;
background: green;
height:100px;
display: table-cell;
}
.rightCol {
min-width: 100px;
display: table-cell;
background: yellow;
height:100px;
}

Resources