Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ export default function Component() {
{['desktop', 'mobile'].map((key) => {
const chart = key as keyof typeof chartConfig
return (
<button tabIndex={0}
<button
tabIndex={0}
key={chart}
data-active={activeChart === chart}
className="relative z-30 flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l data-[active=true]:bg-surface-100 sm:border-l sm:border-t-0 sm:px-8 sm:py-6"
Expand Down
11 changes: 11 additions & 0 deletions apps/design-system/__registry__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2502,6 +2502,17 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"skip-to-content-demo": {
name: "skip-to-content-demo",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/skip-to-content-demo")),
source: "",
files: ["registry/default/example/skip-to-content-demo.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"page-container-demo": {
name: "page-container-demo",
type: "components:example",
Expand Down
11 changes: 8 additions & 3 deletions apps/design-system/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ScrollArea } from 'ui'
import { SkipToContent } from 'ui-patterns/SkipToContent'

import { MobileSidebarSheet } from '@/components/mobile-sidebar-sheet'
import { SideNavigation } from '@/components/side-navigation'
Expand All @@ -12,18 +13,22 @@ interface AppLayoutProps {
export default async function AppLayout({ children }: AppLayoutProps) {
return (
<>
<SkipToContent href="#main" />
<TopNavigation />
<MobileSidebarSheet />
<main className="flex-1 max-w-site mx-auto w-full border-l border-r border-b">
<div className="flex-1 max-w-site mx-auto w-full border-l border-r border-b">
<div className="flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] lg:grid-cols-[240px_minmax(0,1fr)]">
<aside className="fixed top-10 z-30 hidden h-[calc(100vh-3rem)] w-full shrink-0 md:sticky md:block border-r">
<ScrollArea className="h-full">
<SideNavigation />
</ScrollArea>
</aside>
{children}
{/* Content-only landmark: sidebar must stay outside so skip/Tab don't land in the nav */}
<main id="main" tabIndex={-1} className="outline-hidden scroll-mt-12 min-w-0">
{children}
</main>
</div>
</main>
</div>
<SiteFooter />
</>
)
Expand Down
2 changes: 1 addition & 1 deletion apps/design-system/components/mdx-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
TabsList,
TabsTrigger,
} from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

import { ComponentProps } from './component-props'
import { SonnerExpandConfig } from './sonner-expand-config'
Expand Down
5 changes: 5 additions & 0 deletions apps/design-system/config/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ export const docsConfig: DocsConfig = {
href: '/docs/fragments/single-value-field-array',
items: [],
},
{
title: 'Skip to Content',
href: '/docs/fragments/skip-to-content',
items: [],
},
],
},
{
Expand Down
6 changes: 4 additions & 2 deletions apps/design-system/content/docs/accessibility.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,13 @@ Individual options inside of a group can be reached by arrow keys (↑ ↓ ←

### Jumping ahead

Some keyboard-navigable content may be contain hundreds or thousands of items. Help users jump to specific content with the following mitigation strategies:
Some keyboard-navigable content may contain hundreds or thousands of items. Help users jump to specific content with:

- Search and filtering
- Pagination or virtualization
- “Jump to” shortcuts to skip ahead
- Skip links and “jump to” shortcuts

Apps with persistent header and sidebar chrome should expose a skip link as the first focusable element. Use the shared [Skip to Content](fragments/skip-to-content) fragment which owns the component API, usage sample, and target landmark contract.

## Screen readers

Expand Down
2 changes: 1 addition & 1 deletion apps/design-system/content/docs/components/button.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Button
description: Displays a button or a component that looks like a button.
description: Displays a button or a link that looks like a button.
featured: true
component: true
---
Expand Down
2 changes: 1 addition & 1 deletion apps/design-system/content/docs/fragments/admonition.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Avoid title-only Admonitions in new code. A callout with `title` should also inc
## Usage

```tsx
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'
```

```tsx
Expand Down
53 changes: 53 additions & 0 deletions apps/design-system/content/docs/fragments/skip-to-content.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: Skip to Content
description: Keyboard-accessible skip link that jumps past chrome to the main landmark.
component: true
fragment: true
---

<ComponentPreview name="skip-to-content-demo" peekCode wide />

Keyboard-accessible skip link composed from [Button](../components/button) for apps with persistent header and sidebar chrome. Hidden until focused via Tab, then slides into view so users can jump past navigation to the main landmark.

The preview above shows the focused appearance. In product apps, the link stays off-screen until keyboard focus.

See [Accessibility](../accessibility#jumping-ahead) for more information on when to use skip links, and how they coexist alongside other navigation aids.

## Usage

```tsx
import { SkipToContent } from 'ui-patterns/SkipToContent'
```

```tsx
<SkipToContent href="#main" />

<main id="main" tabIndex={-1} className="scroll-mt-(--header-height) outline-hidden">
{children}
</main>
```

Place the skip link at the root of the app chrome so Tab reaches it before navigation. The target landmark must be content only. Do not wrap a sidebar inside the same `<main>`, otherwise a Tab after skip will land in the sidebar navigation.

## Props

### `href`

Hash href to the main content landmark, e.g. `#main`.

### `children`

Link label. Defaults to `Skip to content`.

### `className`

Optional classes merged onto the positioning wrapper (useful for demos or layout overrides).

## Target landmark

Callers own the landmark the skip link points at:

- Matching `id`
- `tabIndex={-1}` so Enter moves focus onto the landmark
- `scroll-mt` when a sticky header is present
- `outline-hidden` so the landmark has no visible focus ring. The subsequent Tab will land on the first interactive child
6 changes: 4 additions & 2 deletions apps/design-system/content/docs/ui-patterns/navigation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ Supabase has a necessarily complex navigation system to handle multiple products

## Components

### NavMenu
### [Nav Menu](../components/nav-menu)

A horizontal list of related views within a consistent PageLayout context, allowing for clearer page-level organisation. Activating a NavMenu item should trigger a URL change.

[NavMenu component guidelines](../components/nav-menu)
### [Skip To Content](../fragments/skip-to-content)

Keyboard-accessible skip link for apps with persistent header and sidebar chrome. Hidden until focused via Tab, then slides into view so users can jump past navigation to the main landmark. See [Accessibility](../accessibility#jumping-ahead) for broader context.

## Page titles

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

export default function AdmonitionButtonSplitDemo() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

export default function AdmonitionDemo() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

export default function AdmonitionDemo() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

export default function AdmonitionDescriptionOnly() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

export default function AdmonitionDestructive() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

export default function AdmonitionDemo() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button, Card, CardContent, CardHeader, CardTitle } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

export default function AdmonitionDemo() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

export default function AdmonitionSuccess() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

export default function AdmonitionWarning() {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
AlertDialogTrigger,
Button,
} from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

const resetTemplate = async () => {
await new Promise((resolve) => setTimeout(resolve, 1200))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

import { AccountRow, InterstitialShell, SupabaseLogo } from './connect-interstitial-shared'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Link from 'next/link'
import { Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

const bucketId = 'user_avatars'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
NavMenuItem,
Switch,
} from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { PageBreadcrumbs } from 'ui-patterns/PageBreadcrumbs'
import { PageContainer } from 'ui-patterns/PageContainer'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { SkipToContent } from 'ui-patterns/SkipToContent'

export default function SkipToContentDemo() {
return (
<div className="relative w-full overflow-hidden rounded-md border bg-studio">
<div className="flex items-center border-b px-4 py-3 text-sm text-foreground-muted">
Demo header / navigation
</div>
{/* Preview shows the focused appearance; production hides until Tab */}
<SkipToContent href="#skip-demo-main" className="relative left-3 top-2 translate-y-0" />
<main id="skip-demo-main" tabIndex={-1} className="outline-hidden p-6 pt-2">
<p className="text-sm text-foreground-light">
Main content landmark. In a real app, Tab once to reveal the skip link, then Enter to jump
here.
</p>
</main>
</div>
)
}
5 changes: 5 additions & 0 deletions apps/design-system/registry/examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,11 @@ export const examples: Registry = [
type: 'components:example',
files: ['example/info-tooltip-demo.tsx'],
},
{
name: 'skip-to-content-demo',
type: 'components:example',
files: ['example/skip-to-content-demo.tsx'],
},
{
name: 'page-container-demo',
type: 'components:example',
Expand Down
5 changes: 3 additions & 2 deletions apps/docs/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ 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 { DOCS_CONTENT_CONTAINER_ID } from '~/features/ui/helpers.constants'
import { TopNavSkeleton } from '~/layouts/MainSkeleton'
import { BASE_PATH, IS_PRODUCTION } from '~/lib/constants'
import { getCustomContent } from '~/lib/custom-content/getCustomContent'
import { TelemetryTagManager } from 'common'
import { genFaviconData } from 'common/MetaFavicons/app-router'
import type { Metadata, Viewport } from 'next'
import { SkipToContent } from 'ui-patterns/SkipToContent'

import { inter, manrope } from '@/fonts'

Expand Down Expand Up @@ -53,7 +54,7 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => {
return (
<html lang="en" className={`${manrope.variable} ${inter.variable}`} suppressHydrationWarning>
<body>
<SkipToContent />
<SkipToContent href={`#${DOCS_CONTENT_CONTAINER_ID}`} />
<TelemetryTagManager />
<GlobalProviders>
<TopNavSkeleton>{children}</TopNavSkeleton>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState } from 'react'
import { Button, Input } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'
import { Input as DataInput } from 'ui-patterns/DataInputs/Input'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

<Admonition type="warning">

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

## Prerequisites

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

## Prerequisites

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Admonition } from 'ui-patterns/admonition'
import { Admonition } from 'ui-patterns/Admonition'

## Prerequisites

Expand Down
Loading
Loading