Empty cell in flexbox - css

There is a usual flexbox with the elements:
.flexbox {
display: flex;
border: 2px solid red;
padding: 2.5px;
}
.flexbox__item {
border: 2px solid blue;
height: 50px;
margin: 2.5px;
flex-grow: 1;
}
<div class="flexbox">
<div class="flexbox__item"></div>
<div class="flexbox__item offset-1"></div>
<div class="flexbox__item"></div>
</div>
How to insert an empty cell in this flexbox after .offset-* without using additional markup and after with before?
Those. It is necessary that it come out like this, but without specifying the width of the blocks. Only flex-grow
.flexbox {
display: flex;
border: 2px solid red;
padding: 2.5px;
}
.flexbox__item {
border: 2px solid blue;
height: 50px;
margin: 2.5px;
width: 25%;
}
.offset-1 {
margin-right: calc(25% + 5px);
}
<div class="flexbox">
<div class="flexbox__item"></div>
<div class="flexbox__item offset-1"></div>
<div class="flexbox__item"></div>
</div>
The number of cells is unknown. Offset-1 means that you need to make an empty one cell,offset-2 means two cell, and so on.

It is possible with additional wrappers - the wrappers will grow, and the cell elements inside the growing wrappers will occupy 1/2, 1/3, etc. of the width of the wrappers.
.flexbox {
display: flex;
border: 2px solid red;
padding: 3px;
}
.flexbox__item {
margin: 3px;
flex-grow: 1;
}
.flexbox__item-inner {
border: 2px solid blue;
height: 50px;
}
.offset-1 {
flex-grow: 2;
}
.offset-1 .flexbox__item-inner {
width: 50%;
}
.offset-2 {
flex-grow: 3;
}
.offset-2 .flexbox__item-inner {
width: 33.33%;
}
<div class="flexbox">
<div class="flexbox__item">
<div class="flexbox__item-inner"></div>
</div>
<div class="flexbox__item offset-1">
<div class="flexbox__item-inner"></div>
</div>
<div class="flexbox__item">
<div class="flexbox__item-inner"></div>
</div>
</div>
<div class="flexbox">
<div class="flexbox__item">
<div class="flexbox__item-inner"></div>
</div>
<div class="flexbox__item offset-2">
<div class="flexbox__item-inner"></div>
</div>
<div class="flexbox__item">
<div class="flexbox__item-inner"></div>
</div>
</div>

Related

Account for gap when calculating flex-basis

