mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 09:55:06 +03:00
## 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 `<main>`. | After | | --- | | <img width="836" height="324" alt="CleanShot 2026-07-24 at 14 08 47@2x" src="https://github.com/user-attachments/assets/6df29452-e53a-4eca-8f64-946f2b9f605d" /> | ## 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 `<main>`). 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). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## 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. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
150 lines
4.5 KiB
Plaintext
150 lines
4.5 KiB
Plaintext
---
|
|
title: Button
|
|
description: Displays a button or a link that looks like a button.
|
|
featured: true
|
|
component: true
|
|
---
|
|
|
|
<ComponentPreview name="button-demo" peekCode wide />
|
|
|
|
## Usage
|
|
|
|
```tsx
|
|
import { Button } from '@/components/ui/button'
|
|
```
|
|
|
|
```tsx
|
|
<Button variant="outline">Button</Button>
|
|
```
|
|
|
|
## Link
|
|
|
|
You can use the `buttonVariants` helper to create a link that looks like a button.
|
|
|
|
```tsx
|
|
import { buttonVariants } from '@/components/ui/button'
|
|
```
|
|
|
|
```tsx
|
|
<Link className={buttonVariants({ variant: 'outline' })}>Click here</Link>
|
|
```
|
|
|
|
Alternatively, you can set the `asChild` parameter and nest the link component.
|
|
|
|
```tsx
|
|
<Button asChild>
|
|
<Link href="/login">Login</Link>
|
|
</Button>
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Sizes
|
|
|
|
Use the `size` prop to determine the size of the button.
|
|
|
|
<ComponentPreview name="button-sizes" />
|
|
|
|
### Variants
|
|
|
|
These are all the different `variant` variations.
|
|
|
|
#### Primary
|
|
|
|
Used for data insertion actions, confirming purchases, strong positive actions.
|
|
|
|
<ComponentPreview name="button-demo" />
|
|
|
|
#### Default
|
|
|
|
Used for opening dialogs, navigating to pages, and other non CRUD actions.
|
|
|
|
This `variant` will probably be the most used button variant.
|
|
It will probably be changed to be the default variant in future.
|
|
|
|
<ComponentPreview name="button-default" />
|
|
|
|
#### Secondary
|
|
|
|
Can be used for signaling a data or config change, but not as serious as a primary button.
|
|
For destructive or side effect actions, use the `destructive` or `warning` variant.
|
|
|
|
<ComponentPreview name="button-secondary" />
|
|
|
|
#### Warning
|
|
|
|
Used for actions that might have a side effect, but not as serious as a destructive action.
|
|
|
|
<ComponentPreview name="button-warning" />
|
|
|
|
#### Destructive (currently `danger`)
|
|
|
|
Used for actions that will have a serious destructive side effect, like deleting data.
|
|
|
|
prop `variant` will probably be changed to `destructive` in the future.
|
|
|
|
<ComponentPreview name="button-destructive" />
|
|
|
|
#### Outline
|
|
|
|
Used for secondary actions, or actions that are not as important as the primary action.
|
|
|
|
<ComponentPreview name="button-outline" />
|
|
|
|
#### Ghost (currently `text`)
|
|
|
|
Used for actions that are not as important as the primary action, or for actions that are not as important as the primary action.
|
|
|
|
prop `variant` will probably be changed to `ghost` in the future.
|
|
|
|
<ComponentPreview name="button-ghost" />
|
|
|
|
#### Link
|
|
|
|
Used for actions that are not as important as the primary action, or for actions that are not as important as the primary action.
|
|
|
|
<ComponentPreview name="button-link" />
|
|
|
|
### Only an icon
|
|
|
|
Displaying only an Icon in a button.
|
|
|
|
<Admonition type="note" title="This feature requires more support" className="mt-3">
|
|
We should update the button component to support this use case better.
|
|
</Admonition>
|
|
|
|
<ComponentPreview name="button-icon" />
|
|
|
|
### As child
|
|
|
|
Supports slot behavior with `asChild` prop.
|
|
|
|
<ComponentPreview name="button-as-child" />
|
|
|
|
### Split with dropdown
|
|
|
|
Pair a button with a chevron `DropdownMenu` trigger when there are variations of the same action, or alternative ways to accomplish the same goal. The default or most likely option should be used on the exposed button.
|
|
|
|
When secondary actions are related but distinct—not alternatives to the primary action—display the primary action as a button and place the rest in an overflow menu instead. See [Table multiple actions](./table#multiple-actions).
|
|
|
|
<ComponentPreview name="button-split-dropdown" peekCode />
|
|
|
|
The shared middle border is the tricky part. Do **not** use `border-l-0` on the chevron button — that drops the divider on hover/focus. Instead:
|
|
|
|
- Primary: `rounded-r-none` and `hover:z-10` so its border stacks above the chevron on hover.
|
|
- Chevron trigger: `rounded-l-none`, `shrink-0`, `px-[4px] py-[5px]`, and `-ml-px` to overlap the adjacent border by one pixel.
|
|
- Chevron trigger only: `aria-label` describing the menu (the icon is decorative).
|
|
|
|
Inside [Admonition](../fragments/admonition#split-button-with-dropdown) actions, also use `flex w-full @lg:w-auto` with `flex-1 @lg:flex-none` on the primary when `layout="responsive"`.
|
|
|
|
## Accessibility
|
|
|
|
[Keyboard focus](../accessibility#focus-management) is automatically handled:
|
|
|
|
- Enabled buttons default to `tabIndex={0}` (keyboard accessible)
|
|
- Disabled buttons default to `tabIndex={-1}` (removed from tab order)
|
|
- You can still override with an explicit `tabIndex` prop when needed
|
|
- Keyboard focus uses the shared `focus-ring` utility; variants do not change ring colour
|
|
|
|
You therefore don't need to manually set `tabIndex`, as Button handles it automatically based on its `disabled` state.
|