How does Bootstrap add "/" to breadcrumb components? - html

Bootstrap breadcrumb components in both v3 and v4 add a "/" to list items:
http://getbootstrap.com/components/#breadcrumbs
https://v4-alpha.getbootstrap.com/components/breadcrumb/
The docs say:
Separators are automatically added in CSS through ::before and
content.
So, I looked at the source code, the relevant section showing:
.breadcrumb-item + .breadcrumb-item::before {
display: inline-block;
padding-right: 0.5rem;
padding-left: 0.5rem;
color: #636c72;
content: "/";
}
However, the content: "/"; only exists on the breadcrumb-item rule. Yet, it seems to work when I follow the v3 docs, which don't require a breadcrumb-item class for items inside a list:
<ol class="breadcrumb">
<li class=''>Home</li>
<li class=''>Library</li>
<li class="active">Data</li>
</ol>
JSFiddle
Why does the above HTML with the above CSS result in / separators being added to items in a .breadcrumb list even though they don't have the .breadcrumb-item class and thus can't benefit from the content: "/" rule? Inspecting the output HTML in JSFiddle shows that no bootstrap javascript magic has added a .breadcrumb-item class to my html list items.

link to this style at github (bootstrap v3.3.7):
https://github.com/twbs/bootstrap/blob/v3.3.7/less/breadcrumbs.less
for (bootstrap v4.0.0):
https://github.com/twbs/bootstrap/blob/v4.0.0-alpha.6/scss/_breadcrumb.scss
(bootstrap v3.3.7 --> breadcrumbs.less)
//
// Breadcrumbs
// --------------------------------------------------
.breadcrumb {
padding: #breadcrumb-padding-vertical #breadcrumb-padding-horizontal;
margin-bottom: #line-height-computed;
list-style: none;
background-color: #breadcrumb-bg;
border-radius: #border-radius-base;
> li {
display: inline-block;
+ li:before {
content: "#{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space
padding: 0 5px;
color: #breadcrumb-color;
}
}
> .active {
color: #breadcrumb-active-color;
}
}
(bootstrap v4.0.0 --> _breadcrumb.scss)
.breadcrumb {
padding: $breadcrumb-padding-y $breadcrumb-padding-x;
margin-bottom: $spacer-y;
list-style: none;
background-color: $breadcrumb-bg;
#include border-radius($border-radius);
#include clearfix;
}
.breadcrumb-item {
float: left;
// The separator between breadcrumbs (by default, a forward-slash: "/")
+ .breadcrumb-item::before {
display: inline-block; // Suppress underlining of the separator in modern browsers
padding-right: $breadcrumb-item-padding;
padding-left: $breadcrumb-item-padding;
color: $breadcrumb-divider-color;
content: "#{$breadcrumb-divider}";
}
// IE9-11 hack to properly handle hyperlink underlines for breadcrumbs built
// without `<ul>`s. The `::before` pseudo-element generates an element
// *within* the .breadcrumb-item and thereby inherits the `text-decoration`.
//
// To trick IE into suppressing the underline, we give the pseudo-element an
// underline and then immediately remove it.
+ .breadcrumb-item:hover::before {
text-decoration: underline;
}
+ .breadcrumb-item:hover::before {
text-decoration: none;
}
&.active {
color: $breadcrumb-active-color;
}
}

In version 3 of Bootstrap, the separators are coming from:
.breadcrumb>li+li:before {
padding: 0 5px;
color: #ccc;
content: "/\00a0";
}
on line 6 of the included file breadcrumbs.less.
Hope this helps! :)

When you inspect the element, you get the style to be:
.breadcrumb > li + li:before {
padding: 0 5px;
color: #ccc;
content: "/\00a0";
}
So the above code targets all the descending <li> elements inside the .breadcrumb element.
The logic is pretty simple. The next element has a breadcrumb before.

There is a CSS rule like this:
.breadcrumb>li+li:before {
padding: 0 5px;
color: #ccc;
content: "/\00a0";
}

Related

How to insert separator between items except when wrapped (in CSS)? [duplicate]

This question expands upon 'Separators For Navigation' by asking, how it is possible to remove the separators at the line breaks cause by viewport size.
Wide Viewport
-> Item 1 | Item 2 | Item 3 | Item 4 | Item 5 <-
Small Viewport
-> Item 1 | Item 2 | Item 3 <-
-> Item 4 | Item 5 <-
Here is a fiddle that shows how a pipe remains at the line break:
Fiddle.
I'm interested in a css-only solution, but javascript is acceptable if it provides the only possible solution.
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></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></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 in few months younger Tom Robinson's answer.
gfullam's answer using flex-box, very impressive alternative with plain over-extending borders and different spacing due flex arrangement.
Oriol's answer for left-aligned list using overflow hidden and real character in pseudo.
A different solution from that same CSS: Last element on line seems like it would work here.
HTML:
<div>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
</div>
CSS:
div {overflow: hidden; margin: 1em; }
div ul { list-style: none; padding: 0; margin-left: -4px; }
div ul li { display: inline; white-space: nowrap; }
div ul li:before { content: " | "; }
(Fiddle)
If you have static width of your element you can calculate by the media-screen.
If not use script
body {
text-align: center;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
display: inline-block;
&:not(:last-child):after {
content: ' |';
}
}
#media screen and (max-width: 265px) {
li {
display: inline-block;
&:not(:last-child):after {
content: '';
}
}
}
Nice question. For the life of me, I can't think of a water-tight CSS-only solution I'm afraid...
I've modified an old solution to a similar question posted a while back: CSS: Last element on line. Funnily enough I was looking for a solution to another problem I had a while back and stumbled across this - been bookmarked since!
Here's a fiddle with my updates: https://jsfiddle.net/u2zyt3vw/1/
HTML:
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
CSS:
body {
text-align: center;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
display: inline-block;
&:not(:last-child):after {
content: ' |'
}
}
li.remove:after {
content: none;
}
jQuery:
$(window).on("resize", function () {
var lastElement = false;
$("ul > li").each(function() {
if (lastElement && lastElement.offset().top != $(this).offset().top) {
lastElement.addClass("remove");
}
lastElement = $(this);
}).last().addClass("remove");
}).resize();
NOTE - it works best onload at the moment, resizing causes a few issue even if I use toggleClass(). So keep pressing "Run" every time you resize the view. I'll work on it and get back to you..
My implementation with JavaScript: https://jsfiddle.net/u2zyt3vw/5/
Hit "Run" again after you've resized the window.
You can also add event listeners such as onresize. Here's the JS:
var listItems = document.getElementsByTagName("li");
var listItemsWidth = [];
var listItemsDistance = [];
for (let i = 0; i < listItems.length; i++) {
listItemsWidth[i] = listItems[i].offsetWidth;
listItemsDistance[i] = listItems[i].getBoundingClientRect().right;
}
for (let i = 0; i < listItems.length; i++) {
if (listItemsDistance[i] == Math.max.apply(null, listItemsDistance)) {
listItems[i].classList -= "notLast";
} else {
listItems[i].classList = "notLast";
}
}
I added the notLast class to all of your elements, and that's what contains the :after pseudo-element with the pipe. This script removes this class from the ones that are closer to the right edge of the container.
I also messed around with the :after pseudo-element and made it position:absolute; for dark reasons.

Primefaces datatable replacing pagination button icons

