How to set column width to content width in CSS Grid - css

I have a list of blocks, that I want to place in two rows, now I use grid:
li:nth-of-type(2n) {
grid-row-start: 2;
}
li:nth-of-type(2n + 1) {
grid-row-start: 1;
}
<ul>
<li>Item</li>
<li>Item</li>
<li>Item1111</li>
<li>content content</li>
</ul>
You can see, what I get:
I want to set width of every grid cell to width of its content, like on the pic
Is it even possible?

If you don't need the rows to align you can try flexbox instead of grid:
ul {
display: flex;
flex-flow: row wrap;
list-style: none;
padding: none;
max-width: 9rem;
}
li {
padding: 0.1em;
border: 1px solid currentColor;
}
<ul>
<li>Content 1</li>
<li>Item 1</li>
<li>Item 2</li>
<li>content 2</li>
</ul>

set grid rows and columns to auto grid-template-rows: auto auto; grid-template-columns: auto auto;
set width of the li's width: max-content;
.grid-container {
display: grid;
grid-template-rows: auto auto;
grid-template-columns: auto auto;
width: 250px;
gap:10px;
}
li {
list-style: none;
padding: 1rem;
background-color: #EDF6FF;
border-radius:10px;
width: max-content;
color:#2E4665;
}
<ul class="grid-container">
<li>Item11111</li>
<li>Content11111</li>
<li>Item</li>
<li>Content</li>
</ul>

Related

Nested Flexbox Wrapping [duplicate]

This question already has answers here:
When flexbox items wrap in column mode, container does not grow its width
(9 answers)
Closed 1 year ago.
I am trying to create a flexbox container that contains a list of items in a row format and then each of those items will be a flexbox with items in a wrapping column format. However, it seems that the first containers rows do not expand to fit the contents of the wrapping columns and end up overlapping each other.
Demo
I want the end result to look like this:
.flex-group {
display: flex;
}
.flex-container {
padding: 0;
margin: 0;
list-style: none;
border: 1px solid silver;
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 500px;
}
.red li {
background: red;
}
.gold li {
background: gold;
}
.blue li {
background: deepskyblue;
}
.flex-item {
padding: 5px;
width: 100px;
height: 100px;
margin: 10px;
line-height: 100px;
color: white;
font-weight: bold;
font-size: 2em;
text-align: center;
}
<div class="flex-group">
<ul class="flex-container blue">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
<li class="flex-item">4</li>
<li class="flex-item">5</li>
<li class="flex-item">6</li>
<li class="flex-item">7</li>
<li class="flex-item">8</li>
</ul>
<ul class="flex-container red">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
</ul>
<ul class="flex-container gold">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
<li class="flex-item">4</li>
<li class="flex-item">5</li>
</ul>
<div>
So at the end of the day, I'm looking to have a non-wrapping row where each of the child elements (dynamic amount) can contain a set of column wrapping elements (dynamic amount). Note: You can almost get the solution if you make the .flex-container have flex-direction: row but I need flex-direction: column since order does matter in this scenario. The main container needs to have a fixed height and each child container can have a dynamic width (due to wrapping elements causing them to grow horizontally).
If you will use grid, you can use this:
.flex-group {
display: flex;
}
.flex-container {
padding: 0;
margin: 0;
list-style: none;
border: 1px solid silver;
display: grid;
grid-template-rows: repeat(3, auto);
gap: 10px;
grid-auto-flow: column;
height: 500px;
}
.red li {
background: red;
}
.gold li {
background: gold;
}
.blue li {
background: deepskyblue;
}
.flex-item {
padding: 5px;
width: 100px;
height: 100px;
margin: 10px;
line-height: 100px;
color: white;
font-weight: bold;
font-size: 2em;
text-align: center;
}
<div class="flex-group">
<ul class="flex-container blue">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
<li class="flex-item">4</li>
<li class="flex-item">5</li>
<li class="flex-item">6</li>
<li class="flex-item">7</li>
<li class="flex-item">8</li>
</ul>
<ul class="flex-container red">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
</ul>
<ul class="flex-container gold">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
<li class="flex-item">4</li>
<li class="flex-item">5</li>
</ul>
<div>
There are a lot of similar question but no answers. I think a workaround can satisfy your needs. Include a script to calculate the width depending on the height you assign to each flex-container and the flex-item height:
check it out here
I had to fix the height to 400px to get the same output as yours but this script will calculate number of cols and lines needed. Couldn't get the padding properly though so I set a variable that will get me the desired output
document.querySelectorAll('.flex-container').forEach((e)=>{
let p = 10 ;
let childCount = e.childElementCount;
let childHeight = e.children[0].clientHeight ;
let childWidth = e.children[0].clientWidth ;
let lines = Math.round((e.clientHeight / (e.children[0].clientHeight + 2*p))) ;
let cols = Math.round(childCount / lines) ;
let width = cols * ( 2*p + childWidth );
e.style.width = width+"px";
});
Still if you want a column based display for your items similar to Pinterest then you need to specify in advance the number of columns and use Masonry Layout :
.masonry-container {
column-count: 3;
column-gap: 15px;
}
.masonry-item {
display: inline-block;
width: 100%;
}
I'm not sure I got this, but it sounds like you don't want your columns to be in 'wrap' mode.
You may try to change those values:
.flex-container {
/* leave the rest of what was here*/
flex-wrap: nowrap;
height: 100%;
}
This is what I thought you want to achieve:

css: sort divs/lis into two columns

Suppose there are two kinds of elements, let's say words and numbers. They should be sorted into two columns. For the example I'm using listitems, but I don't mind changing to divs or something else if that helps.
<div id="container">
<ul>
<li>foo</li>
<li>bar</li>
<li class="num">1</li>
<li class="num">2</li>
<li>baz</li>
</ul>
</div>
Right now I'm achieving this somewhat by using margins, with the following css:
#container {
width: 500px;
margin: auto;
}
li {
float: left;
width: 200px;
background-color: #eee;
margin-bottom: 2px;
margin-right: 300px;
}
.num {
float: right;
margin-right: 0px;
margin-left: 300px;
}
resulting in
http://jsfiddle.net/he13vug4/
What would be a more elegant way to achieve this?
How could I make the numbers "start" (in terms of vertical position) already besides "bar" or even "foo"? (The next word should, however, only start below the numbers). That is, I'd like
or
instead of
They should be sorted into two columns, side by side
Do you need support for older browsers? If not, CSS Grid could help you
Result
#container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-flow: dense;
gap: 10px;
width: 500px;
margin: auto;
list-style: none;
}
li {
background-color: #eee;
}
.word {
grid-column: 1;
}
.num {
grid-column: 2;
}
<ul id="container">
<li class="word">foo</li>
<li class="word">bar</li>
<li class="num">1</li>
<li class="num">2</li>
<li class="word">baz</li>
</ul>
And same code on JSFiddle
Use only left floating and play with clear and margin like below:
#container {
width: 500px;
margin: auto;
}
ul {
list-style:none;
}
li {
float: left;
width: 200px;
background-color: #eee;
margin-bottom: 2px;
}
li + li:not(.num) {
clear:both;
}
li + li.num {
margin-left:10px;
}
li.num + li.num {
margin-left:210px;
}
<div id="container">
<ul>
<li>foo</li>
<li>bar</li>
<li class="num">1</li>
<li class="num">2</li>
<li>baz</li>
<li>baz</li>
<li class="num">1</li>
<li class="num">2</li>
<li class="num">3</li>
<li>baz</li>
<li>baz</li>
</ul>
</div>

Css grid auto-flow minmax with outer columns

