7

Given an inline (or inline-block) element with text of variable length, and another element to the right of the first one that acts as a kind of badge, is there a way to prevent a line break between the last word of the first element and the second element? Both elements occupying the same line is fine; a line break occurring in the text of the first element is also fine; but a line break between the two elements is undesirable. Here is an illustration explaining what I mean.

line break diagram

Is there a way to do this? I tried to have the two elements as spans and put a non-breaking space between them, but that didn't work.

UPDATE: Here's a quick and dirty Codepen example: http://codepen.io/anon/pen/LkzBQJ

html:

<h1>
  <span class="title-text">
    This is some text
  </span><span class="badge">yo!</span>
</h1>
<h1>
  <span class="title-text">
    This is some broken text
  </span><span class="badge">yo!</span>
</h1>

css:

h1 {
  width: 350px;
}

.badge {
  color: #f6511d;
  display: inline-block;
  border: 1px solid #f6511d;
  border-radius: 3px;
  font-size: 0.8em;
  padding: 0.1em 0.2em;
  line-height: 0.97em;
  margin-left: 0.4em;
  vertical-align: 1px;
}

UPDATE2: In my particular case, both the text in the first element, and the badge have to be rendered dynamically, using JavaScript. So Ricardo’s solution below (wrap the last word of the text and the badge in a span with white-space: nowrap), although working, will not be very easy to implement.

azangru
  • 2,644
  • 5
  • 34
  • 55

5 Answers5

5

Check this! This line <h1>This is some text</h1><span class="badge">yo!</span> must be in one line to work.

https://codepen.io/lemonjelly/pen/rNNvLGE

  • 1
    This makes the job, but the key is not only the one line markup, but the `h1 {display: inline}` as well (it makes the default block element to be inline). Note that it works with no other value than `inline`, so in case you need to set `width` on the badge element, you have to use padding hack. – dakur Feb 11 '21 at 16:04
1

SOLUTION:

The solution I could come up with is creating some sort of fix, wrapping text with the badge in a span and using the css property white-space: nowrap;.


JSFiddle


CODE SNIPPET:

.row {
  display: flex;
  counter-reset: paragraph;
}
.col {
  width: 50%;
  padding: 1em;
}
.col--left {
  background-color: #011627;
  padding-right: 0;
}
.col--right {
  background-color: #F71735;
  border-left: 2px dotted #ddd;
}
.col p {
  color: #fff;
}
.col p::before {
  counter-increment: paragraph;
  content: counter(paragraph)". ";
}
.badge-fix {
  display: inline-block;
  white-space: nowrap;
}
.badge {
  display: inline-block;
  padding: .2em .6em .3em;
  font-size: 75%;
  font-weight: 700;
  line-height: 1;
  color: #fff;
  text-align: center;
  white-space: nowrap;
  vertical-align: baseline;
  border-radius: .25em;
}
.badge--royalblue {
  background-color: royalblue;
}
.badge--tomato {
  background-color: tomato;
}
.badge--crimson {
  background-color: crimson;
}
<div class="row">
  <div class="col col--left">
    <p>
      This is <span class="badge-fix">some text
      <span class="badge badge--royalblue">badge</span></span>
    </p>
    <p>
      This is some <span class="badge-fix">reasonably long
      <span class="badge badge--tomato">badge</span></span>
    </p>
    <p>
      This is some <span class="badge-fix">longish text
      <span class="badge badge--crimson">badge</span></span>
    </p>
  </div>
  <div class="col col--right">

  </div>
</div>
Ricky Ruiz
  • 25,455
  • 6
  • 44
  • 53
  • So what you are basically saying is, wrap the last word of the text along with the badge in a span with class `white-space: nowrap`. This solution seems rather difficult to maintain in a situation when the text and the badge are rendered dynamically by javascript — the code will then need to find the last word of the text, wrap it in a span, and then render the badge inside that span. I really hope there is a cleaner solution. – azangru Jul 10 '16 at 22:11
  • You never specified you were dynamically rendering the text and the badge with JS... – Ricky Ruiz Jul 10 '16 at 22:12
  • Edit your question if this is a requirement, my solution perfectly works with what is currently specified. – Ricky Ruiz Jul 10 '16 at 22:15
  • Added another update to my question to include the JS context. If there is no cleaner CSS-based solution, I will accept your answer. – azangru Jul 10 '16 at 22:30
  • Hopefully there is, but I can't think of any other way with pure CSS. – Ricky Ruiz Jul 10 '16 at 22:31
1

This problem is called a widow in typesetting. There are 3 ways to fix this.

Use the widows css property

Only break the last two words together.

<h1 style="widows: 2;">
  <span>This is some broken text</span>
  <span class="badge">yo!</span>
</h1>

Caveat: Not supported by Firefox (https://caniuse.com/?search=widows) and seems to only work properly with page breaks and column breaks

Use a &nbsp; character

Add a "physical" non-breaking space character, and only that character, between the last word and the badge.

<h1 style="widows: 2;">
  This is some broken text&nbsp;<span class="badge">yo!</span>
</h1>

If you have to do this with multi-line code, you can use HTML comments to avoid breaking spaces.

<h1 style="widows: 2;">
  This is some broken text<!--
  -->&nbsp;<span class="badge">yo!</span>
</h1>

Caveat: It's ugly

Wrap the last word and the badge in a white-space: nowrap span.

<h1 style="widows: 2;">
  This is some broken <span style="white-space: nowrap;">text <span class="badge">yo!</span></span>
</h1>

Caveat: Not always possible if you are dealing with dynamically generated code

lhermann
  • 490
  • 6
  • 11
  • 2
    Looks like NBSPs don't work if the following element is an svg, but I used that element in my answer below – ak0000 Jan 16 '23 at 22:56
1

You can do this with modern CSS, by declaring text-wrap: pretty. See the answer at https://stackoverflow.com/a/76701845/2076595.

(Yes, this question is a duplicate but I don’t have enough points to cast any “this is a duplicate question” vote)

Bramus
  • 1,732
  • 14
  • 19
  • I am having a deja vu moment! I could have sworn I've seen your answer to my question a month or so ago, in which you said that `text-wrap: pretty` will be available in Chrome 117, and I thanked you and said I will check back when my Chrome updates to 117. But now that answer that I remember seeing is in response to a different question. Very puzzling. But thank you again for suggesting a modern CSS solution. – azangru Aug 15 '23 at 22:24
0

You can add padding to the text and a negative margin:

<h1>
  <span class="title-text" style="padding-right: 15px;">
    This is some text
  </span><span class="badge" style="margin-left: -15px;">yo!</span>
</h1>
<h1>
  <span class="title-text" style="padding-right: 15px;">
    This is some broken text
  </span><span class="badge" style="margin-left: -15px;">yo!</span>
</h1>

This works for even dynamically generated content, unlike having to make a tag around the last word of the text and the image.

(Based on an answer I saw here: https://stackoverflow.com/a/25857961/5899236)

ak0000
  • 137
  • 5