I find this impossible to achieve

I'm trying to horizontally align an arbitrary number of <li> elements so they're all the same width as the widest one, and so they wrap to a new line when needed. I need to do this without hardcoding any values in the CSS. Here is my CodePen: https://codepen.io/cos4ni2s/pen/KwdBVyz?editors=1100 EDIT The <ul> container should be as wide as its content. Something like width: fit-content;
18 Replies
vince
vince•3mo ago
Just based off your description, sounds like a grid would be the best bet Maybe something like:
display: grid;
grid-template-columns: repeat(autofit, minmax(320px, 1fr));
display: grid;
grid-template-columns: repeat(autofit, minmax(320px, 1fr));
I don't think this is going to work OOTB since it'll make all columns *at least 320px wide, but you can probably play around with the values to get something you want (and I know there's a difference between autofit vs autofill, so might be worth checking out the nuances to see if they matter to this scenario)
Dave W
Dave W•3mo ago
What about flex or in-line flex, and aligning the items space between? I think that's what it's called
Arcsin Cospix
Arcsin CospixOP•3mo ago
Thank you for taking the time to help me. There is only one problem (for me personally) which is the hardcoded 320px value. EDIT It doesn't work with: width: fit-content; on the container <ul> element.
vince
vince•3mo ago
Good idea but with flex items the widths will be sized based off the children / li elements, so they won't have a uniformed width. Grid sizes based off what you extrinsically set (e.g with the grid-template-column property) Right, you'll have to mess around with that value and see if another value will work. But also why can't you use hardcoded values? I think it'll help if you explain what you're trying to do practically (without being technical about it) So you're just trying to make the list horizontal and have it span the width of the page, and then wrap to a new line if needed?
Arcsin Cospix
Arcsin CospixOP•3mo ago
Because I'm making a template for future projects and I don't know what would the correct value be for each project. I want it to stay responsive and universal. I'm trying to make a list horizontal, whose width is the width of the elements it contains, the elements it contains should have equal widths which is equal to the widest elements among them, and then wrap to a new line if needed
Dave W
Dave W•3mo ago
Checking the MDM page, to me, it looks like justify-content: space-around will do what the OP is looking for. The description on MDM says "The items are evenly distributed within the alignment container along the main axis. The spacing between each pair of adjacent items is the same. The empty space before the first and after the last item equals half of the space between each pair of adjacent items. If there is only one item, it will be centered."
Arcsin Cospix
Arcsin CospixOP•3mo ago
Thank you for your suggestion but its about the width of each element not the spacing between them.
vince
vince•3mo ago
I'm not really sure unfortunately if something like this is achievable (with CSS, definitely some JS solution exists). I'm sure someone more knowledgeable at CSS might be able to chime in 👀. My best suggestion is to do what I did above, that way they have all the same width and it wraps to a new line (row) if needed... but unfortunately I believe you'll have to hardcode a minimum width (320px). Doesn't look like min-content works there
Dave W
Dave W•3mo ago
Maybe it could be done using css variables on the :root element? I'm just learning about css variables, but if you could do it that way, you'd only have to change it in one place, and it could be used in multiple different classes.
vince
vince•3mo ago
Could be a good idea. You wouldn't have to even add it to the :root, you could add it to the ul or class itself.
ul {
--item-width: 150px; /* Minimum item width */

display: grid;
grid-template-columns: repeat(autofit, minmax(var(--item-width), 1fr));
}
ul {
--item-width: 150px; /* Minimum item width */

display: grid;
grid-template-columns: repeat(autofit, minmax(var(--item-width), 1fr));
}
^ This is still hardcoded, but at least you're giving the minimum width a descriptive name Even in templates, you'll still have to change values around, so I say just run with this for now until you find something better. But obviously I'm not aware of your exact usecase If you can't find a CSS solution for this what you could do is find the widest widthed element via JS and then set the --item-width css variable to the largest width
Dave W
Dave W•3mo ago
That could a viable option. The JS to do that should be light weight, and you won't have to touch it after you get it working the way you want.
Arcsin Cospix
Arcsin CospixOP•3mo ago
Thank you guys for your valuable help! I thought it is possible in CSS.
vince
vince•3mo ago
It might be but I'm not sure
Dave W
Dave W•3mo ago
@Arcsin Cospix It still might be, but I'm not sure. I'm far from a CSS expert.
Arcsin Cospix
Arcsin CospixOP•3mo ago
I hope so
Dave W
Dave W•3mo ago
I think you need to get a CSS expert to weigh in
Kevin Powell
Kevin Powell•3mo ago
Yeah, this isn't really something that's solveable with CSS. Element's aren't aware of the size of other elements, other than the total width of their parent. With flexbox, it's impossible because the flex algorithm looks at the size of each element to determine how big it should be. You could use JS to loop over the elements, find the widest one, and set that as the min-width for each element. If I were doing something like this, I'd take @vince's approach of using the auto grid, and having a custom property to be able to adjust it as needed. If you don't want the items to stretch, you can drop the minmax(), and just do something like repeat(auto-fit, var(--size)) and they'll be locked in at that size, and wrap as needed.
Arcsin Cospix
Arcsin CospixOP•3mo ago
Thank you, Kevin, for confirming my suspicions. This haunted me for days, trying to solve it using pure CSS.

Did you find this page helpful?