Hi I'd like to use grid's repeat and auto-fill but with an outer columns left and right set to 1vw (vertical width unit) to create some padding.
Not sure how to set the grid items to use the auto-fill columns and ignore the columns set to 1vw.
body, html {
margin: 0;
padding: 0;
}
ul {
margin: 0;
padding: 0;
display: grid;
grid-template-columns: 1vw repeat(auto-fill, minmax(200px , 1fr)) 1vw;
grid-gap: 0.5vw;
}
li {
text-align: center;
list-style: none;
background: peachpuff;
padding: 0.5em;
}
<ul>
<li>Grid Item</li>
<li>Grid Item</li>
<li>Grid Item</li>
<li>Grid Item</li>
<li>Grid Item</li>
<li>Grid Item</li>
</ul>
You're overthinking this. Just give the ul left/right margin of 1vw and remove those grid-columns completely. Or even simpler, a width of 98vw and margin:auto
body, html {
margin: 0;
padding: 0;
}
ul {
margin: auto;
width:98vw;
padding: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px , 1fr));
grid-gap: 0.5vw;
}
li {
text-align: center;
list-style: none;
background: peachpuff;
padding: 0.5em;
}
<ul>
<li>Grid Item</li>
<li>Grid Item</li>
<li>Grid Item</li>
<li>Grid Item</li>
<li>Grid Item</li>
<li>Grid Item</li>
</ul>

Flex: wrapped items with same width as the rest

I'm using flex to create a multi column list that adjusts the number of columns based on the width of the container.
The problem I found is that if I want to use the full width of the parent by setting flex-grow to 1, the items in the last wrapped row are misaligned, since they try to fill the parent.
I found two workarounds that don't work for me: one used media queries, which I can't use because the parent is not the same width of the viewport; the other was using columns, but I can't use it because it causes issues with outlines being cut off and wrapped, which I have in my real setup.
Q:
Is there a way to make all items have the same width while filling the parent on full rows?
* {
box-sizing: border-box;
}
.wrapper {
border: 1px solid red;
width: 50%;
}
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
margin: -1em -1em 0 0;
}
li {
border: 1px solid black;
padding: 1em;
margin: 1em 1em 0 0;
flex: 1 1 10em;
}
<div class="wrapper">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
</ul>
</div>
https://plnkr.co/edit/0u2QxcdLkDfhwV3zASrM
Resize until you get 2 items in the last row.
Add a few extra flex items and make them "invisible" by setting their height/padding/border to 0/none.
Based on how many columns you'll need, it takes one less for it to work.
* {
box-sizing: border-box;
}
.wrapper {
border: 1px solid red;
width: 100%;
max-width: 800px;
}
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
margin: -1em -1em 0 0;
}
li {
border: 1px solid black;
padding: 1em;
margin: 1em 1em 0 0;
flex: 1 1 10em;
}
li:nth-last-child(-n+3) {
height: 0;
border: none;
padding: 0;
margin: 0 1em 0 0;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<div class="wrapper">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
</body>
</html>
If all but the extra elements has a content, you could also use this CSS rule (thanks to Rick Hitchcock)
li:empty {
height: 0;
border: none;
padding: 0;
margin: 1em 1em 0 0;
}
If the amount of columns never will be more than 3, one can use pseudo elements. (Well, the columns can be more as long as there will never be more than 2 items missing on the last row)
* {
box-sizing: border-box;
}
.wrapper {
border: 1px solid red;
width: 100%;
max-width: 800px;
}
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
margin: -1em -1em 0 0;
}
li {
border: 1px solid black;
padding: 1em;
margin: 1em 1em 0 0;
flex: 1 1 10em;
}
ul::before, ul::after {
content: '';
height: 0;
border: none;
padding: 0;
margin: 0 1em 0 0;
flex: 1 1 10em;
order: 999; /* they need to be last */
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<div class="wrapper">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
</ul>
</div>
</body>
</html>
FLex will fail without a bit of javascript to fill or not the eventual space on last line.
Grid , in the futur will be 100% efficient for this kind of beahavior layout.
Snippet below that you can test in FF and Chrome if you have enable "experimental CSS".( https://igalia.github.io/css-grid-layout/enable.html )
* {
box-sizing: border-box;
}
.wrapper {
border: 1px solid red;
width: 50%;
min-width:12.25em;
}
ul {
list-style: none;
margin: 0;
padding: 0 0 1em 1em;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(10em, 1fr))
}
li {
border: 1px solid black;
padding: 1em;
margin: 1em 1em 0 0;
}
<div class="wrapper">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
</ul>
</div>
Of course this is not the answer you expected, but it is about what CSS can (will) do.
You can keep on going with flex and a few tricks, but i believe the best would be flex + a bit of javascript :) at this time.
to start learning and testing the grid CSS system , see :
A tutorial https://css-tricks.com/snippets/css/complete-guide-grid/
browser supports http://caniuse.com/#search=grid
a Polyfill https://github.com/FremyCompany/css-grid-polyfill/
http://gridbyexample.com/browsers/
It depends on the actual situation, but you can prevent flex-grow for the last one, two, three (whatever, I made it for the last two) children with the appropriate selectors:
* {
box-sizing: border-box;
}
.wrapper {
border: 1px solid red;
width: 50%;
}
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
margin: -1em -1em 0 0;
}
li {
border: 1px solid black;
padding: 1em;
margin: 1em 1em 0 0;
flex: 1 1 10em;
}
li:last-child, li:nth-last-child(2) {
flex-grow: 0;
}
<div class="wrapper">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
</ul>
</div>
It's like we need one more value in the "flex" rule for items: a boolean for "make all items uniform dimension". Like flex: grow shrink basis uniform