I need some help with Primefaces datatable pagination buttons. I'm replacing icons on pagination buttons with FontAwesome icons. Here is a CSS example what I did for one of them (I modified others the same way):
div.ui-paginator > a.ui-paginator-prev > .ui-icon.ui-icon-seek-prev {
text-indent: 0;
}
div.ui-paginator > a.ui-paginator-prev > .ui-icon.ui-icon-seek-prev::before {
font-family: FontAwesome;
content: "\f048";
}
I refreshed my page and I've noticed that icons get replaced but now a new character appears on the right side of each Font Awesome icon (F, P, N, and E).
I believe it has to do something with locales where N stands for Next, P is Previous, E is End and F is First. I've had the same problem with the calendar component and I modified my locales.js to solve that issue. I would also like to remove these pagination characters if possible.
How can I do that?
Is it possible to modify the (1 of 150) to my locale language as well?
EDIT 1
Html after replacement looks like this:
Old icons have been removed as described above with the same css procedure only different selectors. I am not using any custom paginators for this, only default Primefaces datatable pagination.
My DataTable xhtml tag is:
<p:dataTable id="DTableA" var="dataObject" value="#{dataTableBean.objectList}" paginator="true" rows="10" rowKey="#{dataObject.id}"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
rowsPerPageTemplate="5,10,15,25,50" reflow="true">
EDIT 2
Following #Jasper de Vries answer didn't resolve my issue because the posted answer is somewhat deprecated. Main selector is incorrect for Primefaces 6.2.
See the following selector:
.ui-paginator > span:before, .ui-sortable-column-icon:before {
font-family: FontAwesome; color: #fff;
}
.ui-paginator > span:before is incorrect. Should be .ui-paginator > a:before for Primefaces 6.2.
Also I had to replace sorting caret icons from suggested answer with these:
.ui-icon-carat-2-n-s.ui-icon-triangle-1-n:before {
content: "\f0d8";
}
.ui-icon-carat-2-n-s.ui-icon-triangle-1-s:before {
content: "\f0d7";
}
and resize them:
.ui-sortable-column-icon {
height: 20px;
}
And second part of my question
Is it possible to modify the (1 of 150) to my locale language as
well?
hasn't been answered yet.
SOLUTION
I've finally figured out what happened when I wrote css like this:
div.ui-paginator > a.ui-paginator-prev > .ui-icon.ui-icon-seek-prev {
text-indent: 0;
}
div.ui-paginator > a.ui-paginator-prev > .ui-icon.ui-icon-seek-prev::before {
font-family: FontAwesome;
content: "\f048";
}
Each icon for the pagination should be replaced with a FontAwesome icon inside the <a> tag and not inside the <span> tag as I did.
It's interesting that the icon class by Primefaces is placed inside the <span> tag and it confused me to think that an icon is placed inside it. Instead it's actually a wrapper tag for text I tried to hide which was already hidden by default. Setting text indent to 0 and adding an icon to that particular span exposes the hidden text.
<a href="#" class="ui-paginator-next ui-state-default ui-corner-all" aria-label="Next Page" tabindex="0">
<span class="ui-icon ui-icon-seek-next">N</span>
</a>
Modified selectors do the trick here. Sample CSS is available by #Jasper de Vries. My edited CSS for pagination goes like this:
.ui-paginator.ui-paginator-top.ui-widget-header.ui-corner-top,
.ui-paginator.ui-paginator-bottom.ui-widget-header.ui-corner-bottom,
.ui-paginator a,
.ui-paginator a:hover {
background: none;
background-image: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
text-shadow: none;
}
.ui-paginator > a {
width: 32px;
height: 32px;
}
.ui-paginator a,
.ui-paginator a:hover {
border: none;
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
padding: 3px 0px 0px 0px;
color: #fff;
}
.ui-paginator a {
background-color: #1E88E5;
}
.ui-paginator a:hover {
background-color: #1565C0;
}
.ui-paginator a,
.ui-sortable-column-icon:before {
font-family: FontAwesome;
}
.ui-sortable-column-icon.ui-icon {
background-image: none;
text-indent: 0;
margin: 0 0 0 .5em;
height: 20px;
}
.ui-paginator span span,
.ui-paginator a span {
display: none;
}
.ui-paginator .ui-paginator-first:before {
content: "\f048";
}
.ui-paginator .ui-paginator-prev:before {
content: "\f04a";
}
.ui-paginator .ui-paginator-next:before {
content: "\f04e";
}
.ui-paginator .ui-paginator-last:before {
content: "\f051";
}
.ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s.ui-icon-triangle-1-n,
.ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s.ui-icon-triangle-1-s {
margin: 0px 0px 0px 0px;
}
.ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s.ui-icon-triangle-1-n {
top: -2px;
}
.ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s.ui-icon-triangle-1-s {
top: 4px;
}
.ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s:before {
content: "\f0dc";
}
.ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s.ui-icon-triangle-1-n:before {
content: "\f0d8";
}
.ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s.ui-icon-triangle-1-s:before {
content: "\f0d7";
}
Thank you #Jasper de Vries and #Kukeltje for your help.

