Commit Graph
11 Commits
Author SHA1 Message Date
Danny White 9a03f3cd9c fix(studio): show keyboard focus on filter bar (#50827)
## Problem

- Filter fields could not be re-entered with Tab, and remove buttons
were previously skipped.
- Earlier focus rings flashed or crowded controls. Segmented filters and
design system examples also had uneven layout.

## Solution

- Tab now reaches each filter’s property, operator, value and remove
button. Editable fields use the caret; read-only values and remove
buttons keep a visible focus cue.
- Refined spacing and sizing. The segmented highlight is inset and
clears when focus enters a control. Both variants and the focus guidance
are documented on the [design system
page](https://design-system-git-dnywh-fix-filter-bar-focus-supabase.vercel.app/design-system/docs/fragments/filter-bar).

|After |
| --- |
| <img width="462" height="126" alt="CleanShot 2026-09-24 at 15 04
23@2x"
src="https://github.com/user-attachments/assets/2ee8bee2-f4fa-40f4-ada5-67bfe32384e7"
/> |
| <img width="362" height="96" alt="CleanShot 2026-09-24 at 15 04 47@2x"
src="https://github.com/user-attachments/assets/9f42054a-f432-493d-b417-f10892676c96"
/> |

## Review instructions

The easiest way to test this is to open Table Editor on both
production/staging and on this PR’s [deploy
preview](https://studio-staging-git-dnywh-fix-filter-bar-focus-supabase.vercel.app/).
Try navigating by keyboard (tab, left/right arrow) within the Filter
Bar. Compare the two.

- In Studio’s Table Editor or the [design system
example](https://design-system-git-dnywh-fix-filter-bar-focus-supabase.vercel.app/design-system/docs/fragments/filter-bar),
add a filter. Tab through its property, operator, value and remove
button, then Shift+Tab back into editing.
- Check that the segmented and pill examples fill their preview width
and that focus cues do not collide with neighbouring controls.

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

## Summary

* **New Features**
* Added a pill appearance for the Filter Bar, with usage guidance and
interactive examples for pill and segmented presentations.
* **Accessibility**
* Improved keyboard navigation through filter controls, including
removing a filter with the keyboard.
* Clarified that an editable text field’s insertion caret can serve as
its visible focus indicator; read-only fields and controls without a
caret still need another visible indicator.
* **Style**
  * Refined Filter Bar spacing and focus styling across appearances.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-28 10:13:53 +10:00
Danny White d067e81a69 fix(ui): align primary colours across text, buttons, and controls (#50697)
## Problem

Primary colour serves readable text and selected controls, but those
uses need different shades. Light mode needs darker text, while dark
mode needs a deeper button fill. Fixed brand green on interactive chrome
also prevents a custom primary hue from carrying through the interface.
Some slider tracks and selected text are hard to read.

## Solution

- Keep `--primary` for accessible text and small selected indicators.
Use `--primary-solid` for button fills, which need a deeper shade in
dark mode.
- Add `--primary-bright` for focus rings, selected control chrome, chart
accents, and other interactive highlights. It follows `--primary-hue`;
`brand-*` stays fixed for Supabase identity.
- Make slider troughs clearer and text selection translucent with theme
foreground text.
- Document the split in the design-system colour guide.

| Before | After |
| --- | --- |
| <img width="980" height="244" alt="Before: light mode primary
controls"
src="https://github.com/user-attachments/assets/dfae325d-0dfe-4231-8bcd-3f89c4b9d793"
/> | <img width="982" height="204" alt="After: light mode primary
controls"
src="https://github.com/user-attachments/assets/5fdcb531-a6e3-4549-8a13-9d9a5ebe6e20"
/> |
| <img width="610" height="120" alt="Before: slider track"
src="https://github.com/user-attachments/assets/04f768e0-51e8-4d06-9b97-c52f4a34f122"
/> | <img width="622" height="126" alt="After: slider track"
src="https://github.com/user-attachments/assets/95127f4e-13dc-4f0f-b63c-cf5d70a28b42"
/> |
| <img width="652" height="512" alt="Before: dark mode controls"
src="https://github.com/user-attachments/assets/3f88de66-90cc-40ee-8cf1-b5f4eb87b09a"
/> | <img width="658" height="498" alt="After: dark mode controls"
src="https://github.com/user-attachments/assets/906bec30-6ca1-4614-9fb3-6cf5e5feec22"
/> |

## Review instructions

1. Compare light and dark mode in the [colour usage
guide](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/color-usage#primary-and-brand-colors).
Check primary ink, primary-solid, primary-bright, and fixed brand
swatches.
2. In Studio, open the ‘new table’ sheet in [Table
Editor](https://studio-staging-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/dashboard/project/_/editor).
Tab through the new table sheet's fields and toggles. Check the focus
rings, selected controls, and the sheet's edges in both themes. You do
not need to save a table.
3. Select text in Studio in both themes, including a link or
primary-coloured label. The selection and text should remain legible.
4. Check the
[Field](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/components/field)
Price Range slider: the unused track should remain visible in both
themes. The selected field card border should follow primary-bright.
5. Check the
[Button](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/components/button)
and [Radio
Group](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/components/radio-group)
previews. In dark mode, `primary` button fill should be deeper than
primary
[text](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/color-usage#text);
selected radios should remain readable.
2026-09-24 09:56:31 +10:00
Danny White e57aae3c83 feat(design-system): document disabled controls and add focusableWhenDisabled (#50068)
## What kind of change does this PR introduce?

Docs update, with supporting `ui` and Studio changes.

## What is the current behaviour?

Disabled buttons with tooltips use native `disabled`, which removes them
from the tab order. Keyboard users cannot focus the control or read the
tooltip explaining why an action is blocked.

The design system also lacked guidance on keeping disabled actions
discoverable and explaining why they are unavailable.

## What is the new behaviour?

- Adds a **Disabled controls** section to the accessibility docs, with
live examples for a focusable disabled button and visible page-level
context
- Adds `focusableWhenDisabled` to `Button`, keeping `disabled` as the
semantic state while using `aria-disabled`, retaining keyboard focus,
and guarding click handlers
- Updates Studio's `ButtonTooltip` to make disabled buttons with tooltip
text focusable automatically

Also includes earlier design-system fixes on this branch:

- Centralises `BASE_PATH` with a `/design-system` fallback so asset URLs
work without a local `.env` file
- Fixes sidebar hover and active tokens in design-system and ui-library,
aligned with Studio's `InnerSideMenuItem`

## To test

**Design system**

1. Open the [accessibility
preview](https://design-system-git-fix-design-system-docs-and-nav-fixes-supabase.vercel.app/design-system/docs/accessibility)
2. Scroll to **Disabled controls**
3. Tab to the **disabled-focusable** example. Confirm the button remains
focusable, looks disabled, and shows its tooltip on focus
4. Confirm the **disabled-unavailable-with-notice** example shows the
admonition and focusable disabled button pattern

**Studio (optional, requires a High Availability project)**

5. Go to Settings → General → **Pause project**. Tab to the button and
confirm it remains focusable, looks disabled, and shows the HA tooltip
on focus
6. Go to Database → Backups and find **Restore** on a scheduled backup
row. Confirm the same behaviour
2026-09-11 01:52:21 +00:00
Danny White 9f5b5ea6a7 fix(ui): standardise disclosure focus rings (#49660)
## What kind of change does this PR introduce?

Bug fix and design-system accessibility update.

## What is the current behavior?

Accordion and collapsible triggers do not consistently use the shared
focus treatment. Accordion focus can animate from the browser outline,
while call sites need to manage radius and focus geometry themselves.

## What is the new behavior?

Bare accordion and collapsible triggers use the rounded `focus-inset`
treatment by default, positioned 2px inside the control. Interactive
children composed with `asChild`, such as a standard `Button`, retain
their own focus treatment. Accordion triggers include a small default
horizontal hit-area padding (`px-2`) and retain an explicit outer-ring
option for standalone controls. The focus indicator is not animated.

Focused tests cover the default and opt-out accordion variants.

| Before | After |
| --- | --- |
| <img width="1792" height="384" alt="CleanShot 2026-09-07 at 15 40
23@2x"
src="https://github.com/user-attachments/assets/8c8fd99a-3669-465a-a7b2-d272ec92dec7"
/> | <img width="1784" height="498" alt="CleanShot 2026-09-07 at 15 39
52@2x"
src="https://github.com/user-attachments/assets/7a0b7de1-a4bc-48cf-b9fc-3421618806b9"
/> |
| <img width="1564" height="360" alt="CleanShot 2026-09-07 at 15 35
11@2x"
src="https://github.com/user-attachments/assets/34945180-e671-4dc2-981a-4c683d08f221"
/> | <img width="1552" height="378" alt="CleanShot 2026-09-07 at 15 36
19@2x"
src="https://github.com/user-attachments/assets/09f2c030-9ffb-4fa5-bf63-5e172326e8ef"
/> |

## Visual impact

Most call sites should look the same at rest. The exceptions are
accordion triggers that did not already set their own horizontal
padding: they pick up the new default `px-2`, `rounded-md`, and lose the
old blanket `transition-all` on the trigger (chevron rotation is
unchanged).

| Resting layout change | Why |
| --- | --- |
| Accordion triggers without an explicit `px-*` | Inherit new default
`px-2` and `rounded-md` |
| Accordion triggers with their own `px-*` | Unchanged (call-site
padding wins) |
| Collapsible triggers | Focus ring only; spacing comes from the child
element |
| Direct `focus-inset` users (table rows, etc.) | Focus ring only |

## To test

Use Tab to focus each disclosure trigger. Confirm the focus outline is
rounded, sits 2px inside the trigger, and appears immediately without a
dark flash.

Previews:
[design-system](https://design-system-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app)
·
[studio-staging](https://studio-staging-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app)
·
[docs](https://docs-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app)
·
[www](https://zone-www-dot-com-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app)

### Design system

-
[Accordion](https://design-system-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app/design-system/docs/components/accordion)
-
[Collapsible](https://design-system-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app/design-system/docs/components/collapsible)

### Studio (focus-only unless noted)

- [Account → Access tokens → **New
token**](https://studio-staging-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app/dashboard/account/tokens):
permission category accordions (already `px-4`)
- [Database →
Roles](https://studio-staging-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app/dashboard/project/_/database/roles):
role row collapsibles
- [Observability → Query
performance](https://studio-staging-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app/dashboard/project/_/observability/query-performance):
index suggestion accordions (already `px-4`)
- [Settings → JWT
(legacy)](https://studio-staging-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app/dashboard/project/_/settings/jwt/legacy):
migration help collapsible

### Docs

- Docs mobile nav (narrow viewport): accordion sections **(resting
layout: +px-2)**

### WWW

- [Pricing →
FAQs](https://zone-www-dot-com-git-dnywh-fixaccordion-focus-rings-supabase.vercel.app/pricing):
FAQ accordions **(resting layout: +px-2)**
- WWW mobile nav (narrow viewport): menu accordions **(resting layout:
+px-2)**

### Not worth opening manually

Sheets, modals, error fallbacks, and replication/cron/integration
advanced-settings accordions inherit the focus fix but need specific
flows or failure states to reach. Error-state troubleshooting accordions
(`TroubleshootingSections`) also gain **+px-2** when visible.


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

## New Features
- Accordion triggers now use an inset focus style by default, with an
option to use the standard ring style.
- Accordion and collapsible trigger styling can be customized more
flexibly.

## Accessibility
- Focus indicators appear consistently without flashing browser
defaults.
- Focus transitions are limited to visual properties that preserve
immediate keyboard focus visibility.

## Documentation
- Updated accessibility guidance explains inset focus behavior and
transition considerations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-07 16:59:40 +10:00
Danny White 37dded67d1 feat: unify SkipToContent across studio, docs, www, and design-system (#48314)
## 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 -->
2026-07-29 06:21:50 +10:00
Danny White c8aca8d3a0 chore(design-system): standardise keyboard focus rings (#41575)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

UI / design-system consistency (accessibility).

## What is the current behavior?

Keyboard focus rings are inconsistent across Studio and `packages/ui`:

- Custom Button uses thick `outline` with per-variant colours (brand /
grey / destructive / warning)
- Form controls use muted grey rings (`ring-background-control`)
- Tabs / NavMenu / Radio use soft brand `ring-ring`
- Studio `.inset-focus` uses dark green `outline-brand-600`

Related: [DEPR-354](https://linear.app/supabase/issue/DEPR-354).

## What is the new behavior?

One shared focus recipe, exposed as Tailwind `@utility` classes in
`packages/config/css/utilities.css`:

| Utility | Use when |
| --- | --- |
| `focus-ring` | Buttons, inputs, most controls (offset ring) |
| `focus-inset` | Dense/flush surfaces such as interactive table rows
(renamed from `inset-focus`) |

```txt
# focus-ring
outline-hidden
focus-visible:ring-2
focus-visible:ring-ring
focus-visible:ring-offset-2
focus-visible:ring-offset-background
```

Applied on Button, shadcn form controls, Menu/NavMenu, Command palette
trigger, Studio table rows, and related call sites. Documented in the
design-system accessibility docs. Variants do not change focus ring
colour.

When the ring must appear on a different element than the focused one
(e.g. Menu + ProductMenu `Link` via `group-focus-visible`, or InputGroup
via `:has()`), keep an explicit ring stack. The utilities bake in
`:focus-visible` on the same element.

## Additional context

**Out of scope**

- Full `packages/ui` / Studio / www sweep
- Legacy Studio form-group green box-shadow cleanup
- ESLint rule for bare `outline-none`

## Test plan

Prefer Safari (“hard mode” for `tabIndex`). Expect one soft brand ring
everywhere: not grey, not solid green outline.

### Design system

- [ ]
[Accessibility](https://design-system-git-dnywh-choreimprove-tab-focus-styles-supabase.vercel.app/design-system/docs/accessibility):
recipe docs match what you see
- [ ]
[Button](https://design-system-git-dnywh-choreimprove-tab-focus-styles-supabase.vercel.app/design-system/docs/components/button):
Tab primary / default / danger; same ring colour
- [ ] [Table → Row-level
navigation](https://design-system-git-dnywh-choreimprove-tab-focus-styles-supabase.vercel.app/design-system/docs/components/table#row-level-navigation):
Tab an interactive row; inset outline (`focus-inset`) sits inside the
row

### Studio

- [ ] **Org home → table view** (`/organizations/_` or org projects):
switch to the table layout, Tab onto a project row; inset outline sits
inside the row (list/card view uses CardButton, not `focus-inset`)
- [ ] **Project sidebar** (Database, Auth, Storage, …): Tab the main
product nav links; ring follows the focused item (not the nested section
menus like Tables / Roles)
- [ ] **Storage → Files**: Tab a bucket row; same inset outline as org
table rows
- [ ] **Project Settings → General** (or Compute and Disk): Tab through
inputs, checkboxes, switches, selects; same offset ring, no ring on
mouse click
- [ ] **Header ⌘K** (desktop width): Tab to the search control after
Feedback; same soft brand `focus-ring` (was a thicker
`ring-border-strong` before)
- [ ] **Table Editor or SQL Editor tabs**: focus a tab, Tab to × if
active; close shows a ring
- [ ] **Light + dark**: ring stays visible against both backgrounds
2026-07-22 12:10:07 -04:00
Danny White 6f19dfe18c fix(ui): bake explicit tabindex into interactive primitives (#47984)
## What kind of change does this PR introduce?

A11y fix for keyboard focus (esp. Safari), plus a lint rule to keep it
from regressing.

## What is the current behavior?

`Button` already defaults an explicit `tabIndex={0}` (#40458). Other
interactive primitives (Checkbox, bare triggers, etc.) still skip Tab
focus in Safari unless macOS Keyboard navigation is on. Raw `<button>`
call sites have no guardrail.

## What is the new behavior?

- Same explicit `tabIndex` default baked into Checkbox, Accordion
Trigger, Collapsible Trigger, Dropdown Menu / Popover / Dialog / Sheet /
Alert Dialog triggers, Table Head Sort, Command reset, sidebar actions,
and shadcn Button
- `supabase/require-explicit-tabindex` ESLint rule, ratcheted in Studio
(82 existing violations)
- Design-system accessibility docs list which primitives bake this in

Resolves [DEPR-621](https://linear.app/supabase/issue/DEPR-621)

## Additional context

### To test

Use **Safari** with macOS Keyboard navigation **off** (the default).
Chrome once for a sanity pass.

**Storage → Files bucket (list view) — Checkbox**

1. Open an empty folder (or one with only folders). Header “select all”
Checkbox is disabled and skipped by Tab — expected (`columnFiles.length
=== 0`).
2. Upload a file (e.g. an image). Header Checkbox enables.
3. Tab to it and toggle with Space. Row Checkboxes should also be
Tab-reachable.

**Storage → Analytics bucket → bucket details — Accordion Trigger**

On an Analytics bucket with no tables yet, Tab to the “Create your first
table via PyIceberg” Accordion Triggers and open/close with Enter/Space.

**Elsewhere (light smoke)**

- Bare Dropdown / Popover / Collapsible triggers still Tab + activate
- `*Trigger asChild><Button>` (Sheets, Dialogs, menus) still one Tab
stop; open/close fine
- Disabled controls stay out of the Tab order

### Later

Chip-away of the 82 raw Studio buttons (and remove exceptions from
rule). Prefer migrating those to `Button` from `ui` later.

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

* **Accessibility Improvements**
* Improved keyboard focus behavior across common interactive controls
(buttons, dialog/popover/dropdown/sheet triggers,
accordions/collapsibles, checkboxes, and sidebar actions).
* Disabled controls now default to `tabIndex={-1}`, while enabled
controls default to `tabIndex={0}`.
* Added explicit `tabIndex` handling for command reset and table sort
header controls.
* **New Features**
* Added a shared `getExplicitTabIndex` utility used by UI components to
standardize focus behavior.
* **Documentation**
* Updated accessibility guidance to clarify which components include
built-in focus/tabIndex handling and which require manual setup.
* **Developer Experience**
* Added an ESLint rule to enforce explicit `tabIndex` on raw button-like
elements, with corresponding baseline updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-18 00:18:45 +10:00
Danny Whiteandcoderabbitai[bot] 0d8ec0d011 feat(design-system): improve table docs (#41215)
* docs updates

* docs expansion

* minor docs fix

* improvements

* fix dependencies

* docs expansion

* more table examples

* map over data

* improve copy

* Update apps/design-system/registry/default/example/table-row-link-actions.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update apps/design-system/registry/default/example/table-row-link-actions.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fixes

* fix

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-12-11 13:38:26 +11:00
Danny WhiteandAli Waseem 74fa49d4bd fix(studio): keyboard focus on button (#40458)
* add necessary prop to main button component

* remove redundant props

* docs

* docs improvements

* typo

* tabbable sidebar items

* aria-label and aria-hidden

* update docs

* clarification

* fix link

---------

Co-authored-by: Ali Waseem <waseema393@gmail.com>
2025-11-25 23:48:58 +00:00
Danny White 552522c16f chore(studio): improve vector storage UI (#40596)
* improve table sheet

* improve metadata keys

* fix styling of foreign schema

* reset radio group

* docs

* resize sheet
2025-11-25 21:37:30 +11:00
Danny WhiteandJoshen Lim d653617cdd chore(studio): improve inline code styling (#40724)
* sweep language

* update class docs

* additional

* basic docs

* sweep relevant instances

* replace text-code

* additional in sweep

* Tiny fix

* prettier

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2025-11-24 16:34:30 +08:00