I'm trying to use gap to specify gaps between flexed items within my grid system, but running in to a major drawback. It seems that when you're using flex-grow: 0;/flex-shrink: 0; in conjunction with gap and flex-basis values that fill the entire available width (i.e. three columns with flex: 0 0 33.3333%;), the columns overflow their parent container as the gap doesn't account for the fixed width as specified with flex: 0 0 33.3333%.
Similar to box-sizing: border-box;, is there some way to instruct the rendering engine that the gap should be subtracted when determining the width of these columns?
Demonstration:
.row {
display: flex;
gap: 30px;
border: 2px solid red;
}
.col {
flex: 0 0 33.3333%;
background: teal;
border: 2px solid #004D4D;
color: white;
font-weight: 700;
padding: 50px;
text-align: center;
}
:root {
font-family: sans-serif;
}
* {
box-sizing: border-box;
}
<h2>With gap:</h2>
<div class="row">
<div class="col">
1
</div>
<div class="col">
2
</div>
<div class="col">
3
</div>
</div>
<h2>Without gap:</h2>
<div class="row" style="gap:0;">
<div class="col">
1
</div>
<div class="col">
2
</div>
<div class="col">
3
</div>
</div>
Note: I could account for this with a formula like flex-basis: calc($width - ($gap / ($number-of-columns / 2));, but as this is for a reusable grid system, I can't practically account for every possible scenario.
Here is another not very elegant quick way to hack your way forward. Similar to the answer by UPinar it alters the outer flex container. Here with negative margin on the right (this might cause other layout problems!). This "solution" is using shrink 0. Also it works with a wrapping flex.
I agree that this should not be so complicated and hacky. Maybe we are missing something? I am also under the impression that this is not the really the desired box-sizing border-box behavior which I hoped to find in combination with the gap property.
flex and gap should be hack free like: Draw three containers each consuming a third of the width and have some space between em. AFAIK gap works that way with CSS grid and CSS columns.
.flex {
display: flex;
flex-grow: 0;
flex-shrink: 0;
flex-wrap: wrap;
}
.flex.gap {
gap: var(--space-s);
margin-right: calc(-1 * var(--space-s));
}
.col {
flex-basis: 33.3333%;
background: teal;
border: 2px solid #004D4D;
color: white;
font-weight: 700;
padding: 50px;
text-align: center;
}
.flex.gap .col {
flex-basis: calc(33.3333% - var(--space-s));
background: teal;
border: 2px solid #004D4D;
color: white;
font-weight: 700;
padding: 50px;
text-align: center;
}
:root {
font-family: sans-serif;
--space-s: 1rem;
}
* {
box-sizing: border-box;
}
<h2>With gap</h2>
<div class="flex gap">
<div class="col">
1
</div>
<div class="col">
2
</div>
<div class="col">
3
</div>
<div class="col">
4
</div>
<div class="col">
5
</div>
<div class="col">
6
</div>
</div>
<h2>Without gap</h2>
<div class="flex">
<div class="col">
1
</div>
<div class="col">
2
</div>
<div class="col">
3
</div>
<div class="col">
4
</div>
<div class="col">
5
</div>
<div class="col">
6
</div>
</div>
The formula you mentioned works... you can use CSS variables to make a reuseable grid system. A buddy and I came up with this:
.flex{
--columns:3;
--gap:30px;
--gap-count:calc( var(--columns) - 1 );
display:flex;
gap:var(--gap);
}
.flex-child {
flex-basis: calc( calc( 100% / var(--columns) ) - calc( var(--gap) / var(--columns) * var(--gap-count) ) );
}
#media (max-width: 992px) {
.flex{
--columns:2;
}
}
#media (max-width: 767px) {
.flex{
--columns:1;
}
}
So then all you need to change are the variables --columns and --gap
https://codepen.io/pyledigital/pen/mdWmjQb
When you add a padding-right: calc(var(--gap-space) * 2); to parent container. Parent container width will calculte before child containers use 100% which is parent container width. You need to change parent containers width before using its width inside child container.
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
min-height: 100vh;
text-rendering: optimizeSpeed;
line-height: 1.5;
margin: 0;
background-color: bisque;
display: grid;
place-content: center;
}
:root{
--gap-space:30px;
font-family: sans-serif;
}
.row-1 {
display: flex;
gap: var(--gap-space);
border: 2px solid red;
padding-right: calc(var(--gap-space) * 2);
}
.row-1 .col{
background: teal;
border: 2px solid #004D4D;
color: white;
font-weight: 700;
padding: 50px;
text-align: center;
flex: 0 0 calc(100% / 3);
}
.row-2{
display: flex;
flex-direction: row;
border: 2px solid red;
}
.row-2 .col{
background: teal;
border: 2px solid #004D4D;
color: white;
font-weight: 700;
padding: 50px;
text-align: center;
flex: 0 0 calc(100% / 3);
}
<h2>With gap:</h2>
<div class="row-1">
<div class="col">1</div>
<div class="col">2</div>
<div class="col">3</div>
</div>
<h2>Without gap:</h2>
<div class="row-2" style="gap: 0">
<div class="col">1</div>
<div class="col">2</div>
<div class="col">3</div>
</div>
What's wrong with using only width?
.col {
width: 33.3333%;
...
}
.row {
display: flex;
gap: 30px;
border: 2px solid red;
}
.col {
width: 33.3333%;
background: teal;
border: 2px solid #004D4D;
color: white;
font-weight: 700;
padding: 50px;
text-align: center;
}
:root {
font-family: sans-serif;
}
* {
box-sizing: border-box;
}
<h2>With gap:</h2>
<div class="row">
<div class="col">
1
</div>
<div class="col">
2
</div>
<div class="col">
3
</div>
</div>
<h2>Without gap:</h2>
<div class="row" style="gap:0;">
<div class="col">
1
</div>
<div class="col">
2
</div>
<div class="col">
3
</div>
</div>

