about summary refs log tree commit diff
path: root/content/posts/2021-10-16-how-to-hide-decorative-anchor.md
blob: 570d38ebb15754f7f6db487ad0d18aff962ad039 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
---
title: "How to hide decorative anchor"
date: 2021-10-16
lang: en
ategories: [ blog, guide ]
tags: [ heading, anchor, accessibility, decorative]
translationKey: "hide-anchor"
---

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:

```html
<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:

```css
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 answers[^0] that you can add alternative text
for pseudo-element by doing this:

```css
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][hidden] 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:

```html
<h2 id="foo">
  <a href="#foo">
    <span aria-hidden="true">#</span>
    <span class="visually-hidden">Section titled Foo</span>
  </a>
  Foo
</h2>
```

[hidden]: https://nicolas-hoizey.com/articles/2021/02/25/accessible-anchor-links-with-markdown-it-and-eleventy/

This method is also used by [HTMHell][hell] and [MDN][mdn]'s social icons
(though, HTMHell call the class `u-hidden` and use `sr-only` in their
instruction).

Here is how <abbr title="Mozilla Developper Network">MDN</abbr> styles the
visually hidden class:

```css
.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 <abbr>RSS</abbr> reader,
as a <abbr>RSS</abbr> reader may not support all HTML tag and doesn't have the
<abbr>CSS</abbr> for the visually hidden element at all.  The heading appears
as "# Section titled Foo Foo", which is rather hideous, if you ask me.

[hell]: https://www.htmhell.dev/20-close-buttons/
[mdn]: https://developer.mozilla.org/

## 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 <abbr>RSS</abbr> feed.

HTML:

```html
<h2 id="foo">
  <span class="decorative" aria-hidden="true"></span>
  <a href="#foo">Foo</a>
</h2>
```

CSS:

```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.

[^0]: one of a rather less reliable source of knowledge, yet commonly used by
  many people