JGuang

按钮

Button · Astryx

交互预览
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0

按钮 · 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 按钮类型属性。
namestring表单提交的 HTML name 属性。
valuestring | number | readonly string[]表单提交的 HTML value 属性。
formstring通过 ID 将按钮与表单元素关联。
isLoadingbooleanfalse显示加载旋转器并禁用交互。通过实时区域播报"Loading"。
isInterruptiblebooleanfalseKeep 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.
isDisabledbooleanfalse禁用按钮。存在工具提示时,使用 aria-disabled 代替原生 disabled 以保持可聚焦。
iconReactNode图标元素。未显式指定尺寸的 Astryx Icon 在 sm/md 按钮中默认为 sm,在 lg 按钮中默认为 md。仅提供 icon 而不提供 children 时,按钮渲染为正方形的纯图标按钮。
isIconOnlybooleanfalseWhen 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.
widthSizeValue按钮宽度。数字按像素处理,字符串按原样使用(如 '100%' 表示全宽按钮)。默认按内容自适应宽度,不会超出容器;行宽不足时标签以省略号截断;纯图标按钮保持正方形。
childrenReactNode可选的可见内容覆盖;label 仍然是必需的(用于无障碍名称)。大多数情况使用 <Button label="Save" />。
endContentReactElement<IconProps> | ReactElement<BadgeProps>标签后方渲染的尾部图标或徽章。仅接受 <Icon> 或 <Badge>。纯图标按钮时忽略。颜色继承自按钮变体。
tooltipstring悬停时显示的提示文本。
onClick(e: MouseEvent) => void标准点击处理函数(从 ButtonHTMLAttributes 透传)。
clickAction(e: MouseEvent) => void | Promise<void>异步点击处理函数。返回的 Promise 处于 pending 状态时显示加载状态。
hrefstringWhen 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.
asComponentTypeCustom link component to use when href is provided (e.g. Next.js Link).
targetstringHTML target attribute when rendered as a link (e.g. "_blank").
relstringHTML 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