46

How do you get an inline-block element to fit its content width, if the content line-breaks because of screen width?

<!-- parent inline-block -->
<div style='display: inline-block;'>
    <div style='display: inline-block; width:200px;'></div>
    <!--
        If this child line breaks, 
        two 200px wide blocks are stacked vertically.
        That should make the parent width 200px,
        but the parent stays much wider than that
    -->
    <div style='display: inline-block; width:200px;'></div>
</div>

I can't think of how to phrase the question so it sounds easy, but I put together a simple JSFiddle illustrating.

#wide {
  position: relative;
  width: 100%;
  border: 1px solid black;
  padding: 5px;
}
#narrow {
  position: relative;
  width: 175px;
  border: 1px solid black;
  padding: 5px;
}
.wrap {
  display: inline-block;
  border: 1px solid green;
  margin: auto;
}
.inlineblock {
  display: inline-block;
  vertical-align: top;
  background: red;
  min-width: 100px;
  min-height: 100px;
  border: 1px solid black;
}
<section id='wide'>
  <div class='wrap'>
    <div class='inlineblock'></div>
    <div class='inlineblock'></div>
  </div>
</section>
<p>
  When the red inline-blocks are forced to line break, how do you make a parent with display:inline-block (the green border) snap to fit? How do you get rid of all the extra horiztonal space in the lower green bordered div?
</p>
<section id='narrow'>
  <div class='wrap'>
    <div class='inlineblock'></div>
    <div class='inlineblock'></div>
  </div>
</section>
Oriol
  • 274,082
  • 63
  • 437
  • 513
user2782001
  • 3,380
  • 3
  • 22
  • 41

2 Answers2

30

You can't. By default, inline-block elements have a shrink-to-fit width:

The shrink-to-fit width is:
min(max(preferred minimum width, available width), preferred width).

Then,

  • When preferred minimum width <= preferred width <= available width, the width will be the preferred width, as you desire.
  • When available width <= preferred minimum width <= preferred width, the width will be the preferred minimum width, as you desire.
  • When preferred minimum width <= available width <= preferred width, the width will be the available width, even if you don't like it.

If you really don't want this, I guess you could add a resize event listener with JS, and set the desired width manually.

Armen Michaeli
  • 8,625
  • 8
  • 58
  • 95
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • @Paulie_D Yes, floats also use the shrink-to-fit width (in fact it's defined for them). And basically all atomic inline-level boxes do the same. – Oriol Jan 25 '16 at 16:11
  • 8
    Thanks...that was my understanding too. Odd that this hasn't been solved yet...seems to be a common design request. – Paulie_D Jan 25 '16 at 16:12
  • 2
    @Paulie_D Maybe it's intentional, to avoid abrupt changes of the width and possible circular definitions. But it would be nice to have this in non-problematic cases. – Oriol Jan 25 '16 at 16:21
  • @Paulie_D BTW, flex items use max-content instead of fit-content/shrink-to-fit. I have self-answered [Should flex item overflow the flex container instead of breaking lines?](http://stackoverflow.com/q/35005024/1529630) to explain this. – Oriol Jan 26 '16 at 00:44
20

inline-block elements can't achieve this layout - as demonstrated by @Oriol - but,

CSS Grid can achieve this layout.

body {
  margin: 0;
}

ul {
  display: inline-grid;
  grid-template-columns: repeat(auto-fit, 100px);
  min-width: 50vw;
  
  /* decorative properties */
  grid-gap: 10px;
  padding: 0;
  list-style: none;
  border: 5px solid salmon;
  box-sizing: border-box;
  
  /* center the grid */ 
  position: relative;
  left: 50vw;
  transform: translateX(-50%);
}
li {
  background-color: lightblue;
  height: 100px;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
  <li>7</li>
  <li>8</li>
  <li>9</li>
  <li>10</li>
  <li>11</li>
  <li>12</li>
  <li>13</li>
  <li>14</li>
  <li>15</li>
  <li>16</li>
  <li>17</li>
  <li>18</li>
  <li>19</li>
  <li>20</li>
  <li>21</li>
  <li>22</li>
</ul>

Codepen demo (Be sure to resize)

Basically the relevant code boils down to this:

ul {
  display: inline-grid; /* (1) */
  grid-template-columns: repeat(auto-fit, 100px); /* 100px = column width - (2) */
  min-width: 50vw; /* or any user-defined min-width (3) */
}
  1. Make the container element an inline-grid container. This will cause the grid to 'shrink-wrap' its contents - so that the grid width will never be wider than it's contents.

  2. Set the grid with a responsive layout (The auto-fill / auto-fit value is used for responsive layouts). If there is no room in a row to fit the next item - it wraps to the next row.

When a responsive layout is used together with an inline-grid - the grid width will equal the width of one item of the grid. (when no width/min-width is explicitly set - like this)

  1. Set the container with a min-width which represents (at most - one partial item less than) the desired maximum width for the container.

So if the given min-width exactly fits in a certain number of items - that means that this will also be the maximum width of the grid because the next item will wrap.

If however the min-width doesn't exactly correspond to the width of 'n' items in that it also fits in part of the n+1th item - in this case the grid will slightly expand to exactly fit the n+1th item - with the n+2th item wrapping to the next row.

Félix Paradis
  • 5,165
  • 6
  • 40
  • 49
Danield
  • 121,619
  • 37
  • 226
  • 255
  • 1
    If we wanted to fit as many `100px` columns as would fit in the parent container, could we use `min-width: calc(100% - 100px);` – Zeal Dec 09 '18 at 06:38
  • 1
    Unfortunately for me this doesn't appear to be applicable to items having a dynamic width within the container, for instance with text, and also with a dynamic maximum width. Thought if I wrapped every word in a span there may be some way of using this. – addMitt Sep 23 '20 at 17:32
  • 1
    Yeah, I guess this doesn't work well with dynamically sized (width-wise) content, since A) You have a grid structure and B) you'll somehow need to specify the grid-columns width. – carl-johan.blomqvist Jan 11 '21 at 16:41
  • When I try to modify this with `max-width:870px;min-width:320px` (with the aim of it trying to show as many columns as possible, up to 8 (8 * 100px + 7 * 10px gap), it doesn't shrink down. I can make the window 600px wide and it'll still be 870px wide. What am I doing wrong? – Codemonkey Mar 25 '21 at 12:38