I'm trying to create a form with a variable number of form fields that would expand horizontally. Each field would have a minimum width of 300 px, but would expand to fill the row if there is extra space. If there is not enough space for each field at 300px, then it would wrap to another row. Flexbox would be the perfect solution for this. However, I also want there to be a variable width container for submit & cancel buttons that is fixed on the right side of the first row. (See the attached illustration.)
How can I create this fixed, right-aligned container that Flexbox would flow around? Can this be done with Flexbox alone? Would CSS Grid (or a combination of Flexbox & Grid) be helpful here? Example code would be appreciated.
I think your best solution is to use float and inline-block. then you can adjust sizing considering media query
body>.container {
background-color: #FFFFFF;
margin: auto;
margin-top: 24px;
padding: 0px;
}
.container {
border: solid 1px #F00;
font-size:0;
}
.box {
box-sizing: border-box;
border: 1px solid #000;
text-align: center;
vertical-align: middle;
min-height: 36px;
width: calc(25% - 10px);
min-width: 200px;
display:inline-block;
margin: 5px;
font-size:initial;
}
.box.buttons {
float:right;
}
<link data-require="bootstrap-css#*" data-semver="4.0.0-alpha.4" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.4/css/bootstrap.min.css" />
<div class="container">
<div class="box buttons">
<button>Submit</button>
<button>Cancel</button>
</div>
<div class="box a">Box A</div>
<div class="box b">Box B</div>
<div class="box c">Box C</div>
<div class="box e">Box E</div>
<div class="box f">Box F</div>
</div>
After some experimentation, I found that this is possible with CSS Grid. Here is the basic layout:
HTML:
<div class="auto-fit">
<div class="A">A</div>
<div class="B">B</div>
<div class="C">C</div>
<div class="D">D</div>
<div class="E">E</div>
<div class="F">F</div>
<div class="G">G</div>
<div class="H">H</div>
<div class="I">I</div>
<div class="J">J</div>
<div class="K">K</div>
<div class="L">L</div>
<div class="M">M</div>
<div class="buttons"><button>Submit</button><button>Cancel</button></div>
</div>
CSS:
div.auto-fit {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 10px;
}
div.auto-fit > div {
background-color: #fff;
border-radius: 3px;
padding: 15px;
font-size: 14px;
}
div.buttons {
grid-column: -1/-2;
grid-row: 1/2;
}
Here is a jsfiddle that shows it in action: https://jsfiddle.net/lobo78/5ufqdm4y/22/
Related
I'm trying to set border for a grid layout with a vertical spanned cell. As suggested in some other forum posts, I set "margin-left: -1px" to avoid double borders (in the case a cell with right border is next to a cell with left border)
The problem is that only one part of the vertical border of the spanned cell is displayed, see example. How to solve it?
.grid-container {
display: grid;
grid-template-columns: 90px 90px 90px;
grid-template-rows: 24px 24px 27px;
}
.grid-item {
background-color: #EEEEEE;
}
<div class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item" style="grid-row: span 3; border: solid 1px;margin-left: -1px;">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
</div>
In this particular case the problem is arising because the 5 and 8 items come after the 3.
One way round it is to give the 3 item a higher z-index.
.grid-container {
display: grid;
grid-template-columns: 90px 90px 90px;
grid-template-rows: 24px 24px 27px;
}
.grid-item {
background-color: #EEEEEE;
}
<div class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item" style="grid-row: span 3; border: solid 1px;margin-left: -1px;z-index: 1;">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
</div>
I would give .grid-container the same background color as the border color and use a gap of equal width as the border for the cell mentioned, or any other cell requiring the same.
In your specific case, with 3 * 90px grid columns, you will need to limit the width of the grid with width: max-content.
Snippet, moved the inline style to .special for clarity...
.grid-container {
background-color: black; /* added */
width: max-content; /* added */
display: grid;
grid-template-columns: 90px 90px 90px;
grid-template-rows: 24px 24px 27px;
}
.grid-item {
background-color: #EEEEEE;
}
.grid-item.special { /* special cell */
grid-row: span 3;
border: solid 1px;
gap: 1px;
}
<div class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item special">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
</div>
I'm sure this kind of question was asked before, but I really can't describe it exactly and concisely enough to let the search engine to understand me. So here we go:
To better explain my question I'm writing the code in tailwind style here. A stack snippet is also attached below:
<div class="root w-screen h-screen flex flex-col">
<div class="header h-[72px] w-full bg-red shrink-0"></div>
<div class="content grow">
<!-- a whole lot of content, very tall, height > 2000 px -->
</div>
</div>
In this example, I would like to limit the height of the entire div.root to 100vh. However, because div.content is very tall, it expands the body that it shows a vertical scrollbar.
Well this is fairly easy to overcome, I only need to add scroll-y-auto to div.content. So the body scrollbar disappears, and div.content shows a vertical scrollbar. Perfect.
However later on, I decided to split div.content into two columns: both column shall have its own vertical scrollbar. Intuitively I changed the code to:
<div class="root w-screen h-screen flex flex-col">
<div class="header h-[72px] w-full bg-red shrink-0"></div>
<div class="content grow">
<div class="left overflow-y-auto">
<!-- a whole lot of content, very tall, height > 2000 px -->
</div>
<div class="right overflow-y-auto">
<!-- a whole lot of content, very tall, height > 2000 px -->
</div>
</div>
</div>
But this does not work at all, as the attached snippet demonstrates. body got its scrollbar back, but not div.left or div.right.
I've explored several ways to solve this problem. In the end the best solution I got was to set the height of div.content to calc(100% - 72px). This works perfectly, but I understand it's only because I know the exact height of div.header is fixed at 72px.
Was I doing something wrong here? What's the most elegant way to solve this kind of problem?
body {
margin: 0;
}
.root {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
height: 72px;
width: 100%;
background-color: red;
flex-shrink: 0;
}
.content {
flex-grow: 1;
display: flex;
flex-direction: row;
}
.very-tall-content {
background-color: green;
height: 2400px
}
.left, .right {
flex-grow: 1;
margin: 0 4px;
overflow-y: auto;
}
<div class="root">
<div class="header"></div>
<div class="content">
<div class="left">
<p class="very-tall-content"></p>
</div>
<div class="right">
<p class="very-tall-content"></p>
</div>
</div>
</div>
Allright, try this one maybe it fixed your problem :)
instead of using flex for .root use grid. down here we have a
header with minimum height of 72px and if it's content overloads, the
header will auto-fit them
:root {
--header-min-height: 72px;
}
body {
margin: 0;
}
.root {
display: grid;
grid-template-rows: minmax(var(--header-min-height), auto) 1fr;
height: 100vh;
}
.header {
grid-row: 1;
background: darkcyan;
}
.content {
grid-row: 2;
display: flex;
background-color: palegreen;
overflow-y: hidden;
}
.content>div {
flex-grow: 1;
overflow-y: auto;
}
.white-space {
height: 3000px;
}
<div class="root">
<div class="header"></div>
<div class="content">
<div class="left">
Left Side
<div class="white-space"></div>
Left Side
</div>
<div class="right">
Right Side
<div class="white-space"></div>
Right Side
</div>
</div>
</div>
here's the example if it overloads.
:root {
--header-min-height: 72px;
}
body {
margin: 0;
}
.root {
display: grid;
grid-template-rows: minmax(var(--header-min-height), auto) 1fr;
height: 100vh;
}
.header {
grid-row: 1;
background: darkcyan;
}
.content {
grid-row: 2;
display: flex;
background-color: palegreen;
overflow-y: hidden;
}
.content>div {
flex-grow: 1;
overflow-y: auto;
}
.white-space {
height: 3000px;
}
.row {
display: flex;
flex-direction: row;
width: fit-content;
}
.item {
background: rgba(255, 255, 255, .5);
width: fit-content;
padding: 10px;
margin: 5px;
border-radius: 5px;
}
<div class="root">
<div class="header">
<div class="row">
<div class="item">test</div>
<div class="item">test</div>
<div class="item">test</div>
</div>
<div class="row">
<div class="item">test</div>
<div class="item">test</div>
<div class="item">test</div>
</div>
<div class="row">
<div class="item">test</div>
<div class="item">test</div>
<div class="item">test</div>
</div>
</div>
<div class="content">
<div class="left">
Left Side
<div class="white-space"></div>
Left Side
</div>
<div class="right">
Right Side
<div class="white-space"></div>
Right Side
</div>
</div>
</div>
I've got two columns in a parent container of 600px width. The children’s character length dictates the column width (weighted split). However, as both columns become increasingly similar in their width, a balanced (50/50 split) layout should be preferred, illustrated below.
Is it possible to achieve this kind of layout in flexbox or grid, without javascript? I imagine determining string length and switching css properties according to a threshold would be an option that I don't want to go down.
My intention isn't to create a single type of split but rather to make the layout respect both splits conditionally.
.container {
outline: 1px solid red;
width: 100%;
height: 60px;
display: flex;
}
.child {
padding: 0.5px;
outline: 1px solid black;
display: grid;
place-content: center;
}
.grow {
flex-grow: 1; /* flexible split */
}
.balanced {
width: 100%; /* 50-50 split */
}
<div class="container">
<div class="child grow">
asdasdasdasd
</div>
<div class="child grow">
asdaassdasdasdsdasdasd
</div>
</div>
<div class="container">
<div class="child balanced">
asdasdasdasd
</div>
<div class="child balanced">
asdaassdasdasdsdasdasd
</div>
</div>
Just add a maximum width?
.container {
outline: 1px solid red;
width: 100%;
height: 60px;
display: flex;
}
.child {
padding: 0.5px;
outline: 1px solid black;
display: grid;
place-content: center;
}
.grow {
flex-grow: 1;
max-width:50%;
}
.balanced {
width: 100%;
/* 50-50 split */
}
<div class="container">
<div class="child grow">
asdasdasdasd
</div>
<div class="child grow">
asdaassdasdasdsdasdasd
</div>
</div>
<div class="container">
<div class="child balanced">
asdasdasdasd
</div>
<div class="child balanced">
asdaassdasdasdsdasdasd
</div>
</div>
Isn't flex-grow alone solving directly your problem?
See the snippet:
.container {
outline: 1px solid red;
width: 100%;
height: 60px;
display: flex;
}
.child {
padding: 0.5px;
outline: 1px solid black;
display: grid;
flex-grow: 1; /* flexible split */
place-content: center;
}
<div class="container">
<div class="child">
asdasd
</div>
<div class="child">
asdaassdasdasdsdasdasd
</div>
</div>
<div class="container">
<div class="child">
asdasdasdasd
</div>
<div class="child">
asdaassdasdasdsdasdasd
</div>
</div>
<div class="container">
<div class="child">
asdaassdasdasdsdasdasd
</div>
<div class="child">
asdaassdasdasdsdasdasd
</div>
</div>
In the meantime I found the answer.
Setting flex-basis enforces a 50/50 split when column width is similar. The unequal split is respected by setting flex-grow and flex-shrink.
.parent {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 50%;
padding-right: 1em;
padding-left: 1em;
}
I want to have a responsive grid of elements of variable length. The grid should fill the available width of the containing element, with the number of columns varying depending on the width of the container. This is straightforward to achieve using CSS grids; however, I don't know how to add a vertical border between columns (i.e., only in the interior column gaps). The below simple demo manages to achieve a vertical border in the event that there are three columns:
https://codepen.io/yowzadave/pen/OYPvLd?editors=1100
html, body {
box-sizing: border-box
}
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(24rem, 1fr));
grid-column-gap: 0.5rem;
}
.item {
border-right: 1px solid black;
padding-right: 0.5rem;
}
.item:nth-child(3n) {
border-right: none;
padding-right: 0;
}
.box {
background-color: pink;
height: 2rem;
margin: 0.5rem;
}
<html>
<body>
<div class="container">
<div class="item"><div class="box"></div></div>
<div class="item"><div class="box"></div></div>
<div class="item"><div class="box"></div></div>
<div class="item"><div class="box"></div></div>
<div class="item"><div class="box"></div></div>
<div class="item"><div class="box"></div></div>
</div>
</body>
</html>
...but in the event that the browser is wider or narrower, the borders will be misplaced. Is there a way to correctly place the borders at all browser widths?
You can use pseudo element on all the grid item where you will simply have overlap and be sure to cover all the gaps:
html,
body {
margin: 0;
}
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
grid-column-gap: 0.5rem;
overflow:hidden; /* Hide the overflow */
position:relative;
}
.item {
box-sizing: border-box;
position:relative;
}
.item:before {
content:"";
position:absolute;
top:0;
right:-0.25rem; /* Half the gap */
height:100vh; /* Big height*/
width:1px;
background:#000;
}
.box {
background-color: pink;
height: 2rem;
margin: 0.5rem;
}
<div class="container">
<div class="item">
<div class="box"></div>
</div>
<div class="item">
<div class="box"></div>
</div>
<div class="item">
<div class="box"></div>
</div>
<div class="item">
<div class="box"></div>
</div>
<div class="item">
<div class="box"></div>
</div>
<div class="item">
<div class="box"></div>
</div>
</div>
I have a scroll container that's usually the size of the whole screen. Inside of it I place dynamic content. So I won't know which height it has or how many elements will be inserted.
Now I want to layout it like this:
if there is enough space, I want the whole content vertically centered inside the scroll container
if the total height of the content exceeds the height of the scroll container, I want the container to just scroll the contents like there was no centering.
I created an example where I tried to solve this problem with flexbox. With content height less than the container height it works like intended. But when the content exceeds the container height, due to justify-content, some elements of the content are cut off:
You can see on the image that the scroll container's scrollTop is all the way at the top, yet elements 1 & 2 aren't visible.
I'd like to know if there is a CSS only solution. A JS solution I could do myself but that's not what I'm after. If it's not possible, that's okay too.
.container {
display: inline-block;
width: 300px;
height: 300px;
border: 2px solid red;
overflow-y: auto;
margin: 1rem 0;
display: flex;
flex-direction: column;
justify-content: center;
}
.block {
width: 80%;
height: 3rem;
margin: 1rem auto;
background: blue;
flex-shrink: 0;
color: #fff;
text-align: center;
}
<div class="container">
<div class="block">1</div>
</div>
<div class="container">
<div class="block">1</div>
<div class="block">2</div>
<div class="block">3</div>
</div>
<div class="container">
<div class="block">1</div>
<div class="block">2</div>
<div class="block">3</div>
<div class="block">4</div>
<div class="block">5</div>
<div class="block">6</div>
<div class="block">7</div>
<div class="block">8</div>
</div>
Try applying the overflow to an inner containing div like so:
.container {
display: inline-block;
width: 300px;
height: 300px;
border: 2px solid red;
margin: 1rem 0;
display: flex;
flex-direction: column;
justify-content: center;
}
.inner {
overflow-y: auto;
}
.block {
width: 80%;
height: 3rem;
margin: 1rem auto;
background: blue;
flex-shrink: 0;
color: #fff;
text-align: center;
}
<div class="container">
<div class="inner">
<div class="block">1</div>
</div>
</div>
<div class="container">
<div class="inner">
<div class="block">1</div>
<div class="block">2</div>
<div class="block">3</div>
</div>
</div>
<div class="container">
<div class="inner">
<div class="block">1</div>
<div class="block">2</div>
<div class="block">3</div>
<div class="block">4</div>
<div class="block">5</div>
<div class="block">6</div>
<div class="block">7</div>
<div class="block">8</div>
</div>
</div>