4

I have a set of spans, each of which has "white-space: nowrap" to keep them individually non-breaking. I want them to have some visual spacing separating them if they are next to each other on a line, but if the browser decides to line-break, I don't need or want left or right margins at the ends of the lines.

For example:

<div class="container">
   <span id="s1">User Option 1</span>
   <span id="s2">User Option 2</span>
   <span id="s3">User Option 3</span>
   <span id="s4">User Option 4</span>
   <span id="s5">User Option 5</span>
   <span id="s6">User Option 6</span>
   <span id="s7">User Option 7</span>
   <span id="s8">User Option 8</span>
</div>

In a wide window, I would like to see:

User Option 1 ________ User Option 2 ________ User Option 3 ________ User Option 4
User Option 5 ________ User Option 6 ________ User Option 7 ________ User Option 8

(I have supplied underscore characters to show where I would like some space/padding/margin. I don't want to see the underscores, I want some spacing there.)

But I want the spacing only as needed, and for it to continue to behave nicely when the set of items wraps at different points. So, if the window is somewhat narrower:

User Option 1 ________ User Option 2 ________ User Option 3
User Option 4 ________ User Option 5 ________ User Option 6
User Option 7 ________ User Option 8

Or narrower still:

User Option 1 ________ User Option 2
User Option 3 ________ User Option 4
User Option 5 ________ User Option 6
User Option 7 ________ User Option 8

I know if I just give everything a margin-left, then there is unwanted space reserved at the left of everything:

________ User Option 1 ________ User Option 2 ________ User Option 3
________ User Option 4 ________ User Option 5 ________ User Option 6
________ User Option 7 ________ User Option 8

If I give margin-left within a :not:first-child selector, it actually looks a bit worse:

User Option 1 ________ User Option 2 ________ User Option 3
________ User Option 4 ________ User Option 5 ________ User Option 6
________ User Option 7 ________ User Option 8

If I switch to right-margin, everything always looks good:

User Option 1 ________ User Option 2 ________ User Option 3 ________
User Option 4 ________ User Option 5 ________ User Option 6 ________
User Option 7 ________ User Option 8 ________

because you don't see the right margins that are unneeded.

But there is still a cause for complaint in that these elements are reserving that space off to the right and they therefore will "word-wrap" sooner than necessary because of it, so they still have the unwanted feature of wasting space to the right.

Is there something in CSS that really says what I want to say?

Colloquially, I would say: "margin-right-if-sibling-rendered-to-my-right" ?

Johannes
  • 64,305
  • 18
  • 73
  • 130
Mark Goldfain
  • 731
  • 2
  • 8
  • 24
  • I agree with @Highdef , this is a link I use all the time: [A Complete Guide to Flexbox | CSS-Tricks](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) – Mark Wheeler Dec 19 '17 at 00:29
  • instead of :not:first-child you may use :nth-child(4n+0) to select every fourth element. – Radek Dec 19 '17 at 00:29

2 Answers2

2

The margin space / wrapping problem has been solved by CSS Grid.

You create separation between grid items using the following properties:

  • grid-row-gap
  • grid-column-gap
  • grid--gap (the shorthand for both properties above)

These properties create space between items, but never between items and the container.

jsFiddle demo

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  grid-auto-rows: 50px;
  grid-gap: 10px;
  border: 1px dashed black;
}

span {
  background-color: lightgreen;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
<div class="container">
  <span id="s1">User Option 1</span>
  <span id="s2">User Option 2</span>
  <span id="s3">User Option 3</span>
  <span id="s4">User Option 4</span>
  <span id="s5">User Option 5</span>
  <span id="s6">User Option 6</span>
  <span id="s7">User Option 7</span>
  <span id="s8">User Option 8</span>
</div>

Then, if you want space between the container and the items, add padding to the container.

jsFiddle demo

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  grid-auto-rows: 50px;
  grid-gap: 10px;
  padding: 10px;  /* added */
  border: 1px dashed black;
}

span {
  background-color: lightgreen;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
<div class="container">
  <span id="s1">User Option 1</span>
  <span id="s2">User Option 2</span>
  <span id="s3">User Option 3</span>
  <span id="s4">User Option 4</span>
  <span id="s5">User Option 5</span>
  <span id="s6">User Option 6</span>
  <span id="s7">User Option 7</span>
  <span id="s8">User Option 8</span>
</div>

More details here:

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
0

I think for the particular layout described, using CSS Grid is way more suitable than flexbox. Let's take a look at how it renders the layout:

  • We make the parent container (grid-container) have the display:grid property.
  • We justify the contents using space-between so the grid items are at their extremities (Unlike flexbox the grid-item doesn't float to the extreme right when space is available).
  • We define the template columns with a fixed width after which it will wrap down to the next row.

.grid-container {
  display: grid;
  grid-template-columns: repeat(auto-fit, 300px);
  justify-content: space-between;
  text-align: center;
}

.grid-container>* {
  background-color: gray;
  color:white;
  border:2px solid black;
  margin:2px 0px 2px 0px;
}
<div class="grid-container">
   <span id="s1">User Option 1</span>
   <span id="s2">User Option 2</span>
   <span id="s3">User Option 3</span>
   <span id="s4">User Option 4</span>
   <span id="s5">User Option 5</span>
   <span id="s6">User Option 6</span>
   <span id="s7">User Option 7</span>
   <span id="s8">User Option 8</span>
</div>
  • How can you allow each span to stretch to the width of its parent if e.g. the text is in one span does not wrap and therefore occupies a maximum width of the parent? In your example, no matter how much text a span contains, it will never stretch – tonix Jul 29 '19 at 11:24