Building anchor links

Guy Waldman

Guy Waldman

What does any blog author with two posts need? "More content!" I hear you shout, to which I say - not today! I have decided to add - drum roll, please - some anchor links to the headings! Okay, I lied, I did indeed decide to make some content around it, since the last time I posted a blog post... Well, let's just say it's been a long time to avoid embarrassment.

So what are anchor links, anyway? If you're unfamiliar, anchor links are basically links to specific sections of a page.
Try hovering over one of the headings in this page, and clicking on the icon next to it - it will append a # with the heading (e.g. #adding-the-anchor) and will scroll the page so that it focuses on the heading. If you navigate to this URL, it will also focus on the heading.
How this works is not magic - the ID of the heading should match the heading "slug"; In this example, the "Adding the Anchor" <h2> heading has an ID of adding-the-anchor. Try inspecting the HTML and seeing for yourself!

Note: If you link to a page's heading (for example, in technical documentation), bear in mind that the heading can change (which may "break" your link and lead folks to the top of the page rather than a specific section) but web developers are aware of this functionality and may not change the ID to avoid that.
Text Fragments are also useful for this kind of stuff (but are much more prone to breaking).

So, let's begin with the simplest heading and see how you would go about doing something like this:

1<h2>Adding the Anchor</h2>
html

The actual code I use to render headings is omitted for simplicity (and accounts for various level of heading tags).

Let's break this down into three parts:

  1. Add the anchor to the heading
  2. Display it only on hover
  3. Add the actual anchor link functionality

Adding the Anchor

First, let's style the heading a bit (I will not be assuming any predefined styles):

1h2 {
2  font-weight: bold;
3  font-size: 40px;
4  font-weight: bold;
5}
css

This should look something like this:

(click to zoom)

Let's wrap the heading in a <div> with a class of container (we'll style that soon) and add a link icon from heroicons with a class of icon (also to be styled later):

1<div class="container">
2    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
3         stroke="currentColor" class="icon">
4      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
5    </svg>
6  <h2>Adding the Anchor</h2>
7</div>
html

Now, let's style the icon a bit:

1.icon {
2  color: #9ca3af;
3  width: 18px;
4  height: 18px;
5}
css

Note: By setting the SVG's stroke property to currentColor (that is also how it's copied from Heroicons), we can change the text color and it will control the SVG's stroke. This technique is particularly useful in CSS utility libraries like Tailwind, in which you have special classes for the setting the text color.

At this point we should have something like this:

(click to zoom)

Instead, what we want to do is have them on the same line - we'll use Flexbox for that.
We'll also want to align them both to the bottom, so an items-end class should take care of that:

1.container {
2	display: flex;
3	align-items: flex-end;
4}
5
6.icon {
7	color: #9CA3AF;
8	width: 18px;
9	height: 18px;
10}
11
12h2 {
13	font-weight: bold;
14	font-size: 40px;
15	font-weight: bold;
16}
css

Another important thing we'll want to do, in my case, is have the heading stay put, and move the anchor link to the left of it. The way to achieve is adding a wrapper with a position: relative, and adding a wrapper over the SVG with a position: absolute, thus detaching it from the document flow.
We'll also move it up a bit, so that its bottom end aligns with the text's baseline (there are better ways to achieve this, but this will be do for our purposes).

1.container {
2	position: relative;
3	display: flex;
4	align-items: flex-end;
5}
6
7.icon {
8	position: absolute;
9	left: -36px;
10	bottom: 4px;
11	color: #9CA3AF;
12	width: 18px;
13	height: 18px;
14}
15
16h2 {
17	font-weight: bold;
18	font-size: 40px;
19	font-weight: bold;
20}
css
(click to zoom)

Displaying on Hover

Alright, let's say that we're content with this. Now how do we only show it on hover?

Doing this in JavaScript would be very simple; however, I always tend to try and make things work using only HTML and CSS, before asking JS for help.

So, let's set the icon to display: none and when the container is hovered, let's change it to display: block.
No sweat, right?

1.container {
2	position: relative;
3	display: flex;
4	align-items: flex-end;
5}
6
7.container:hover .icon {
8  display: block;
9}
10
11.icon {
12	display: none;
13	position: absolute;
14	left: -36px;
15	vertical-align: text-bottom;
16	bottom: 4px;
17	color: #9CA3AF;
18	width: 18px;
19	height: 18px;
20}
21
22h2 {
23	font-weight: bold;
24	font-size: 40px;
25	font-weight: bold;
26}
css

Now we get to the tricky part, however. Do you notice the issue? When we hover over the title, and then move our mouse to the anchor - it disappears!

How I solved this (and I am sure that there are other, possibly better approaches out there) is by essentially expanding the "hit box" of the heading with a neat hack - I added padding-left: 36px (which gave the text left padding, thus moving it to the right) and "corrected" it by moving it back left with a left: 36px (the class is already position: relative so no need to set it). This alone would work, except it would also move the icon left by 36px, so I set the icon to left: 0.

1.container {
2	position: relative;
3	display: flex;
4	align-items: flex-end;
5	padding-left: 36px;
6	right: 36px;
7}
8
9.container:hover .icon {
10  display: block;
11}
12
13.icon {
14	display: none;
15	position: absolute;
16	left: 0;
17	vertical-align: text-bottom;
18	bottom: 4px;
19	color: #9CA3AF;
20	width: 18px;
21	height: 18px;
22}
23
24h2 {
25	font-weight: bold;
26	font-size: 40px;
27	font-weight: bold;
28}
css

Adding the Functionality

To make this into a working anchor link, we need to make sure of two things:

  1. Add an ID to the heading (its "slug", e.g. for "Adding the Anchor" we could use adding-the-anchor)
  2. Make the anchor icon a link to the same page, with an added #adding-the-anchor

The HTML would look something like this:

1<div class="container">
2	<a href="#adding-the-anchor">
3		<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
4			stroke="currentColor" class="icon">
5		<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
6		</svg>
7	</a>
8  <h2 id="adding-the-anchor">Adding the Anchor</h2>
9</div>
html

This concludes this post! I hope it proved useful to at least some of you.

If it helps, here is a Codesandbox which demonstrates this in action: https://codesandbox.io/s/anchor-link-demo-nk9ez