Flexbox: Why isn't whole div shown when element below becomes hidden?

I need a flexible column where a couple of widgets are placed below each other and they are supposed to take up space dynamically. A widget has a title, and a scrollable content.
The last widget is supposed to be collapsible (by clicking on the widget title).
The problem is: When I collapse the widget, part of the title becomes hidden.
See here (click "Batch runs" to see the problem):
http://jsfiddle.net/stoefln/Ls0aqnvf/8/
$('.batchRunsTitle').on('click', function() {
$('.batchRuns').toggle()
})
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="column" style="height: calc(100% - 80px); background: #AAA; display: flex; flex-direction: column; ">
<div class="batchView" style="flex-grow: 1; display: flex; flex-direction: column; border: 1px solid #F00; overflow: hidden;">
<div class="header" style="">widget title 1</div>
<div class="tests" style="flex-basis: 70%; border: 1px solid #F0F; overflow: auto;">test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/></div>
<div>
widget title 2
</div>
<div class="ehs" style="flex-basis: 30%; border: 1px solid #00F; overflow: auto;">test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/></div>
</div>
<div class="batchRunsContainer" style="flex-grow: 1; display: flex; flex-direction: column; overflow: auto">
<div class="batchRunsTitle" style="cursor: pointer; background-color: #6c2; border: 10px solid #55F; ">
widget title 3
</div>
<!-- why is the "Batch runs" title only half visible when the next block gets hidden??? -->
<div class="batchRuns" style="overflow: auto; display: block;">
test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>
</div>
</div>
</div>
<div class="log" style="height: 80px; background-color: #EEAEEE66; position: absolute; bottom: 0; right: 0; left: 0;">
Log
</div>
Also for reference here the 2 states as screenshots:
Open:
Collapsed:
Looks like it is being cut due to the overflow: auto on the batchRunsContainer. One way to fix this is to put the batchRunsTitle div outside the batchRunsContainer. Here is a working demo (check in full page):
const title = document.querySelector('.batchRunsTitle')
const content = document.querySelector('.batchRuns')
title.addEventListener('click', () => {
if (content.style.display !== 'none') {
content.style.display = 'none'
} else {
content.style.display = ''
}
})
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
width: 100%;
}
<div class="column" style="height: calc(100% - 80px); background: #AAA; display: flex; flex-direction: column; ">
<div class="batchView" style="flex-grow: 1; display: flex; flex-direction: column; border: 1px solid #F00; overflow: hidden;">
<div class="header" style="">widget title 1</div>
<div class="tests" style="flex-basis: 70%; border: 1px solid #F0F; overflow: auto;">test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/></div>
<div>
widget title 2
</div>
<div class="ehs" style="flex-basis: 30%; border: 1px solid #00F; overflow: auto;">test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/></div>
</div>
<div class="batchRunsTitle" style="cursor: pointer; background-color: #6c2; border: 10px solid #55F; ">
widget title 3
</div>
<div class="batchRunsContainer" style="flex-grow: 1; display: flex; flex-direction: column; overflow: auto">
<div class="batchRuns" style="overflow: auto; display: block;">
test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>
</div>
</div>
</div>
<div class="log" style="height: 80px; background-color: #EEAEEE66; position: absolute; bottom: 0; right: 0; left: 0;">
Log
</div>

Could browser like tabs made with flex box or css only?

