Reputation: 12944
I need to create a horizontal layout where one block takes all available space and the other ones shrink to fit their content.
For example:
<div class="grid">
<div class="expand">Long text label</div>
<div class="shrink">Button</div>
</div>
A more complex example with two rows (an actual grid):
<div class="grid">
<div class="row">
<div class="shrink">...</div>
<div class="expand">...</div>
<div class="shrink">...</div>
<div class="shrink">...</div>
</div>
<div class="row">
<div class="shrink">...</div>
<div class="expand">...</div>
<div class="shrink">...</div>
<div class="shrink">...</div>
</div>
</div>
My requirements:
I am targeting Android and iOS smartphones.
I have tried to adapt the code from this answer but I could not make it work for multiple rows. Also, the source code must be out of order, which is confusing (though not blocking for my use case). Here's a jsfiddle: http://jsfiddle.net/k3W8L/
Upvotes: 54
Views: 78358
Reputation: 19629
You need to use grid-template-column
and set the size of column you want to shrink-to-fit as auto
, and specify the size of at least one other column using the fr
unit.
Example: To recreate the sidebar-content layout, where the sidebar is collapsible,
-------------------------------
| Sidebar | Content |
-------------------------------
you can create the grid as:
.grid {
display: grid;
...
grid-template-columns: auto minmax(0, 1fr); // see note below
grid-template-areas: "sidebar content";
}
.sidebar {
grid-area: sidebar;
}
.content {
grid-area: content;
}
See the codepen here for a demo & code: https://codepen.io/khs/pen/vegPBL You can click on the navbar to see auto-sizing in action.
Note: One thing I've learnt since writing this answer is that in most cases, and especially in a fixed-height/fixed-width layout, you should use minmax(0, 1fr)
instead of 1fr
. The reason is that 1fr
is actually a shorthand for minmax(auto, 1fr)
. This breaks when used for fixed layouts with longer content. See this question for more details: Why does minmax(0, 1fr) work for long elements while 1fr doesn't?
Hence I've updated my answer to reflect the fact. The minmax
might be a little verbose, but it's almost always what you want for this kind of layout.
Context: I bumped into this question recently while learning to use grid in my app. With some help from ilyaigpetrov's answer, I was able to get a shrink-to-fit column sizing to work. Their answer doesn't give much explanation though, so I thought I'd add this:
Upvotes: 84
Reputation: 3883
I don't know whether my code is valid, but it gets the job done: either with grid or with flex.
.container {
display: grid;
grid-template-columns: 1fr auto auto;
}
.one {
background-color: pink;
}
.two {
background-color: yellow;
}
.three {
background-color: lightgreen;
}
/* Now the same but with flex. */
.whole-row {
grid-column: 1 / -1;
}
.flexy {
display: flex;
border: 1px solid red;
}
.flexy > .one {
flex-grow: 1;
}
.flexy > .two,
.flexy > .three {
/* flex-grow: 0; <- This value is a default. */
}
<div class="container">
<div class="one">one</div>
<div class="two">two</div>
<div class="three">three</div>
<div class="one">one</div>
<div class="two">two</div>
<div class="three">three</div>
<div class="whole-row flexy">
<div class="one">one</div>
<div class="two">two</div>
<div class="three">three</div>
</div>
</div>
Upvotes: 1
Reputation: 12944
I found the answer to my own question while trying to create a concise example.
It makes use of table layout:
.grid {
display: table;
table-layout: auto;
width: 100%;
}
.row {
display: table-row;
}
.expand {
display: table-cell;
width: 100%;
max-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.shrink {
display: table-cell;
white-space: nowrap;
}
The width: 100%
in .expand
is what makes the block fill all available space, thus fulfilling the first two requirements.
Note that the width
in .grid
simply sets the width of the entire grid, you can use any value here.
For some reason, putting max-width: 0
in .expand
prevents the block to grow more than the available space, a smallish value like 100px
will also do. I discovered this by chance and I don't know why it works.
Here's the jsfiddle: http://jsfiddle.net/fr253/
This answer helped me getting started.
Upvotes: 2