As you might have noticed, I recently have some changes to the website design. The changes are mostly for accessibility and readability: the text is now bigger so it’s easier to read and select. I also color visited links, so people can spend less time knowing they’ve read a link (haha who wants people to spend less time on their site I must be crazy right?). One of the changes it the heading anchor with a decorative “#” to make visual readers recognize the heading levels easier. However, if this would probably be announced as “hash” by screen readers, which wouldn’t make sense. In this post, I’d show how I ended up with the current one.
Simple heading anchor
Heading anchor can simply be done by wrapping the content inside the anchor
element, <a>
, which refers to the ID of the heading:
<h2 id="foo">
<a href="#foo">Foo</a>
</h2>
This is very simple, and it works perfectly fine. However, I also would like
to have a fancy visual cue in front of the heading some website use a chain
link, but I prefer to use a simple character instead of an image. The hash
symbol makes sense for me, since I mostly use Markdown. I use heading level
minus 1 hashes for this (since heading level one is not used within a post).
Using ::before
pseudo-element, this is rather simple:
h2::before {
content: '# ';
}
h3::before {
content: '## ';
}
...
Unfortunately, this leads to an accessibility problem as stated at the beginning of this post: screen readers (inconsistently) announces this as “hash”. Multiply that with the level of heading and imagine the nuisance.
There are several proposals to solve this, which is discussed in following sections.
Alternative text for pseudo-element
I’ve read from some StackOverflow answers1 that you can add alternative text for pseudo-element by doing this:
h2::before {
content: '# ';
content: '# ' / '';
}
I don’t know which browser supports this; it looks like bad syntax. The answer itself said this is non-standard. And I’d tell you this is not supported by Firefox, at least.
Using aria-hidden content and visually hidden description
So, I found a blog post whose content is similar to this one. It is likewise a long post, but in short the method is instead of using a pseudo-element, you can use aria-hidden decorative element with a visually hidden text. It looks like this:
<h2 id="foo">
<a href="#foo">
<span aria-hidden="true">#</span>
<span class="visually-hidden">Section titled Foo</span>
</a>
Foo
</h2>
This method is also used by HTMHell and MDN’s social icons
(though, HTMHell call the class u-hidden
and use sr-only
in their
instruction).
Here is how MDN styles the visually hidden class:
.visually-hidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute !important;
width: 1px;
}
This, however, looks bad if you read my posts from a RSS reader, as a RSS reader may not support all HTML tag and doesn’t have the CSS for the visually hidden element at all. The heading appears as “# Section titled Foo Foo”, which is rather hideous, if you ask me.
Current solution
It is a rather simple combination of the previous approach and my original
approach: use aria-hidden decorative icon, and use ::before
pseudo-element to
avoid it rendering in the RSS feed.
HTML:
<h2 id="foo">
<span class="decorative" aria-hidden="true"></span>
<a href="#foo">Foo</a>
</h2>
CSS:
h2 .decorative::before {
content: '# ';
}
I have only tested this on Firefox with orca as screen reader, though I expect it to do well on others as well.
-
one of a rather less reliable source of knowledge, yet commonly used by many people ↩︎
Tags: