按钮 · Button
触发保存、提交或确认等操作;可选择不同强调程度、尺寸及加载状态。
安装与前置条件
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0
需要 React 19+,按顺序导入基础 CSS 和主题,并设置 Theme Provider。参见开始使用。
示例与使用方式
- 按钮 · 综合展示 1:首先体验这个示例,再查看代码理解组件的组合方式。
- 按钮 · 浮动 2:可交互查看这一用法的行为,复制代码时保留它实际使用的属性与导入。
- 按钮 · 尺寸变化 / 样式变化 / 样式 3:可交互查看这一用法的行为,复制代码时保留它实际使用的属性与导入。
- 按钮 · 样式变化 / 样式 4:可交互查看这一用法的行为,复制代码时保留它实际使用的属性与导入。
- 按钮 · 交互示例 5:可交互查看这一用法的行为,复制代码时保留它实际使用的属性与导入。
- 按钮 · 包含图标 6:可交互查看这一用法的行为,复制代码时保留它实际使用的属性与导入。
API 与上游详细参考
优先使用 0.7.1 官方中文说明;上游尚未提供中文的条目保留英文。属性名、类型及默认值保持原生 API,不把示例状态名称当成组件属性。
Button
Button triggers an action when clicked. Use it for form submissions, confirmations, navigation, or any interaction that needs a clear call to action.
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| label * | string | 无障碍标签;纯图标按钮时用作 aria-label。 | |
| variant | 'primary' | 'secondary' | 'ghost' | 'destructive' | 'secondary' | 视觉样式变体。 |
| size | 'sm' | 'md' | 'lg' | 'md' | 尺寸变体。 |
| elevation | 'none' | 'low' | 'med' | 'high' | 'none' | 浮动按钮(如 FAB)的静止阴影深度。none 为默认扁平按钮;在 ButtonGroup 内忽略。 |
| type | 'button' | 'submit' | 'reset' | 'button' | HTML 按钮类型属性。 |
| name | string | 表单提交的 HTML name 属性。 | |
| value | string | number | readonly string[] | 表单提交的 HTML value 属性。 | |
| form | string | 通过 ID 将按钮与表单元素关联。 | |
| isLoading | boolean | false | 显示加载旋转器并禁用交互。通过实时区域播报"Loading"。 |
| isInterruptible | boolean | false | Keep the button clickable while a clickAction is pending: the spinner and aria-busy still show, but the button is not disabled and the action is not deduped, so a re-click lands and interrupts the in-flight action with a fresh one. |
| isDisabled | boolean | false | 禁用按钮。存在工具提示时,使用 aria-disabled 代替原生 disabled 以保持可聚焦。 |
| icon | ReactNode | 图标元素。未显式指定尺寸的 Astryx Icon 在 sm/md 按钮中默认为 sm,在 lg 按钮中默认为 md。仅提供 icon 而不提供 children 时,按钮渲染为正方形的纯图标按钮。 | |
| isIconOnly | boolean | false | When true, renders as a square icon-only button with label as aria-label. Requires icon. Tip: for a dedicated icon-only button component, use IconButton from '@astryxdesign/core/IconButton' instead. |
| width | SizeValue | 按钮宽度。数字按像素处理,字符串按原样使用(如 '100%' 表示全宽按钮)。默认按内容自适应宽度,不会超出容器;行宽不足时标签以省略号截断;纯图标按钮保持正方形。 | |
| children | ReactNode | 可选的可见内容覆盖;label 仍然是必需的(用于无障碍名称)。大多数情况使用 <Button label="Save" />。 | |
| endContent | ReactElement<IconProps> | ReactElement<BadgeProps> | 标签后方渲染的尾部图标或徽章。仅接受 <Icon> 或 <Badge>。纯图标按钮时忽略。颜色继承自按钮变体。 | |
| tooltip | string | 悬停时显示的提示文本。 | |
| onClick | (e: MouseEvent) => void | 标准点击处理函数(从 ButtonHTMLAttributes 透传)。 | |
| clickAction | (e: MouseEvent) => void | Promise<void> | 异步点击处理函数。返回的 Promise 处于 pending 状态时显示加载状态。 | |
| href | string | When provided, renders the button as a link element (<a> or custom link component). The destination follows the shared navigation rule described on the Link href prop. | |
| as | ComponentType | Custom link component to use when href is provided (e.g. Next.js Link). | |
| target | string | HTML target attribute when rendered as a link (e.g. "_blank"). | |
| rel | string | HTML rel attribute when rendered as a link (e.g. "noopener noreferrer"). |
使用建议
- Reserve primary for the single most important action in the view. Use secondary or ghost for everything else based on emphasis.
- Write labels that describe the action ("Save changes", "Delete account", "Send invite"), not vague labels like "OK" or "Click here".
- Show a loading state for actions that take time, like saving or submitting, so the user knows it is working.
- Always provide a label for icon-only buttons so screen readers can announce what the button does. Add a tooltip for sighted users.
- 避免:Place more than one primary button in the same view; this dilutes the visual hierarchy.
- 避免:Use the destructive variant without a confirmation step for irreversible actions like deleting data.
- 避免:Use a button for navigation. If it only takes the user to another page, use a link instead. Buttons are for actions like saving, deleting, or submitting.
Actions in narrow rows
import {useState} from 'react';
import {Button} from '@astryxdesign/core/Button';
import {Card} from '@astryxdesign/core/Card';
import {Heading} from '@astryxdesign/core/Heading';
import {HStack} from '@astryxdesign/core/HStack';
import {Icon} from '@astryxdesign/core/Icon';
import {IconButton} from '@astryxdesign/core/IconButton';
import {StackItem} from '@astryxdesign/core/Stack';
import {TextInput} from '@astryxdesign/core/TextInput';
import {VStack} from '@astryxdesign/core/VStack';
// On a phone (or any card narrower than its actions), a labelled Button
// shrinks with its row and truncates its label with an ellipsis instead of
// overflowing or being clipped by the card. The full label stays the
// accessible name. Icon-only buttons keep their square size.
function ReviewersCard() {
const [emails, setEmails] = useState('');
return (
<Card maxWidth={560}>
<VStack gap={4}>
<HStack gap={2} vAlign="center">
<StackItem size="fill">
<Heading level={3} maxLines={1}>
Design review
</Heading>
</StackItem>
<Button label="Request review from the design team" size="sm" />
<IconButton
label="More review options"
icon={<Icon icon="moreHorizontal" />}
variant="ghost"
size="sm"
/>
</HStack>
<TextInput
label="Reviewer emails"
placeholder="name@example.com"
value={emails}
onChange={setEmails}
/>
{/* In a 320px form footer the long primary action truncates.
StackItem size="static" keeps the short Cancel label whole. */}
<HStack gap={2} hAlign="end">
<StackItem size="static">
<Button label="Cancel" variant="secondary" />
</StackItem>
<Button label="Send invitations to all 12 reviewers" variant="primary" />
</HStack>
</VStack>
</Card>
);
}
主题 API
{
"targets": [
{
"className": "astryx-button",
"visualProps": [
"size",
"variant",
"elevation"
]
}
],
"vars": [
{
"name": "--_button-radius",
"description": "圆角半径",
"default": "var(--radius-element)",
"private": true
},
{
"name": "--button-focus-offset",
"description": "焦点环轮廓偏移",
"default": "var(--focus-outline-offset)"
},
{
"name": "--button-icon-only-aspect",
"description": "纯图标按钮的宽高比",
"default": "1 / 1"
}
],
"derived": [
{
"property": "borderRadius",
"vars": [
"--_button-radius"
]
}
]
}
无障碍
- Text label: Button text must have at least 4.5:1 contrast with the button background in every state. For Hover and Pointer down, measure the final background after the overlay is applied. — 4.5:1
- Essential icon or spinner arc: An icon used instead of text must have at least 3:1 contrast with the button background. The moving spinner arc must also meet 3:1. An icon beside a visible label does not need its own check. — 3:1
- Badge text: Badge text inside a button must have at least 4.5:1 contrast with the Badge background. Check all 14 built-in Badge colors in Rest, Hover, and Pointer down on page and surface backgrounds. This covers 336 pairs per mode. Check custom end content separately. — 4.5:1
- Visible control boundary: The button edge needs 3:1 contrast only when users need it to see the control. A text-only button can rely on its label. — 3:1 if needed
- Keyboard focus indicator: The focus outline needs at least 3:1 contrast with the area around the button. Check every style. Destructive buttons use a red outline. — 3:1
- Disabled appearance: Disabled controls do not need to meet these contrast ratios. — Not required
组件结构
- Icon: A leading icon that reinforces the label, like a trash icon on a Delete button.
- Label: The visible text describing the action. Also used as the accessible name.
- End content: A trailing badge or icon after the label, like a notification count or dropdown arrow.
- Spinner: Replaces the icon during loading to show the action is in progress.
完整示例的额外依赖
部分示例使用独立图标包。示例清单中的每条安装命令包含该示例实际需要的依赖;若要运行本页全部示例,还需安装:
npm install @heroicons/react@2.2.0