From 37dded67d123f7c650a78ea6320e53e118dc5262 Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Tue, 28 Jul 2026 16:21:50 -0400 Subject: [PATCH] feat: unify SkipToContent across studio, docs, www, and design-system (#48314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What kind of change does this PR introduce? Feature / a11y polish ## What is the current behavior? Studio and Docs each had their own skip-to-content link (different styling and behaviour). www and design-system had none. ## What is the new behavior? Shared `SkipToContent` in `ui-patterns`, adopted by Studio, Docs, www, and design-system. Documented as a fragment with a short note under Accessibility → Jumping ahead. Tab once to reveal the button (top-left), Enter to jump to a content-only `
`. | After | | --- | | CleanShot 2026-07-24 at 14 08
47@2x | ## To test Shared steps for every app: enable Tab key navigation if needed, load the preview, press **Tab** once — skip button should slide in top-left. Press **Enter** — focus jumps to main content (no blue ring on `
`). Press **Tab** again — first interactive control in the page body, not the sidebar/nav. Hover the skip button — solid fill, clear hover state, no chrome showing through. - **Studio** — [preview](https://studio-staging-git-dnywh-featskip-to-content-supabase.vercel.app) → sign in → any project page - **Docs** — [preview](https://docs-git-dnywh-featskip-to-content-supabase.vercel.app) → any docs page with sidebar - **www** — [preview](https://zone-www-dot-com-git-dnywh-featskip-to-content-supabase.vercel.app) → homepage or any marketing page with the default nav - **Design system** — [preview](https://design-system-git-dnywh-featskip-to-content-supabase.vercel.app) → any docs page (confirm Tab from content does **not** walk the sidebar), plus [Skip to Content fragment](https://design-system-git-dnywh-featskip-to-content-supabase.vercel.app/docs/fragments/skip-to-content) ## Additional context Follow-up to #47694 / #48303 (Studio) and #47515 (Docs). ## Summary by CodeRabbit - **New Features** - Added a reusable “Skip to content” accessibility link across key layouts and pages. - Updated main landmarks to support keyboard focus and skip-link navigation (`id="main"`). - **Accessibility** - Skip links now follow consistent landmark-target conventions and remain hidden until focused. - Improved documentation for skip links/jump shortcuts in persistent chrome layouts. - **Documentation** - Added a dedicated Skip to Content fragment, navigation entry, and expanded accessibility guidance. - Updated button description wording in component docs. - **Tests** - Added component tests for SkipToContent. - **Chores** - Exposed SkipToContent via additional public package entry points. --- .../default/block/chart-bar-interactive.tsx | 3 +- apps/design-system/__registry__/index.tsx | 11 ++++ apps/design-system/app/(app)/layout.tsx | 11 ++-- apps/design-system/config/docs.ts | 5 ++ .../content/docs/accessibility.mdx | 6 ++- .../content/docs/components/button.mdx | 2 +- .../docs/fragments/skip-to-content.mdx | 53 +++++++++++++++++++ .../content/docs/ui-patterns/navigation.mdx | 6 ++- .../default/example/skip-to-content-demo.tsx | 19 +++++++ apps/design-system/registry/examples.ts | 5 ++ apps/docs/app/layout.tsx | 5 +- apps/docs/components/SkipToContent.tsx | 22 -------- apps/docs/layouts/MainSkeleton.tsx | 2 +- .../components/layouts/DefaultLayout.tsx | 26 ++------- apps/www/app/(home)/layout.tsx | 6 ++- apps/www/components/Go/GoPageRenderer.tsx | 8 ++- apps/www/components/Layouts/Default.tsx | 10 +++- packages/ui-patterns/package.json | 8 +++ .../src/SkipToContent/SkipToContent.test.tsx | 41 ++++++++++++++ .../src/SkipToContent/SkipToContent.tsx | 37 +++++++++++++ .../ui-patterns/src/SkipToContent/index.tsx | 1 + 21 files changed, 228 insertions(+), 59 deletions(-) create mode 100644 apps/design-system/content/docs/fragments/skip-to-content.mdx create mode 100644 apps/design-system/registry/default/example/skip-to-content-demo.tsx delete mode 100644 apps/docs/components/SkipToContent.tsx create mode 100644 packages/ui-patterns/src/SkipToContent/SkipToContent.test.tsx create mode 100644 packages/ui-patterns/src/SkipToContent/SkipToContent.tsx create mode 100644 packages/ui-patterns/src/SkipToContent/index.tsx diff --git a/apps/design-system/__registry__/default/block/chart-bar-interactive.tsx b/apps/design-system/__registry__/default/block/chart-bar-interactive.tsx index fca68de5bc7..128664bb431 100644 --- a/apps/design-system/__registry__/default/block/chart-bar-interactive.tsx +++ b/apps/design-system/__registry__/default/block/chart-bar-interactive.tsx @@ -146,7 +146,8 @@ export default function Component() { {['desktop', 'mobile'].map((key) => { const chart = key as keyof typeof chartConfig return ( - - ) -} - -export { SkipToContent } diff --git a/apps/docs/layouts/MainSkeleton.tsx b/apps/docs/layouts/MainSkeleton.tsx index 93cc70fa00c..5ae70b4386c 100644 --- a/apps/docs/layouts/MainSkeleton.tsx +++ b/apps/docs/layouts/MainSkeleton.tsx @@ -290,7 +290,7 @@ const Container = memo(function Container({ id={DOCS_CONTENT_CONTAINER_ID} tabIndex={-1} className={cn( - 'w-full transition-all ease-out relative scroll-mt-(--header-height)', + 'w-full transition-all ease-out relative scroll-mt-(--header-height) outline-hidden', // desktop override any margin styles 'lg:ml-0', className diff --git a/apps/studio/components/layouts/DefaultLayout.tsx b/apps/studio/components/layouts/DefaultLayout.tsx index 0d507baaca3..734493bb7bb 100644 --- a/apps/studio/components/layouts/DefaultLayout.tsx +++ b/apps/studio/components/layouts/DefaultLayout.tsx @@ -1,14 +1,8 @@ import { useBreakpoint, useParams } from 'common' import { useRouter } from 'next/router' import { PropsWithChildren, useEffect, useState } from 'react' -import { - buttonVariants, - cn, - ResizablePanel, - ResizablePanelGroup, - SidebarProvider, - usePanelRef, -} from 'ui' +import { ResizablePanel, ResizablePanelGroup, SidebarProvider, usePanelRef } from 'ui' +import { SkipToContent } from 'ui-patterns/SkipToContent' import { BannerStack } from '../ui/BannerStack/BannerStack' import { LayoutHeader } from './Navigation/LayoutHeader/LayoutHeader' @@ -101,19 +95,7 @@ export const DefaultLayout = ({
- - Skip to content - + {/* Top Banner */}
@@ -144,7 +126,7 @@ export const DefaultLayout = ({ maxSize={`${contentMaxSizePercentage}`} defaultSize={`${contentMaxSizePercentage}`} > -
+
{children}
diff --git a/apps/www/app/(home)/layout.tsx b/apps/www/app/(home)/layout.tsx index 61677b56677..0e005f310f9 100644 --- a/apps/www/app/(home)/layout.tsx +++ b/apps/www/app/(home)/layout.tsx @@ -1,15 +1,19 @@ import Footer from '~/components/Footer' import Nav from '~/components/Nav' +import { SkipToContent } from 'ui-patterns/SkipToContent' import { ThemeForcer } from './ThemeForcer' export default function HomeLayout({ children }: { children: React.ReactNode }) { return ( <> +