All components (204)
Side panel
Layers · Drawer · .amset-panel
What it is and when to use it
A panel that slides in from one side of the screen: filters, the detail of a row, a short form. Inside it is a modal dialog, so focus enters, stays inside and returns to the trigger on closing.
When to use it
- Filters for a list, with "Apply" and "Clear" in the footer.
- The detail of a row without losing sight of the list.
side:end(default) orstart, which follow the language, andtoporbottom.actionsfor the footer buttons: it receives the function that closes the panel.loadingwhile the content arrives: the body shows a skeleton and the title and the buttons stay.dismissable={false}(clicking outside does not close) orkeyboardDismissDisabled(Escape does not close) for a half-filled form that must not be lost to an extra click.
When not to
- A short decision in the center:
Dialog. - A small panel attached to the button that does not block the page:
Dropdown. - If it is a whole page with its own address, it is not a panel: it is navigation.
Keyboard: on opening, focus enters and stays inside. Escape closes and focus returns to the trigger; clicking outside also closes. If both exits are turned off, what is inside has to offer one.
Accessibility: it is a modal browser <dialog>, with the rest of the page inert while it is open. It is announced as dialog with the title as its name and description as its accessible description. The cross is on by default (withCloseButton) and is named with closeLabel ("Close the panel"). While loading, the body carries aria-busy and announces loadingText ("Loading…"). If the body scrolls, it takes focus so that it can be traversed with the keyboard.
Fine-tuning: CSS variables --amset-panel-*: -width (on the sides: 20, 28 or 40 rem depending on the size), -height (top and bottom), -padding, -background and -from, which is where it enters from. Each side brings its own: the bottom one rises, the top one drops and the side ones slide in sideways. The veil is the browser's ::backdrop. The body is the only thing that scrolls.
What it looks like
<Drawer title="Filters" trigger={<Button variant="outline">Open filters</Button>}>
<Filtros />
</Drawer>Props
Import it with import { Drawer } from '@amset/ui'. Required props are marked.
| Prop | Type | What it does |
|---|---|---|
childrenrequired | ReactNode | — |
titlerequired | string | — |
actions | (close: () => void) => ReactNode | Footer buttons. Receives `close`, like the dialog. |
className | string | Merged with the library classes; it does not replace them. |
closeLabel | string | — |
description | string | — |
dismissable | boolean | Pressing outside closes. By default yes. With `false`, the panel only goes away through its own buttons: for a half-filled form that must not be lost by one click too many. |
keyboardDismissDisabled | boolean | Escape closes. By default yes. Same case as the previous one, and with the same warning: if both are turned off, what is INSIDE has to offer a way out, or whoever does not use a mouse is locked in. |
loading | boolean | The content is arriving. A skeleton is shown instead of `children`, and the body is marked busy. The title and the buttons stay: whoever opened it knows what it is about and can close it without waiting. |
loadingText | string | What is announced to the reader while loading. "Loading…" by default. |
onOpenChange | (open: boolean) => void | — |
open | boolean | — |
side | PanelSide | Which side it enters from, and which side it slides from when appearing. `start` and `end` follow the language. |
size | "sm" | "md" | "lg" | On the sides, 20, 28 or 40 rem wide; top and bottom, 30, 50 or 80% of the height. |
trigger | ReactElement<unknown, string | JSXElementConstructor<any>> | — |
withCloseButton | boolean | — |
It also accepts the attributes of the element it renders: className, style, data-*, aria-* and its events.
CSS variables
These CSS variables are how you adapt it. Declare them on the component's selector, not on :root: the stylesheet redeclares them in the rule itself, and a declaration on the same element always wins.
| Variable | Default |
|---|---|
--amset-panel-width | 28rem |
--amset-panel-height | 50vh |
--amset-panel-padding | var(--amset-space-5) |
--amset-panel-background | var(--amset-surface) |
--amset-panel-from | translateX(16px) |
How to change it
.amset-panel {
--amset-panel-width: ...;
--amset-panel-height: ...;
}In the test bench
Every state and variant, to try one at a time, on its page at ui.amset.tech:
