Explanation
You can exploit fact that trailing and line trailing white space automatically collapses:
document.write(
'word<b style="background: red; outline: 1px solid blue;"> </b>'
.repeat(42)
);
As you can see there are red spaces with blue outlines between words, but the very last and and two at line ends lack the red area because it's width collapsed to zero: that is the white-space collapsing in effect.
It is possible to adjust width with word-spacing
and use pseudo element instead, so setting inline ::after { content: ' '; word-spacing: 2em; }
gives you wide inline rectangle that can have decorated backgrounds or borders but disappears when it is not between words.
Simplified example
Simplified use case (from https://codepen.io/myf/pen/dyOzpZM, tested just in 2021-02 evergreen Firefox and Chromium, will not work in pre-Chromium Edge; for more robust example see the second snippet below):
ul {
text-align: center;
padding: 0;
}
li {
display: inline;
}
li::after {
/*
This has to be space, tab or other
breakable white-space character:
*/
content: " ";
word-spacing: 1em;
background-image: linear-gradient(
-0.2turn,
transparent 0 calc(50% - 0.03em),
currentcolor 0 calc(50% + 0.03em),
transparent 0
);
}
/*
That's it: just inline text
with styled ::after spaces
that collapse at line breaks
and at the end of the element.
That's basically how spaces work in text.
*/
/*
Unrelated whimsical effects:
*/
body { background: #456; color: #fed; min-height: 100vh; margin: 0; display: flex; align-items: center; }
ul { --dur: 3s; font-family: Georgia, serif; font-size: min(7vw, calc(100vh / 7)); margin: 0 auto; position: relative; padding: 0 1em; -webkit-text-fill-color: #999; text-transform: capitalize; animation: poing var(--dur) infinite alternate ease-in-out; }
@keyframes poing { from { max-width: 3.4em; } to { max-width: min(19em, calc(100vw - 2em)); color: lime; } }
ul::before, ul::after { -webkit-text-fill-color: currentcolor; position: absolute; top: 50%; transform: translatey(-50%); animation: calc(var(--dur) * 2) calc(var(--dur) * -1.5) infinite forwards linear; }
ul::before { content: "☜"; left: 0; animation-name: a !important; }
ul::after { content: "☞"; right: 0; animation-name: b !important; }
@keyframes a { 50% { content: "☛"; } }
@keyframes b { 50% { content: "☚"; } }
ul:hover, ul:hover::before, ul:hover::after { animation-play-state: paused; }
<ul>
<li>foo</li>
<li>bar</li>
<li>baz</li>
<li>gazonk</li>
<li>qux</li>
<li>quux</li>
</ul>
It uses flat list with single word items, so is not very relevant for real-world usage.
More realistic example with elements highlights
nav {
text-align: center;
padding-right: 1em; /* = li::after@word-spacing */
}
ul {
display: inline;
margin: 0;
padding: 0;
}
li {
display: inline;
/*
white-space: nowrap should be moved to child A
because IE fails to wrap resulting list completely
*/
}
li::before {
content: ' ';
/*
this content is important only for Chrome in case
the HTML will be minified with *no whitespaces* between </li><li>
*/
}
li::after {
content: ' ';
/*
this is actual placeholder for background-image
and it really must be space (or tab)
*/
white-space: normal;
word-spacing: 1em;
/*
= nav@padding-right - this actually makes width
*/
background-image: radial-gradient(circle, black, black 7%, transparent 15%, transparent 35%, black 45%, black 48%, transparent 55%);
background-size: 1em 1em;
background-repeat: no-repeat;
background-position: center center;
opacity: 0.5;
}
/*
no need to unset content of li:last-child::after
because last (trailing) space collapses anyway
*/
a {
white-space: nowrap;
display: inline-block; /* for padding */
padding: 1em;
text-decoration: none;
color: black;
transition-property: background-color;
transition-duration: 500ms;
}
a:hover {
background-color: #ccc;
}
/*
For demonstrative purposes only
Give items some content and uneven width
*/
nav:hover > ul > li {
outline: 3px dotted rgba(0,0,255,.5);
outline-offset: -3px;
}
nav:hover > ul > li::after {
opacity: 1;
background-color: rgba(255, 0, 0, .5);
}
nav:hover > ul > li:hover {
outline-style: solid;
}
nav:hover > ul > li:hover::after {
background-color: cyan;
}
nav:hover > ul > li > a {
outline: 3px solid rgba(0,255,0,.5);
outline-offset: -3px;
}
nav > ul {
counter-reset: c;
}
nav > ul > li {
counter-increment: c;
}
nav > ul > li > a::before {
content: counter(c, upper-roman) '. ';
letter-spacing: .3em;
}
nav > ul > li > a::after {
content: ' item ' counter(c, lower-roman);
word-spacing: .3em;
letter-spacing: .1em;
transform: translatex(.1em);
display: inline-block;
}
<nav>
<ul><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#"></a></li>
</ul>
</nav>
<!-- For demonstrative purposes is content of links made by CSS
-->
(Originally from https://jsfiddle.net/vnudrsh6/7/) This proof-of-concept uses background-image of "eventually colapsing" CSS generated content space after each <li>
. Tested in 2016 in Firefox, Chrome and IE11.
Obviously you might need to use some character or more complex shape as divider. Naturally you can use (vector) background-image, and you can even use text in SVG, although making it correspond with surrounding ("real") text might be quite daunting.
Bare-bones with SVG
Minimal working example without any "list" element, with textual ❦
fleuron:
body {
text-align: center;
}
b::after {
content: " ";
word-spacing: 16px;
background: url("data:image/svg+xml;charset=utf-8,\
<svg xmlns='http://www.w3.org/2000/svg' \
viewBox='-3,-15,16,16'>\
<text>❦</text>\
</svg>");
}
<b>foo</b> <b>bar</b> <b>baz</b> <b>gazonk</b> <b>qux</b> <b>quux</b>
<b>foo</b> <b>bar</b> <b>baz</b> <b>gazonk</b> <b>qux</b> <b>quux</b>
<b>foo</b> <b>bar</b> <b>baz</b> <b>gazonk</b> <b>qux</b> <b>quux</b>
Other notable answers:
- Same technique used in overlooked Liphtier's answer from 2014. (I've found that one long after posting this answer, so to my disappointment I cannot claim my answer is was first.)
- Same technique used few months later in Tom Robinson's answer.
- Impressive flex-box-based solution with plain over-extending borders and different spacing in gfullam's answer.
- For left-aligned list you can set overflow hidden and cut overlapping real character in pseudo element: Oriol's answer
and Nathan Arthur's.
.