Files
supabase/apps/design-system/content/docs/accessibility.mdx
T
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

114 lines
4.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Accessibility
description: Make Supabase work for everyone.
---
Accessibility is about making an interface work for as many people as possible across as many circumstances as possible. All of us lean on affordances that accessible experiences provide:
- Keyboard navigation
- Legible and resizable elements
- Large tap targets
- Clear and simple language
## Checklist
About to push some code? At a minimum, check your work against this list:
- Are interactive page elements [keyboard-focusable](#focus-management)?
- Are all elements announcable by a [screen reader](#screen-reader-support)?
- Are textual elements legible and scalable?
- Can I use this on a smaller and/or older device?
## Focus management
All interactive page elements should be reachable by keyboard. Given the below inconsistency between devices and browsers, add `tabIndex={0}` to all buttons, links, and non-text inputs, ideally at the component level. Consider tying the state of `tabIndex` to the `disabled` state of a component, if applicable.
Chromium-based browsers and Firefox handle this automatically via the Tab key. Safari, by default, requires the Option key to also be held down. Enabling _Keyboard navigation_ on macOS Settings [removes this requirement](https://mayank.co/blog/safari-focus/#keyboard-navigation) but makes links non-tabbable as a result.
Interactive page elements should also provide visual feedback upon selection via a `focus-visible` state. We use consistent focus styles such as `inset-focus` so users recognize this state instantly.
[Button](components/button) has all of the above built-in. Bespoke interactive elements however, such as the below interactive [Table Row](components/table#examples), require these props to be added manually:
```tsx showLineNumbers {4-14}
<TableRow
key={id}
className="relative cursor-pointer h-16 inset-focus"
onClick={(event) => {
if (event.currentTarget !== event.target) return
handleBucketNavigation(bucket.id, event)
}}
onKeyDown={(event) => {
if (event.currentTarget !== event.target) return
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
handleBucketNavigation(bucket.id, event)
}
}}
tabIndex={0}
>
<TableCell>{name}</TableCell>
</TableRow>
```
Consider also affordances like `ctrl` and `meta` key support for opening in a new tab. Anything that you can do with a mouse input should be replicable by keyboard.
See the examples within [Table](components/table#examples) for more.
### Radio groups
Single-select option groups such as `<input type="radio">` should behave as a single control. Only the first item of radio groups should become focused with the Tab key. The next Tab should move focus from the group to the next focusable control.
Individual options inside of a group can be reached by arrow keys (↑ ↓ ← →). Space is the canonical key to activate radio options, with Enter being a secondary affordance.
### 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:
- Search and filtering
- Pagination or virtualization
- “Jump to” shortcuts to skip ahead
## Screen readers
Textual elements are supported out-of-the-box by screen readers.
### Imagery
Images should have their contents described with an `alt` attribute. Write an objective description of the content rather than its context. For example:
```tsx showLineNumbers {2}
// Correct: painting a picture with words
<img src="beagle.png" alt="A tricolor beagle galloping through a grassy field, ears in the air" />
// Incorrect: Unhelpful context
<img src="beagle.png" alt="Our logo" />
```
Icons and other visual elements that aren’t strictly images should use the `aria-label` attribute. For example:
```tsx showLineNumbers {2}
<BucketTableCell>
<BucketIcon aria-label="bucket icon" size={16} />
</BucketTableCell>
```
Visual elements that are _purely_ visual aids may be removed from the accessibility tree via the `aria-hidden` attribute. For example:
```tsx showLineNumbers {2}
<BucketTableCell>
<ChevronRight aria-hidden={true} size={14} />
</BucketTableCell>
```
Never use `aria-hidden={true}` on focusable elements, since these are critical pieces of functionality.
### Scaffolding
Some scaffolding elements only make sense visually, in the context of surrounding visual content. For example: a table column for actions may not have a visual _Actions_ label because its purpose is obvious (by nearby contents) to a sighted person. For everyone else’s sake, this column should be titled with `sr-only` text:
```tsx showLineNumbers {2}
<TableHead>
<span className="sr-only">Actions</span>
</TableHead>
```