feat(docs): add keyboard-accessible skip to content link (#47515)

https://github.com/user-attachments/assets/30438e3c-b9aa-411b-be38-3fdcd50f7dc6

Closes DOCS-92



## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## Problem

Screenreaders have to navigate our main menu on every page.
This comes from a very old request. 2024. 😱 

## Solution

A 'Skip to Content' button is standard a11y practice.

This PR improves keyboard navigation by letting users bypass the top nav
and sidebar to jump directly to main content.

## Tophatting

1. Go to any page in our docs. Try a sample of different layouts.
2. Use TAB to navigate. See 'Skip to Content' appear. 
**Note:** You may need to SHIFT + TAB if your keyboard focus is past the
main navigation. Mouse clicks can shift focus.
3. Press ENTER.
4. Continue to TAB and see the next links focused are in the main body.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a “skip to content” button in the docs to improve keyboard
navigation.
* **Accessibility / UX**
* Made the main content area programmatically focusable and adjusted
scroll positioning for smoother jumps.
* **Style**
* Removed the prior global skip-link styles in favor of component-based
skip-to-content behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Miranda LimonczenkoandCursor authored and GitHub committed 2026-07-02 23:59:29 +00:00
1 parent a1716edc3c
commit 4f05124ce7
4 files changed
+26 -8

No files matched your search

+2
View File
@@ -4,6 +4,7 @@ import 'ui-patterns/ShimmeringLoader/index.css'
import '../styles/globals.css'
import '../styles/prism-okaidia.css'
import { SkipToContent } from '~/components/SkipToContent'
import { GlobalProviders } from '~/features/app.providers'
import { TopNavSkeleton } from '~/layouts/MainSkeleton'
import { BASE_PATH, IS_PRODUCTION } from '~/lib/constants'
@@ -54,6 +55,7 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => {
return (
<html lang="en" className={`${manrope.variable} ${inter.variable}`} suppressHydrationWarning>
<body>
<SkipToContent />
<TelemetryTagManager />
<GlobalProviders>
<TopNavSkeleton>{children}</TopNavSkeleton>
+22
View File
@@ -0,0 +1,22 @@
import { DOCS_CONTENT_CONTAINER_ID } from '~/features/ui/helpers.constants'
import Link from 'next/link'
import { Button, cn } from 'ui'
const SkipToContent = () => {
return (
<Button
size="tiny"
variant="default"
asChild
className={cn(
'fixed top-0 left-4 z-[100] w-auto',
'-translate-y-full focus-visible:translate-y-4',
'transition-transform duration-200 ease-out'
)}
>
<Link href={`#${DOCS_CONTENT_CONTAINER_ID}`}>Skip to content</Link>
</Button>
)
}
export { SkipToContent }
+2 -1
View File
@@ -283,8 +283,9 @@ const Container = memo(function Container({
<main
// used by layout to scroll to top
id={DOCS_CONTENT_CONTAINER_ID}
tabIndex={-1}
className={cn(
'w-full transition-all ease-out relative',
'w-full transition-all ease-out relative scroll-mt-(--header-height)',
// desktop override any margin styles
'lg:ml-0',
className
-7
View File
@@ -406,13 +406,6 @@ th code {
image-rendering: high-quality;
}
.skip-link {
@apply sr-only;
}
.skip-link:focus {
@apply not-sr-only;
}
/* Code blocks need margin applied when in content container */
.prose :where(.shiki:not(.shiki-wrapper *), .shiki-wrapper) {
margin-block: 2rem;