web-design: WHY doesn't :before scss work nested inside li unless I put it seperated?

okay
I have an rtl website. there is a ul with many li. the first words in each li are ltr a links.
What I wanted to do was simple. I want to have a frontAwesome icon before each li so I did the code normally
html
<ul class="vocab">
<li>CPU(central processing unit).: وحدة المعالجة المركزية</li>
<li>CPU(central processing unit).: وحدة المعالجة المركزية</li>
<li>CPU(central processing unit): وحدة المعالجة المركزية</li>
<li>CPU(central processing unit): وحدة المعالجة المركزية</li>
<li>CPU(central processing unit): وحدة المعالجة المركزية</li>
<li><span class="ltr">left ...</span></li>
</ul>
This is the scss
body {
margin: 0;
padding: 0;
background-color: #ffffff;
direction: rtl;
}
.ltr {
direction: ltr;
display: inline-block;
}
.vocab{
list-style: none;
width: 70%;
border-right: 3px solid rgba(77, 181, 56, 1);
background-color: rgba(77, 181, 56, .1);
padding: $p;
margin: 0 auto;
padding-right: 2 * $h1;
li {
padding-right: $h1;
:before{
content: "\f137";
font-family: FontAwesome;
display: inline-block;
margin-right: -$h1;
width: $h1;
}
}
}
four problems happened ..
I made some li with only text (no links). the ones with no link have no icon before them(unless I put them in a span).
and for some reason, if li has a links at the beginning, the icons became links?
as another test for my algorithm to have a ltr text between rtl. I made some without this class. the ones without ltr class worked great in terms of position. HOWEVER, the one with this class had its icon between the text ( I assume it's due to my way of putting the icon ).
4.the last thing is the text written directly into li has no icon.
here is the output:
output without solution solved
my solution is a bit weird. and it is what I need an explanation of HOW DID IT WORK?
what I did ..
I made ONE change in my scss.. I made :before into li:before and didn't nest it
.vocab{
list-style: none;
width: 70%;
border-right: 3px solid rgba(77, 181, 56, 1);
background-color: rgba(77, 181, 56, .1);
padding: $p;
margin: 0 auto;
padding-right: 2 * $h1;
li {
padding-right: $h1;
}
li:before{
content: "\f137";
font-family: FontAwesome;
display: inline-block;
margin-right: -$h1;
width: $h1;
}
}
the output solved
to sum up .. when I made :before nested inside li which is nested in .vocab .. it didn't work and actually gave up some weird output
BUT, when I made li:before nested inside .vocab (not :before nested with li) it worked
WHY? isn't it basically the same thing???
The SCSS:
li {
:before {
foo: bar
}
}
represents the CSS:
li :before { foo: bar }
The space is a descendant combinator.
You are trying to get:
li:before { foo: bar }
i.e. "Before the li" and not "Before each of the li's descendents".
You can use an ampersand to supress the descendent combinator:
li {
&:before {
foo: bar
}
}
As pointed out by Qunetin, the correct syntax is
.li {
&::before {
//stuff
}
}
But also remember that the correct syntax for pseudoelements is the double colon (::before). The single colon (:hover) is used for pseudoclasses

Pseudo class on visited link

Using only CSS, I'm trying to set a list of links to have a exclamation mark next to them if they are 'unvisited' links, and a check box next to them if they have been visited. The former works fine, but when the links have been visited, the tick box doesn't appear. My CSS is as follows:
.journey-table td a:link:before {
content: "\f071";
font-family: FontAwesome;
padding-right: 5px;
}
.journey-table td a:visited:before {
content: "\f14a";
font-family: FontAwesome;
padding-right: 5px;
}
Any help would be greatly appreciated.
According to this page, the styling of a :visited element, has been made very limited, for privacy reasons. Because of this, any child elements or pseudo elements of a visited link will be styled like an unvisited link.
I've created an example for you to understand
a:before {
background-color: blue;
content: "";
display: block;
height: 25px;
width: 25px;
float: left;
margin-right: 10px;
}
a:hover:before {
background-color: red;
}
this is a link

Responsive Separator for Horizontal List

This question expands upon 'Separators For Navigation' by asking, how it is possible to remove the separators at the line breaks cause by viewport size.
Wide Viewport
-> Item 1 | Item 2 | Item 3 | Item 4 | Item 5 <-
Small Viewport
-> Item 1 | Item 2 | Item 3 <-
-> Item 4 | Item 5 <-
Here is a fiddle that shows how a pipe remains at the line break:
Fiddle.
I'm interested in a css-only solution, but javascript is acceptable if it provides the only possible solution.
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></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></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 in few months younger Tom Robinson's answer.
gfullam's answer using flex-box, very impressive alternative with plain over-extending borders and different spacing due flex arrangement.
Oriol's answer for left-aligned list using overflow hidden and real character in pseudo.
A different solution from that same CSS: Last element on line seems like it would work here.
HTML:
<div>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
</div>
CSS:
div {overflow: hidden; margin: 1em; }
div ul { list-style: none; padding: 0; margin-left: -4px; }
div ul li { display: inline; white-space: nowrap; }
div ul li:before { content: " | "; }
(Fiddle)
If you have static width of your element you can calculate by the media-screen.
If not use script
body {
text-align: center;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
display: inline-block;
&:not(:last-child):after {
content: ' |';
}
}
#media screen and (max-width: 265px) {
li {
display: inline-block;
&:not(:last-child):after {
content: '';
}
}
}
Nice question. For the life of me, I can't think of a water-tight CSS-only solution I'm afraid...
I've modified an old solution to a similar question posted a while back: CSS: Last element on line. Funnily enough I was looking for a solution to another problem I had a while back and stumbled across this - been bookmarked since!
Here's a fiddle with my updates: https://jsfiddle.net/u2zyt3vw/1/
HTML:
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
CSS:
body {
text-align: center;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
display: inline-block;
&:not(:last-child):after {
content: ' |'
}
}
li.remove:after {
content: none;
}
jQuery:
$(window).on("resize", function () {
var lastElement = false;
$("ul > li").each(function() {
if (lastElement && lastElement.offset().top != $(this).offset().top) {
lastElement.addClass("remove");
}
lastElement = $(this);
}).last().addClass("remove");
}).resize();
NOTE - it works best onload at the moment, resizing causes a few issue even if I use toggleClass(). So keep pressing "Run" every time you resize the view. I'll work on it and get back to you..
My implementation with JavaScript: https://jsfiddle.net/u2zyt3vw/5/
Hit "Run" again after you've resized the window.
You can also add event listeners such as onresize. Here's the JS:
var listItems = document.getElementsByTagName("li");
var listItemsWidth = [];
var listItemsDistance = [];
for (let i = 0; i < listItems.length; i++) {
listItemsWidth[i] = listItems[i].offsetWidth;
listItemsDistance[i] = listItems[i].getBoundingClientRect().right;
}
for (let i = 0; i < listItems.length; i++) {
if (listItemsDistance[i] == Math.max.apply(null, listItemsDistance)) {
listItems[i].classList -= "notLast";
} else {
listItems[i].classList = "notLast";
}
}
I added the notLast class to all of your elements, and that's what contains the :after pseudo-element with the pipe. This script removes this class from the ones that are closer to the right edge of the container.
I also messed around with the :after pseudo-element and made it position:absolute; for dark reasons.