组件 · 操作
Button
A button for an action, with or without a label; its color says what kind of action it is, and its size follows where it sits.
import { Button } from "@botiverse/raft-ui";A button starts an action. Its color says what kind of action it is: pink for the one action a screen exists for, an ink border for the rest, no frame for cancel and minor actions, orange for what interrupts and red for what cannot be undone. An icon-only button is the same component at an icon size.
When to use
| Situation | Use |
|---|---|
| An action with a label | Button |
| An action whose icon is understood without words: search, more, back, close, copy, attach | Button at an icon size; see Icon buttons |
| An action whose icon needs words to be understood | Button with a label |
| The quietest control on a screen, action or not | TextLink: text that underlines on hover |
| A list of actions on one thing | DropdownMenu |
| Picking a value | Select |
Variants
| Variant | Use for | Examples |
|---|---|---|
variant="primary" | The action the screen exists for: a pink sticker | Send, Create Channel, Bring an Agent |
variant="default" | Every other action, including one that every row of a list repeats: an ink border | Assign, Approve, Claim |
variant="ghost" | Cancel and minor actions inside a surface that draws its own edge: no frame | Cancel in a dialog |
variant="warning" | Actions that interrupt but can be undone: an orange sticker | Stop Wug, Leave Channel, Reset |
variant="danger" | Actions that cannot be undone, on the confirm button of a confirmation dialog: a red sticker | Delete Agent |
variant="on-frame" | A frameless button on the frame: the rail, the web top bar, the desktop title bar | The workspace button |
accent renders the same as primary, and outline, information, muted and success the same as default. Use the names in the table.
On the frame, a button takes the frame's ink (foreground-on-frame) and lays fill-frame-medium over itself on hover, on press and while its popup is open. The text and icon sizes become the 36px frame item.
Which color
Emphasis is relative to the surface that holds the button, not a property of the action. A frameless Cancel reads as a control inside a dialog and as a line of text on a page; the action did not change, what encloses it did. The test is whether the surface draws its own edge and takes focus while it is open — a dialog, a sheet, a menu, a popover, a toolbar, a card, a panel header all do; a page and a section of a page do not. When a surface is not on that list, apply the test rather than guessing: if the button would be the only thing marking where it sits, give it a border.
Every button is flat: the ink border or the sticker marks it as a control, never a shadow (Elevation). Colored buttons carry ink type (Color).
- Make the primary action pink. Use at most one pink button per screen; a screen may have none. Unread counts and attention dots don't count toward the one.
- When a screen has a second primary action, make it
default. - Give every other action an ink border (
default). - Make cancel and minor actions frameless (
ghost) inside a surface that draws its own edge. On a page, give the same action an ink border (default). - Use TextLink for the bottom of the ladder: where even a frameless button is too much weight, such as Forgot Password on a sign-in page or a See All beside a section title. It may carry an action, but it is the quietest control on the screen, never a way to fit one more button into a crowded row. It is not a Button variant: a text link is a word in a line, not a box, so it takes the size of the text around it, and a Button would bring a height, a border and padding that each have to be taken off again.
- Use orange (
warning) for actions that interrupt but can be undone: stop, leave, reset. - Use red (
danger) only for actions that cannot be undone, and only in a confirmation dialog (AlertDialog). Don't color a destructive item in a menu (DropdownMenu). - Don't use
danger-secondaryordanger-outline: red text on an outline button puts red outside a confirmation dialog. - Never make a button yellow: yellow means "you" (Color).
Sizes
Size follows the purpose. The same purpose has the same size everywhere, and there are no other sizes. lg, icon-lg and xl take the control height (Spacing): a finger's size on a phone and a touch screen, and Feishu's PC sizes on a desktop (from 768px with a mouse or trackpad). On a desktop lg is as tall as md, with its wider padding and its text-subhead label. Still pick lg by purpose, as the table says: the same code runs on a phone, where lg is the finger-sized button the purpose needs, and on a desktop it costs nothing in height.
| Size | Height | Use for |
|---|---|---|
size="lg" | 44px on a phone; 32px, 44px hit area, on a desktop | Dialog and drawer footers, form submit buttons, a full-width column of actions at the bottom of a panel (Remove Member, Leave Channel, Archive Channel) |
size="md" (default) | 32px, 44px hit area | Actions in list rows, cards, banners, toolbars and panel headers |
size="xl" | 52px on a phone, 40px on a desktop | Only the full-width pink button across a sign-in, sign-up or setup form |
These three and the two icon sizes, icon-md and icon-lg, are all the sizes. xs and sm render exactly as md, and icon-xs and icon-sm exactly as icon-md. They are deprecated aliases, kept until raft-ui 1.0. node node_modules/@botiverse/raft-ui/codemods/button-sizes.mjs <src> renames them, and nothing on screen changes.
When a place looks too tight for a 32px button, change the container, not the button. A banner with a button is a standard banner, not sm.
Size by place
Every place has one answer. Where a part of the container sets the place, follow it: the size comes with the place, not with the button.
| Place | Size | Who decides |
|---|---|---|
| Dialog, drawer and sheet footers | lg | DialogFooter: Cancel and the submit button are lg. DialogCommit moved into a phone's header is md |
| A form's submit; Save on a card that holds fields | lg | You, because fields are above it |
| Beside a field: copy a link, remove a row, add | lg, icon-lg | You: it takes the field's height (Icon buttons) |
| A full-width button across a sign-in, sign-up or setup form | xl | You |
| A full-width column of actions at the bottom of a panel | lg | You |
| A banner's button and its Dismiss | md | BannerAction places them; BannerClose is its own 32px × |
| A toast's action | none | ToastAction is the toast's own text action; it takes no Button size |
| A desktop panel header | none | PanelAction and PanelToggleAction are their own buttons, 32px on a desktop and 44px on a phone |
| Card and message action card actions; a single action in a settings section | md | You |
| The end of a list row or a table row | md, icon-md | You |
| Toolbars, filter bars, empty states, the bottom of a popover, Load More | md, icon-md | You |
| A phone top bar; the media viewer | icon-lg | The top bar, Lightbox |
| The rail, the web top bar, the desktop title bar | on-frame at md or icon-md (36px) | The frame |
| A menu, a phone's overflow menu | No Button: a menu item | DropdownMenu, the sheet |
Inside a floating toolbar
A toolbar that floats over content shows on every message under the pointer, so it is more compact than the page. The message hover toolbar and the text-selection toolbar are 36px high, with 28px buttons 4px from the frame and 4px apart, at radius-chip. The container sizes them: MessageItemToolbarButton and MessageTextSelectionToolbarButton are the toolbars' own parts, and MessageMultiSelectToolbarActions brings a md Button in it to 28px (MessageItem). The only other 28px button is SidebarSectionControl on a sidebar section's 16px header line. 28px is not a Button size, so don't make a Button 28px yourself.
Icon buttons
| Where | Props | Size and icon |
|---|---|---|
| A phone top bar, or beside a field | variant="ghost" size="icon-lg" | 44px, 22px icon on a phone; 32px, 18px icon, 44px hit area on a desktop |
| A desktop panel header, composer tools, copy in cards and command lines | variant="ghost" size="icon-md" | 32px, 18px icon, 44px hit area |
| A tool that stands alone in content | variant="default" at an icon size | Ink border on layer-card |
| Send | variant="primary" at an icon size | A pink sticker |
| The rail, the web top bar, the desktop title bar | variant="on-frame" size="icon-md" | 36px, 20px icon |
- Make an icon button next to an input (copy a link, remove a row) the input's height:
size="icon-lg", which follows the field's height on a phone and on a desktop. - Put several icon buttons in a panel header inside
PanelActions(Panel), which aligns them to the end and spaces them. - Mark a bell that has something new with an attention dot on its top-right corner.
- Count the send button as the screen's pink button.
Icon sizes by context are in Iconography.
States
Hover, press, focus and disabled follow States: every button hovers and presses with the medium tier (Hover and press): a sticker lays sticker-medium over itself, a paper-colored button fill-medium.
- Disabled, frameless: a frameless icon button (
ghostat an icon size) and anon-framebutton only turn their icon and textforeground-disabled. No fill or border appears. - Disabled, colored: a disabled pink, orange or red button is no longer a sticker: it takes the shared disabled look.
- Loading:
loadingshows the pixel spinner (Spinner) and keeps the button's width and color. Use it for any wait a button started (Choosing a component).
Content
- Start with a verb and name the result: Send for Review, not OK or Submit. In a dialog the title names the object, so its main button says the verb alone: Create in Create Channel (Writing).
- Use Title Case (Writing), and repeat the button's words in the dialog it opens (Writing).
- Put an icon before the label (
data-icon="inline-start"), and Return or an arrow after it (data-icon="inline-end"). - While loading, say what is happening: Creating….
Accessibility
Button wraps Base UI Button and renders a native <button>. For navigation, render a real link instead: render={<a href="…" />}.
- Give every icon-only button an
aria-labelin Title Case (Add Reaction). On devices with a pointer, add a Tooltip with the same name and the shortcut. - While
loading, the button isaria-disabledand the spinner is named byloadingLabel. SetloadingLabelto what is happening. - Use
focusableWhenDisabledwhen a Tooltip has to explain why an action is unavailable: the button stays focusable and keeps the disabled look.
API
Button
| Prop | 类型 | 默认值 |
|---|---|---|
| boolean | false | |
| string | "Loading" | |
| `md` (32px) and `icon-md` for rows, cards, banners, toolbars and panel headers; `lg` and `icon-lg` (the control height) for dialog footers, forms and beside a field; `xl` for a full-width call to action. The size follows the place: see Button › Size by place. | "md" | "lg" | "xl" | "icon-md" | "icon-lg" | DeprecatedButtonSize | "md" |
| "primary" | "accent" | "warning" | "danger" | "default" | "outline" | "information" | "muted" | "success" | "danger-secondary" | "danger-outline" | "ghost" | "on-frame" | "default" | |
| Whether the button should be focusable when disabled. | boolean | false |
| Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | boolean | true |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
相关组件
改动
- 0.8.0 · Breaking Changes
Buttonno longer hasvariant="link"orsize="inline". Underlined text is nowTextLink. A link inside a sentence (a banner's "View Billing") becomes<TextLink variant="strong">; a link on its own (Forgot Password, See All) becomes<TextLink>, which is muted.TextLinkhas no height, border or padding, so drop the classes that took them off. - 0.6.1 · Added
Buttonvariant="on-frame": a frameless button on the yellow frame (rail, web top bar, desktop title bar); on the framemdandicon-mdare the DS 36px frame item. - 0.6.0 · Changed
Buttontransitions are colour-only (notransition-all);xs/smrender asmd,icon-xs/icon-smasicon-md.