ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Astryx Button 组件族契约(Family Contract)全解析:从五个成员组件到十二项跨组件不变式

Astryx Button 组件族契约(Family Contract)全解析:从五个成员组件到十二项跨组件不变式 Astryx Button 组件族契约Family Contract全解析从五个成员组件到十二项跨组件不变式【免费下载链接】astryxAn open source design system thats fully customizable and agent ready项目地址: https://gitcode.com/GitHub_Trending/as/astryx导读本文以 Astryx 设计系统仓库中的 docs/families/buttons.mdButton family contract为核心系统讲解 Button、IconButton、ToggleButton、ButtonGroup、ToggleButtonGroup 五个成员组件如何共享同一套行动表面action surface契约同时保留各自语义。你将掌握组件族成员的准入规则、可访问名称与原生语义的强制要求、异步 Action 模型fire-once 与可中断的区别、尺寸与 elevation 的归属原则以及 ButtonGroup 的 roving focus 键盘模型。所有结论均有对应源码路径可供查证适合设计系统维护者、组件库贡献者与使用 Astryx 构建应用的开发者阅读。一、什么是组件族契约Button family 的定位在 Astryx 的知识体系中组件族契约family contract负责声明一组兄弟组件共享的行为例如输入尺寸、覆盖层关闭、状态呈现等。组件规格component spec通过链接指向所属家族而不是复制共享规则见 docs/families/README.md。Button family 契约描述的是一个连贯的按钮系统无论一次操作是带文字标签、纯图标、需要保持按压态、独立存在还是被分组排列用户遇到的都应该是同一个按钮系统。家族成员共享相同的控件几何、可访问名称要求、交互反馈、异步操作模型与表面surface所有权但各自仍保有与瞬时动作、导航目的地、持久按压态或分组相关的专属语义。该契约当前为current状态由cixzhang审批2026-09-04review_triggers涵盖 behavior、layout、theming、accessibility 与 public-api 五个维度验证依据为 Button.test.tsx、IconButton.test.tsx、ButtonGroup.test.tsx 与 ToggleButton.test.tsx。二、成员规则什么算按钮契约明确成员资格取决于公开职责public responsibility而不是是否 import 了 Button 或是否渲染了button元素。2.1 成员MembersButton通用行动表面拥有原生按钮语义、焦点与按压反馈、尺寸、视觉变体、加载呈现、链接模式与静止 elevation。IconButtonButton 的显式纯图标投影继承表面契约而不另起一套操作模型。ToggleButton复用 Button 表面增加受控的持久按压态、按压态视觉与可中断的按压 Action。ButtonGroup拥有连接式操作组几何、roving focus、继承的尺寸与禁用态以及一份共享的连接式 elevation。ToggleButtonGroup拥有单选/多选状态与独立 ToggleButton 表面的布局不因把选择聚在一起而成为连接式视觉表面。2.2 协作者Collaborators——不是成员Spinner提供 pending 反馈Tooltip提供可见说明LinkProvider提供 Button 的导航渲染器SizeContext提供继承的控件尺寸DropdownMenu可能为 ButtonGroup 贡献一个按钮触发器。这些协作者只是被组合进成员各自保留自己的契约不会变成按钮族成员。2.3 被排除的组件Link的首要目的是导航而非按钮表面Switch、CheckboxInput、RadioList表达的是设置或表单值而非按钮动作SegmentedControl 与 TabList在自己的选择/导航契约下切换视图或目的地仅仅把 Button 用作触发器或动作插槽的组件仍归属其自身的组件族。这一按公共责任分类的规则在源码中同样成立IconButton 只是将isIconOnly固定为 true 的薄封装见 IconButton.tsxToggleButton 只是以variantghost、isInterruptible渲染 Button 的薄封装见 ToggleButton.tsx但它们各自拥有专属语义。三、五个成员的分工与共享所有者契约明确各成员的表面所有权边界Button 拥有共同动作表面而IconButton 是它的纯图标投影不创建另一个操作模型——这解释了为什么 IconButton 在类型层面直接复用ButtonPropsOmit 掉isIconOnly、children、endContent并把icon设为必填见 IconButton.tsx。ToggleButton 复用 Button 表面并叠加持久按压态ToggleButtonGroup 不做成连接式视觉表面因为分组选择不等于共享表面。当协作者被组合进成员时Spinner、Tooltip、Icon、Link、DropdownMenu 的行为不会因此变成按钮专属。四、核心概念轴Canonical Concepts契约用一张概念表定义了六个可变的语义轴这是理解整个家族的关键概念取值/状态默认语义稳定性激活模型 activation model瞬时动作、导航、持久按压Button 与 IconButton 只激活一次ToggleButton 表示被保持的按压态shipped distinction内容模式 content mode可见标签、自定义可见内容、纯图标每个控件必须有必填的可访问label纯图标将其视觉隐藏shipped family rule尺寸 sizesm、md、lgmd成员显式尺寸优先于继承的组尺寸shipped family axis视觉状态 visual staterest、hover、focus、active、disabled、loading适用处含 pressed状态保持控件几何与可访问用途shipped family rule异步动作 async action无、fire-once、可中断持久动作普通动作在 pending 时去重持久开关保持可逆shipped family distinction高度 elevationnone、low、med、highnone绘制可见表面的元素拥有阴影shipped family axisToggleButton 采纳已获批准分组 grouping独立、带间距集合、连接表面语义与绘制的包含关系决定所有权仅分组本身不足以决定family rule4.1 源码中的尺寸轴Button 的尺寸映射到共享 token 的高度契约sm/md/lg分别对应--size-element-sm、--size-element-md、--size-element-lg见 Button.tsx。纯图标成员在解析尺寸下必须是正方形——通过aspectRatio: 1 / 1保证Button.tsx。图标尺寸随按钮尺寸缩放sm/md 为 16px、lg 为 20pxButton.tsx。这与契约 FR7共享尺寸保持家族几何、图标仅按钮外尺寸不变完全对应。4.2 源码中的 elevation 轴Button 的elevation默认nonelow/med/high映射到--shadow-low、--shadow-med、--shadow-highButton.tsx。契约 FR8 要求绘制表面的元素拥有阴影独立按钮自己拥有静止 elevation而连接组内的按钮被强制渲染为none因为阴影属于组共享的表面——这一点在源码中直接可见!buttonGroup elevationStyles[elevation]Button.tsx且 theme props 中也会把组内按钮的 elevation 覆盖为noneButton.tsx。五、十二项跨组件不变式FR1–FR12这是契约的技术核心每一项都是可测试的强制规则FR1 — 每个控件都必须有可访问名称。必须传入非空的label纯图标控件通过aria-label暴露标签图标不能替代程序化名称。源码中label被声明为必填 propButton.tsxisIconOnly时aria-label自动生效Button.tsx。FR2 — 原生动作语义是默认。瞬时或持久动作渲染可操作的 button支持键盘激活、focus-visible 反馈typebutton除非组件文档化的表单模式另有规定Button 的href模式是显式导航变体同时遵循family:navigation-destinations见 docs/families/navigation-destinations.md。源码中默认type buttonButton.tsx。FR3 — 禁用即不可操作。禁用成员不得调用回调或 Action当禁用原因需要焦点时可使用可聚焦的aria-disabled语义但仍拦截激活组的禁用态覆盖成员可用性。源码中带 tooltip 的禁用按钮会退化为aria-disabled而非原生 disabled以保持键盘用户可达 tooltipButton.tsx。FR4 — pending 反馈保持用途与几何。loading/Action 挂起时必须设置aria-busy、保持控件尺寸稳定、用 Spinner 呈现而不改变可访问用途显式禁用样式与 pending 样式保持区分。源码在加载态设置aria-busyButton.tsx。FR5 — 回调与 Action 顺序一致。同时暴露同步回调与 Action 时回调先运行阻止该事件即阻止 Actionfire-once 动作在 pending 时去重显式可中断的持久动作可接受新激活并替换在途意图。源码通过actionInFlightRef实现去重而可中断调用方如 ToggleButton跳过该守卫Button.tsx。FR6 — 持久态是显式且可逆的。ToggleButton 必须用aria-pressed暴露有效状态并在激活时请求下一个受控值pending 态可以乐观更新但新激活必须基于在途的有效值而非陈旧的已提交值。源码用useOptimistic并让nextPressed !isPressed基于乐观值而非已提交值实现ToggleButton.tsx。FR7 — 共享尺寸保持家族几何见 4.1 节。FR8 — elevation 属于绘制表面见 4.2 节。FR9 — elevation 与交互状态无关。pressed、hover、focus、active、loading、disabled、icon 状态不得改变 elevation 的所有者或层级。FR10 — 公开、渲染与主题状态一致。成员暴露视觉轴时公开 prop、实际渲染的data-*状态与文档化的主题视觉 prop 必须描述其真实绘制值wrapper 不得把被忽略的子值报告为有效输出。源码通过themeProps(button, {...})输出渲染层实际值Button.tsx。FR11 — 按钮专属组有一个可访问所有者。ButtonGroup 与 ToggleButtonGroup 必须暴露组标签并传播文档化的尺寸与禁用默认值同时不剥夺成员的可访问名称。源码中组根元素带rolegroup与aria-labelButtonGroup.tsx。FR12 — 连接组与带间距组保持区分。ButtonGroup 连接式呈现去除成员间空隙、共享外边缘、拥有一份 elevation并使用文档化的 roving-focus 键盘模型当前 ToggleButtonGroup 保持带间距的独立子表面子项可各自拥有 elevationwrapper 无阴影。未来若实现连接式开关组必须把 elevation 移到组上、成员变平。ToggleButton 的 elevation 是一个已获批准的实施缺口加上既有的可选Elevation轴即可恢复家族对等性且不改变无 prop 时的渲染其当前透明 ghost 表面被接受但不声称透明问题已解决——阴影可能提供浮动边界任何后续填充或描边处理都需要单独的视觉评审。六、允许的组件变化AV1–AV6契约在不变式之外还允许五个成员在六条边界内自由变化AV1 — 瞬时 vs 持久动作Button/IconButton 不保留按压态ToggleButton 拥有isPressed、onPressedChange、pressedChangeAction与按压态呈现。AV2 — 可见 vs 纯图标内容Button 可渲染可见标签、自定义可见内容、前置图标与 end 内容IconButton 总是渲染一个必填图标、无可见标签ToggleButton 两者皆可并可在按压时替换图标pressedIcon见 ToggleButton.tsx。AV3 — 视觉强调Button/IconButton 暴露 Button 变体映射primary、secondary、ghost、destructive见 Button.tsxToggleButton 拥有自己的选中/未选中处理而不是发明一套瞬时动作层级。AV4 — 导航Button/IconButton 在提供href时可渲染为链接且禁用时回退为button因为禁用链接是公认的可访问性反模式见 Button.tsxToggleButton 保持带aria-pressed的动作。AV5 — 组键盘模型ButtonGroup 可为连接式动作簇使用 roving focusToggleButtonGroup 保留其按压按钮集的键盘与选择行为。仅视觉相似不要求两组合用同一复合控件模型。AV6 — Tooltip 所有权组件可要求显式 tooltip 或为纯图标用途提供文档化的自动 tooltip可访问标签无论哪种方式都必须存在。Button 通过useTooltiphook 附加 tooltip不额外插入 DOM 节点Button.tsx。七、代表性矩阵成员 × 共享不变式 × 刻意变化成员与状态共享不变式刻意变化Button / 普通动作具名、定尺寸、可聚焦的动作表面fire-once pending 行为可见内容、变体、表单类型与可选链接模式IconButton / 紧凑动作或 FABButton 行为、正方形几何、家族 elevation必填图标标签是程序化而非可见ToggleButton / 按压与未按压Button 几何、命名、焦点、pending 与表面所有权受控aria-pressed、按压图标/处理、可中断 ActionButtonGroup / 水平或垂直具名组继承尺寸/禁用态连接边缘、一份共享 elevation、roving focusToggleButtonGroup / 单选或多选具名组继承尺寸/禁用态选择所有者带间距子表面wrapper 无 elevation未来连接式开关组具名选择组与按压语义一份绘制的组表面拥有 elevation成员绘制 none7.1 ButtonGroup 的 roving focus 与纯 CSS 连接几何ButtonGroup 的实现有两个值得注意的源码细节roving focus整个组是单个 Tab 停靠点方向键沿 orientation 在成员间移动、Home/End 跳到两端、焦点环绕、跳过禁用成员这是 APG 的 roving tabindex 技术通过useListFocus实现ButtonGroup.tsx。成员选择器是button, a[href], [tabindex]——注释解释了为何不能基于[tabindex0]取值roving 会重写每个成员的 tabindex取值选择器会在盖章为 -1 的瞬间丢失成员。纯 CSS 连接子按钮通过 context 消费位置感知样式无需 cloneElement 或 wrapper div。IS_LAST_ITEM选择器专门处理尾端圆角Button.tsx——不能用:last-child因为带 tooltip 的 Button 会渲染不可见的 layer 元素被useLayer内联渲染后抢占了:last-child槽位历史 issue #2508。7.2 ToggleButtonGroup 的类型安全选择 APIToggleButtonGroup 使用判别联合discriminated union在类型层面强制正确的选择 APItypesingle时value: string | null且onChange: (v: string | null) voidtypemultiple时value: string[]且onChange: (v: string[]) void见 ToggleButtonGroup.tsx。组的样式是display: inline-flex加gap: --spacing-1ToggleButtonGroup.tsx印证了 FR12 的带间距子表面、wrapper 无阴影。八、采纳现状与例外Adoption and exceptions组件当前采纳缺口或例外Button拥有共享表面、尺寸、状态、Action、链接与 elevation 机制链接模式额外遵循family:navigation-destinationsIconButton继承 Button props始终选择纯图标呈现tooltip 仍属显式消费者指导ToggleButton继承 Button 几何、焦点、pending 呈现与渲染 elevation 状态已批准采纳缺口添加既有可选elevationprop 与匹配的主题元数据ButtonGroup连接表面、继承尺寸/禁用态、共享 elevation、roving focus连接时成员 elevation 被有意抑制ToggleButtonGroup受控单选/多选、继承尺寸/禁用态、带间距布局wrapper 不是绘制表面因此不暴露家族 elevation九、验证地图Verification map契约把不变式映射到可执行验证这保证了写进契约的规则都有测试兜底契约验证方式代表成员与状态变异/失败预期FR1–FR3role/name、键盘、回调、禁用与禁用原因测试文本 Button、IconButton、ToggleButton、链接模式、成员/组禁用成员失去名称、键盘路径或在禁用时被激活FR4–FR6Action 顺序、pending、乐观更新、去重与可中断性测试Button fire-once ActionToggleButton 快速按压/取消按压 Actions尺寸或用途变化、Action 绕过回调取消、陈旧状态胜出FR7单元测试加真实浏览器几何检查所有尺寸文本/纯图标按压/未按压loading家族高度发散、纯图标不再方形、状态改变外尺寸FR8–FR10data 属性、主题元数据与计算阴影测试独立 Button/IconButton/ToggleButton连接与带间距组每个 elevation 层级阴影落在错误盒上、状态改变深度、公开/主题/渲染值不一致FR11–FR12组语义、传播、DOM、键盘与渲染表面测试连接 ButtonGroup带间距 ToggleButtonGroup水平/垂直禁用成员组缺名称、默认传播失败、带间距/连接所有权混淆十、决策链接与内容边界spec:AST-002/DEC-1见 docs/specs/AST-002/spec.md——公开 API 准入是显式且基于证据的family:navigation-destinations见 docs/families/navigation-destinations.md——Button 链接模式保留共享的导航安全契约。契约在最后明确了内容边界本文件只拥有跨组件的 Button 族行为组件专属的 prop 表、回调载荷类型、选择算法、变体、tooltip 策略、组布局、实现机制、当前审计结果与产品专属动作层级分别归属各自的组件、架构、设计、审计或调用点所有者。这也意味着想了解某个组件的全部可用 prop应进一步阅读对应组件的*.doc.mjs文档如 Button.doc.mjs、ButtonGroup.doc.mjs以及在 apps/storybook/stories 目录下查阅对应的 Storybook 故事Button.stories.tsx、IconButton.stories.tsx、ToggleButton.stories.tsx、ButtonGroup.stories.tsx获取可运行示例。结语把契约当作可验证的单一事实源Astryx 的 Button family contract 并不只是一份设计文档它是一份可测试、可审计、带版本状态current与审批记录的知识记录。它以十二项跨组件不变式约束五个成员组件用一张验证地图把它们绑到测试文件同时用允许的变化清单保留每个成员应有的语义自由度。对于组件库维护者这份契约回答了最棘手的边界问题——elevation 归谁、异步动作如何去重、连接组与间距组何时必须分离对于应用开发者理解这份契约意味着你能准确预测 Button、IconButton、ToggleButton 与两个 Group 在任意组合下的行为而不必逐行阅读渲染逻辑。【免费下载链接】astryxAn open source design system thats fully customizable and agent ready项目地址: https://gitcode.com/GitHub_Trending/as/astryx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表