Skip to content

My “Skip to content” markup was breaking the back button on iOS

One of the great joys of having your own website is being able to publish all sorts of random pages, such as this listing of DOM events, because why not.

Perusing these sort of reference pages I built for myself, it became increasingly grating that the basic feature of maintaining the scroll position during navigation seemed to be broken on iOS Safari.

I checked: the issue didn’t affect all websites, just my three-HTML-files-in-a-trenchoat website. Whenever I followed an external link and tapped the Back button, Safari frustratingly scrolled to the top of my original page, instead of keeping my place on the page.

Video: I open an article on my website about my favorite albums from last year. I scroll down to Altın Gün and tap the link, which takes me to the album’s Bandcamp page. When I tap the Back button, the article is scrolled back to the h1 heading.

The problem: <main tabindex=-1> loses your scroll position

A key detail in the video is that Safari doesn’t scroll the page all the way back to the top, but to the <h1> heading. This suggests it has some sort of heuristic that leads it to believe that’s where the focus/scroll should be.

I started by looking at how desktop browsers place the focus when returning to the page, by inspecting document.activeElement:

That Safari focuses the <main> element was a clue to what might be going on with the mobile version.

Turns out my Skip to content markup — more specifically, the tabindex=-1 on the <main> element — is what makes Safari scroll the <main> element’s top edge into view when you return from a navigation.

<a href="#main">Skip to content</a>

<main id="main" tabindex="-1"></main>

Removing the tabindex from the <main> element made iOS Safari behave.

Is the attribute needed in the first place? To include tabindex has long been recommended to ensure that a variety of browsers and assistive technology move the focus properly to the destination element. A host of issues that <main tabindex=-1> is supposed to solve have been fixed in the meantime, but not all of them.

It seems some sort of manual focus management is still in order, and without any obvious documented downsides, the pattern remains popular. But breaking the scroll position is a pretty significant drawback, one that warrants a re-evaluation of this approach, at least until Safari fixes the issue.

The solution: to be discussed

For now, I have removed the tabindex attribute, and will be looking for an alternate solution. Ultimately, it might involve JavaScript, as for the pattern used on, or changing the anchor to point to an element inside <main>, such as the <h1>.

I have also made a test case and submitted WebKit#272624 about it. Testing in Browserstack reveals that the issue was introduced somewhere around iOS/Safari 14 back in 2020.