Need tabs to shrink while the main container doesn't fit all items by width.
Here is expected behavior:
http://adamschwartz.co/chrome-tabs/
But could it be done on pure css, flexbox may be?
Solution was pretty simple. Just display: flex; for container, and overflow: hidden; for tab items.
Don't know why my question was downvoted. :(
html {
font-family: sans-serif;
}
.container {
display: flex;
border: 1px solid silver;
padding: 10px 10px 0;
width: 400px;
margin: 0 auto 10px;
}
.tab {
overflow: hidden;
text-overflow: ellipsis;
height: 20px;
border: 1px solid silver;
border-top-right-radius: 8px;
border-top-left-radius: 8px;
padding: 10px;
margin-right: 8px;
border-bottom: none;
}
.tab:last-child {
margin-right: 0;
}
<div class="container">
<div class="tab">Google</div>
<div class="tab">Apple</div>
<div class="tab">Facebook</div>
</div>
<div class="container">
<div class="tab">Google</div>
<div class="tab">Apple</div>
<div class="tab">Facebook</div>
<div class="tab">Chrome</div>
<div class="tab">Flexbox</div>
</div>
<div class="container">
<div class="tab">Google</div>
<div class="tab">Apple</div>
<div class="tab">Facebook</div>
<div class="tab">Chrome</div>
<div class="tab">Flexbox</div>
<div class="tab">Stackoverflow</div>
</div>

Resize the header and content div dimensions respond when the window size changes but maintain right sidebar dimensions

I am trying to use a flexbox approach to create a layout that will resize the header width and content dimensions when a window is resized, but maintain the sidebar dimensions.
I found the following example from this Flexbox Approach to get me started, and it works as desired for the content div itself. But after looking it over, I'm unsure how to make it work as described with a fixed width, 100% height sidebar.
CSS from example:
<style>
html, body {
height: 100%;
margin: 0
}
.box {
display: flex;
flex-flow: column;
height: 100%;
}
.box .row {
border: 1px dotted grey;
}
.box .row.header {
flex: 0 1 auto; /* The above is shorthand for: flex-grow: 0, flex-shrink: 1, flex-basis: auto */ }
.box .row.content {
flex: 1 1 auto;
}
.box .row.footer {
flex: 0 1 40px;
}
</style>
HTML from example:
<div class="row header">
<p><b>header</b> <br /> <br />(sized to content)</p>
</div> <div class="row content">
<p> <b>content</b> (fills remaining space) </p>
</div>
<div class="row footer">
<p><b>footer</b> (fixed height)</p>
</div>
</div>
The following codepen example gave me the information I needed to get my layout working:
http://codepen.io/JosephSilber/pen/HqgAz
CSS:
.header {
height: 50px;
}
.body {
position: absolute;
top: 50px;
right: 0;
bottom: 0;
left: 0;
display: flex;
}
.sidebar {
width: 140px;
}
.main {
flex: 1;
display: flex;
flex-direction: column;
}
.content {
flex: 1;
display: flex;
overflow: auto;
}
.box {
min-height: -webkit-min-content;
display: flex;
}
.column {
padding: 20px;
border-right: 1px solid #999;
}
.column > div {
height: 2000px;
background: red;
}
.column:nth-child(2) > div {
height: auto;
}
/* All of these are just for this demo's design. */
body {
font-family: sans-serif;
font-size: 20px;
line-height: 50px;
text-transform: uppercase;
font-weight: bold;
}
.header {
text-align: center;
color: #fff;
background: #444;
}
.sidebar {
background: #666;
padding: 4px 20px;
color: #fff;
}
.page-header {
padding: 6px 20px;
background: #004141;
color: #fff;
font-size: .8em;
}
.content {
background: #ddd;
}
HTML:
<div class="header">Main header</div>
<div class="body">
Move this: <div class="sidebar">Sidebar</div>
<div class="main">
<div class="page-header">Page Header. Content columns are below.</div>
<div class="content">
<div class="box">
<div class="column">
<div>Column 1</div>
</div>
<div class="column">
<div>Column 1</div>
</div>
<div class="column">
<div>Column 1</div>
</div>
</div>
</div>
</div>
To here: <div class="sidebar">Sidebar</div>
</div>
To get the sidebar on the right side, I simply moved <div class="sidebar">Sidebar</div>to just above the closing div tag for the .body class.

Using margin on flex items