Overflow scroll and nowrap unwanted scrollbar

I have a drop down menu which I'd like to have the text content control the width of. Sometimes this menu will have a max-height with a scroll-bar.
If you run the snippet below you'll see the last word of the longest piece of text will wrap due to the presence of the scroll-bar. Using white-space: nowrap; will cause the text to go underneath the y-scroll-bar an make an x-scroll-bar to appear.
What's the best way to make this work? There's plenty I could do with JS (detect width of the text and apply a width to the container etc) but looking for CSS only solutions.
.drop-menu {
postion: relative;
}
ul, il {
margin: 0;
padding: 0;
list-style: none;
}
.drop-items {
overflow: auto;
max-height: 100px;
position: absolute;
border: 1px solid #ccc;
}
.drop-items li {
border-bottom: 1px solid #ccc;
/* white-space: nowrap; */
}
<div class="drop-menu">
<span>Menu</span>
<div class="drop-items">
<ul>
<li>Item</li>
<li>Item item item item item item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
</div>
</div>
Try adding .drop-items { left: 0; right: 0; }. Note, the width of the container will no longer decided by the content.
By the way you have a typo postion, which should be position.
jsFiddle
.drop-menu {
position: relative;
}
ul,
il {
margin: 0;
padding: 0;
list-style: none;
}
.drop-items {
overflow: auto;
max-height: 100px;
position: absolute;
left: 0;
right: 0;
border: 1px solid #ccc;
}
.drop-items li {
border-bottom: 1px solid #ccc;
white-space: nowrap;
}
<div class="drop-menu">
<span>Menu</span>
<div class="drop-items">
<ul>
<li>Item</li>
<li>Item item item item item last</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
</div>
</div>
Or, just use some right padding, e.g. .drop-items li { padding-right: 30px; } to make the room for the horizontal scrollbar. According to this article - "on modern browsers generally the scrollbars are 17 pixels wide but I would still recommend that you continue to use 20 pixels as your spec when working on wireframes or creating mock-ups".
You can even do overflow-y: auto; overflow-x: hidden; if needed.
jsFiddle
.drop-menu {
position: relative;
}
ul,
il {
margin: 0;
padding: 0;
list-style: none;
}
.drop-items {
overflow: auto;
max-height: 100px;
position: absolute;
border: 1px solid #ccc;
}
.drop-items li {
border-bottom: 1px solid #ccc;
white-space: nowrap;
padding-right: 30px;
}
<div class="drop-menu">
<span>Menu</span>
<div class="drop-items">
<ul>
<li>Item</li>
<li>Item item item item item last</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
</div>
</div>

Resources