Different ways to do separators in horizontal text
Quite often on the web, you'll see a set of "things" with a separator between them. For example, at the top of this post, you'll see
Thing 1 | Something Else | Another Thing.
This provides clear visual separation between logical groups. But there are a couple of problems. Firstly, the separator character may not be interpreted correctly by screen readers. They may read out "Vertical Pipe", which isn't very user friendly. Similarly, robots may not attach the correct semantics to the separator. Which is a problem if you're trying to do machine learning on text.
HTML and CSS are about the separation of content and style. So let's examine the ways we can provide a better way to do separators.
Hide Them
I think that this is the least satisfying method.
HTML<span aria-hidden="true">|</span> Thing 1 <span aria-hidden="true">|</span> Something else
The aria-hidden
tag hides the content from accessibility software. It is specifically designed to remove purely decorative items. But, because the separators are simply removed, the text runs together. That's a problem for screen readers.
CSS Borders
This is another popular way to visually distinguish groups.
HTML<span style="border-right: solid #000 1px">Thing 1</span> <span style="border-right: solid #000 1px">Another thing</span>
You can style the CSS however you like. Depending on your layout, you may need to use :not()
on the last element to prevent an unnecessary separator.
But, again, there's no way for a screen reader or robot to know that the content is supposed to be distinct from each other.
⋮:before
and ::after
?
Similarly, the ::before
and ::after
selectors can be used to add content before/after an element:
HTML<style>
span::after {
content: " | ";
}
</style>
<span>Thing 1</span> <span>Another thing</span>
Abuse the <hr>
element?
This is a bit wacky. The <hr>
element is a - somewhat - outdated way to provide a sectioning break by drawing a horizontal line. But it is accessible in CSS. By changing its rotation and width, it makes a passable vertical separator!
HTMLThing 1 <hr style="rotate: 90deg;width: 1em;"> Another thing
It can be a bit tricky to get the styling just right - but it will effectively separate logical groups.
List styling
I like using lists for things like this. It groups the items together and provides a logical "block" that either a human or robot can interpret or skip.
Here's a baroque method to make a list horizontal and to replace the content of the bullet.
HTML<style>
#hz > li::before {
display: list-item;
content: "";
position: absolute;
margin-inline-start: -.5em;
}
#hz > li {
display: inline-block;
list-style-type: "|";
padding-right: 1em;
}
#hz > li:first-child {
list-style-type: "";
}
</style>
<ul id="hz">
<li>Thing 1</li>
<li>Another thing</li>
</ul>
That removes all the bullets. Then replaces every one except the first with a pipe spaced back a little from the text. And lays the list out horizontally.
If you want to change the colour or position of the bullet, you can use ::marker
instead.
Mix And Match
My preferred way is to use <li>
, remove the bullets, and then style a border on the left - except for the first one. This keeps the semantics of the list, and is a relatively simple piece of CSS.
HTML<style>
#hz > li {
display: inline-block;
border-left: solid #000 1px;
padding: 0 1em 0 1em;
}
#hz > li:first-child {
border: none;
}
</style>
<ul id="hz">
<li>Thing 1</li>
<li>Another thing</li>
</ul>
Is there a best way?
No, it is a matter of personal taste and appetite for complexity. My "strong opinion weakly held" is that semantic HTML is nearly always preferable to just chucking stuff in a load of <span
>s and <div>
s. It makes the content easy to understand if you're browsing without CSS, or if you're a robot trying to understand the world.
Similarly, I think simple CSS is better than complicated CSS. Something small and human-readable is always going to be easier to understand and manage in the future.
But, if you think I'm wrong - or know a better way to lay things out - please let me know in the comments.
Šime (she-meh) 🦊 said on twitter.com:
Re: Mix And Match
The display: inline-block on <li> seems to remove list semantics. I checked in Safari/VoiceOver, and the list was not announced.
bazbt3 said on appdot.net:
@Edent Heh! /me scurries off to update my profile (here at least) to make it screen-readable. Thanks for the tip.
Jeremy says:
On my site, I have settled for the following HTML for navigation.
<div class="feeds" aria-labelledby="site-feeds-label" role="list"> <span id="site-feeds-label" class="label">Site feeds </span> <span role="listitem" aria-label="ATOM Feed"><a href="https://takeonrules.com/index.atom">ATOM feed</a></span> <span aria-hidden="true"> · </span> <span role="listitem" aria-label="RSS Feed"><a href="https://takeonrules.com/index.xml">RSS feed</a></span> <span aria-hidden="true"> · </span> <span role="listitem" aria-label="JSON Feed"><a href="https://takeonrules.com/index.json">JSON feed</a></span> </div>
The separator is the middot. From middot.net (https://middot.net)
What I like about this is navigations visually render horizontally, regardless of stylesheets. And with the role="list" and role="listitem", accessibility devices will pick these up as lists. I also find the middot character to be just enough of a visual cue of separation.