I was under the impression that a margin can be added to flex items/children, and flexbox should automatically take that into account and calculate the correct spacing between the items.
I can't seem to get this working as I would like though.
Fiddle here: https://jsfiddle.net/dba5ehcw/1/
.flex-item{
border: 1px solid blue;
box-sizing: border-box;
height: 160px;
width: 50%;
}
So each flex item at the moment is half the width of the container, and they flow nicely next to each other.
I would like to be able to add a margin of say, 1em to the flex-items in order to give them some breathing room, but in doing so, they become larger than the 50% and no longer stack next to each other on the same line because they are too wide.
Is there a way to use margin on the flex-items and have the flexbox container take this into account and adjust (decrease) their widths accordingly?
There are multiple ways to do this:
Use calc:
.flex-item {
width: calc(50% - 2em);
margin: 1em;
}
.flex-container {
border: 1px solid red;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 320px;
}
.flex-item {
border: 1px solid blue;
box-sizing: border-box;
height: calc(160px - 2em);
width: calc(50% - 2em);
margin: 1em;
}
<div class="flex-container">
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
Use nested boxes:
.flex-item {
width: 50%;
display: flex;
}
.flex-item > div {
border: 1px solid blue;
flex: 1;
margin: 1em;
}
.flex-container {
border: 1px solid red;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 320px;
}
.flex-item {
height: 160px;
width: 50%;
display: flex;
}
.flex-item > div {
border: 1px solid blue;
flex: 1;
margin: 1em;
}
<div class="flex-container">
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
</div>
Place each row in a nowrap container, and use a positive flex-shrink factor
.row {
display: flex;
}
.flex-item {
width: 50%;
margin: 1em;
}
.flex-container {
border: 1px solid red;
width: 320px;
}
.row {
height: 160px;
display: flex;
}
.flex-item {
border: 1px solid blue;
width: 50%;
margin: 1em;
}
<div class="flex-container">
<div class="row">
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
<div class="row">
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
<div class="row">
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
</div>
Don't use width. Instead, force line-breaks at the right places, and use flex: 1 to make the elements grow to fill remaining space.
.flex-item {
flex: 1;
}
.line-break {
width: 100%
}
.flex-container {
border: 1px solid red;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 320px;
}
.flex-item {
border: 1px solid blue;
box-sizing: border-box;
height: calc(160px - 2em);
flex: 1;
margin: 1em;
}
.line-break {
width: 100%;
}
<div class="flex-container">
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="line-break"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="line-break"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
You need to do it with padding - which, when in border-box mode does not make the container larger than it's specified width - not margin, and a nested flex div. This is how all flexbox-based grid systems work. Code below:
.flex-container{
border: 1px solid red;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 320px;
}
.flex-item{
padding:1em;
box-sizing: border-box;
height: 160px;
width: 50%;
display:flex;
}
.flex-item>div {
border: 1px solid blue;
flex: 1 1 auto;
}
<div class="flex-container">
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
</div>
instead of using margins, try adding a gap on your flex container
.flex-container {
display: flex;
gap: 1em
}
flex-item {
width 50%
}
Try this : -
.flex-container {
border: 1px solid red;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 320px;
}
.flex-item {
justify-content: space-around;
margin: 1%;
background: red;
border: 1px solid blue;
box-sizing: border-box;
height: 160px;
width: 48%;
}
<div class="flex-container">
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
I believe I was trying to achieve the same thing, from my understanding, with the addition that I wanted the two boxes to stack on top of each other when the viewport gets small enough (when viewing on mobile/tablet).
For some reason I thought this would be way easier as I saw something similar in a Bootstrap tutorial video I watched but I think he was using row and col classes, with a g gutter class, and not Flex.
Anyway, HTML:
<div class="d-flex flex-wrap flex-half-screen-responsive">
<div class="col-lg-6">
Lorem Ipsum
</div>
<div class="col-lg-6">
Lorem Ipsum
</div>
</div>
CSS:
.flex-half-screen-responsive {
margin: -0.5em;
}
.flex-half-screen-responsive > * {
flex: 1 1 48%;
margin: 0.5em;
}
I don't like how I have to specify that hardcoded 48% value but it seems to work just as I want it so whatever; spent way too much time on this already lol. Anyway I hope this helps someone looking for the same behavior.

Resources