Daniel Waltrip Daniel Waltrip - 29 days ago 7
CSS Question

Vertical list of elements, where each row shrinks its width to match the inner content

I want to create a vertical list, where each row shrinks its width to perfectly contain its inner content (versus the default

div
behavior of expanding its width to fill the container).

I'd like to do this with only one HTML element for each row (no extra wrapping divs).

The following code does exactly what I want, but it doesn't work in Safari (bug?).



.container {
margin: 10px;
border: 2px solid #999;
padding: 5px;
display: flex;
flex-direction: column;
width: 300px
}
.row-item {
padding: 5px;
margin: 5px;
border: 1px solid green;
/* this will shrink the width to the inner content
in Chrome and Firefox, but not in Safari */
margin-right: auto;
}

<div class='container'>
<div class='row-item'>Item #1</div>
<div class='row-item'>Another Item...</div>
<div class='row-item'>Item No. 3</div>
</div>





Here is a codepen with the above code: http://codepen.io/anon/pen/woKYqx

I know that it is trivial to solve this problem by adding a wrapping div and then using
display: inline-block
on the inner element (or several other similar solutions).

However, it seems like it should be possible to solve this without adding extra HTML elements. It is a fairly simple layout.

Is there a cross-browser way to do this with a single HTML element for each row?

Answer

You're using margin-right: auto to remove extra space on the row, forcing the flex item to take the width of its content.

As you've noted, this method fails in Safari.

So instead use align-self: flex-start on the flex items:

.row-item {
  padding: 5px;
  margin: 5px;
  border: 1px solid green;
  align-self: flex-start;  /* NEW */
  /* margin-right: auto;  (can be kept or removed) */
}

OR, align-items: flex-start on the flex container.

.container {
  margin: 10px;
  border: 2px solid #999;
  padding: 5px;
  display: flex;
  flex-direction: column;
  align-items: flex-start; /* NEW */
  width: 300px
}