关于 beam-glow-states
beam-glow-states 是使用 border-beam React 组件呈现加载、选中和活动状态的技能,涵盖效果选择、语义化静态提示、无障碍及渲染检查。
主要功能
- 01选择五种光束效果样式
- 02配置配色、主题、强度和时长
- 03确保无动画时状态仍清晰
- 04处理激活和淡出过渡
- 05检查无障碍与响应式布局
适用场景
- 01在卡片或输入框显示加载状态
- 02突出选中的面板或活动控件
- 03检查减少动态效果时的动画状态
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mengto/ skills --skill beam-glow-states安装后在 Claude Code 中输入/beam-glow-states确认已加载。
附带文件agents/openai.yamlREFERENCES.md
详细介绍
Beam 光效状态
将 beam 用作装饰性的状态强调。即使没有动画,也要通过文本、形状、对比度以及正确的语义属性,让状态清晰易懂。
以下 API 已针对 border-beam 1.3.0 验证。安装的版本发生变化时,请重新核对官方 README 和导出的类型。
安装和导入
使用仓库的包管理器安装相同的包:
npm install border-beam
pnpm add border-beam
yarn add border-beam
bun add border-beam
此包要求 React 和 React DOM 18 或更高版本。它提供 ESM、CommonJS 和 TypeScript 声明。它会注入组件作用域样式,因此不要单独导入 CSS 文件。
优先使用命名导入:
import { BorderBeam } from "border-beam";
import type {
BorderBeamProps,
BorderBeamSize,
BorderBeamTheme,
BorderBeamColorVariant,
} from "border-beam";
也支持默认组件导出:
import BorderBeam from "border-beam";
在 Next.js App Router 项目中,请从客户端组件渲染它,因为它使用 React 状态、效果、观察器和动画帧:
"use client";
import { BorderBeam } from "border-beam";
选择效果
size | 动效 | 最佳用途 |
|---|---|---|
sm | 紧凑的流动边框 | 图标按钮、胶囊按钮、紧凑控件 |
md | 完整的流动边框 | 选中卡片、当前面板、主要活动界面 |
line | 流动的底边 | 搜索、提示词、输入、进度或命令界面 |
pulse-inner | 局限于内部的呼吸光效 | 加载卡片、处理中面板、持续选中状态 |
pulse-outside | 向外扩散的呼吸光晕 | 有足够扩散空间的单个突出活动任务或主视觉控件 |
按状态使用以下默认值:
- 加载或处理中:
pulse-inner,strength={0.55}到0.75,duration={2.3}到3。 - 选中或当前状态:
md或pulse-inner,strength={0.3}到0.55,duration={3.2}到5。 - 焦点或短暂的活动反馈:
sm或line,strength={0.35}到0.6。 - 高优先级实时任务:
pulse-outside,strength={0.45}到0.7;每个视图中最多只显示一个。
中性产品 UI 使用 colorVariant="mono",冷色调技术状态使用 ocean",紧迫感较强的状态使用 sunset,罕见的高显著性时刻使用 colorful。
从一个已挂载的包装组件开始
让 BorderBeam 保持挂载,并通过切换 active 来控制状态。状态变为 false 时移除它,会跳过内置的淡出效果。
<BorderBeam
size="pulse-inner"
colorVariant="ocean"
theme="dark"
strength={0.65}
active={isWorking}
>
<section className="task-card" aria-busy={isWorking}>
<p>{isWorking ? "Generating layout options…" : "Layout options ready"}</p>
</section>
</BorderBeam>
active 会在 0.6s 内淡入,并在 0.5s 内淡出。只有在工作需要与视觉过渡完成时刻同步时,才使用 onActivate 和 onDeactivate。
接入加载状态
添加 beam 前,先确保加载状态如实反映实际情况:
<BorderBeam
size="pulse-inner"
colorVariant="ocean"
strength={0.65}
active={pending && !reduceMotion}
onDeactivate={() => {
// Optional: advance only after the 0.5s beam exit completes.
}}
>
<section className="beam-surface" aria-busy={pending}>
<span className="status-dot" aria-hidden="true" />
<span>{pending ? "Building preview…" : "Preview ready"}</span>
</section>
</BorderBeam>
- 显示可见的状态标签或进度值;绝不要只用移动边缘提示加载。
- 对于大约在
150ms或更短时间内完成的操作,避免显示 beam。 - 显示后,让加载界面至少保持约
400ms到600ms,以避免闪烁。 - 在待处理和完成状态之间保持布局不变。
- 确保取消、重试和错误控件仍可使用。效果图层已设置
pointer-events: none。
如果工作与单个输入框或提示词栏相关,请使用 line。如果整张卡片都处于忙碌状态,请使用 pulse-inner。
接入选中或当前状态
同时使用组件状态和语义状态:
<BorderBeam
size="md"
colorVariant="mono"
staticColors
duration={4.2}
strength={0.42}
active={selected && !reduceMotion}
className="beam-card"
>
<button
type="button"
className="beam-surface"
aria-pressed={selected}
onClick={onSelect}
>
{label}
</button>
</BorderBeam>
- 对标签页、listbox 选项、网格单元格及类似的选择控件,使用
aria-selected。 - 对当前页面、步骤、日期或位置,使用
aria-current。 - 仅对切换按钮使用
aria-pressed。 - 保留静态的选中背景或轮廓。beam 应用于增强提示,而不是独自承载含义。
- 放慢持续显示的选中 beam。较快的默认流动速度应留给加载或短暂激活状态。
对于大型集合,仅让新选中的项目播放动画 800ms 到 1200ms,然后保留静态选中样式。不要让每个选中项目都无限期地显示 beam。
焦点与激活状态
由于所有标准的 HTMLDivElement 属性和捕获处理器都会被转发,因此请在包装元素上跟踪焦点:
const [focused, setFocused] = useState(false);
<BorderBeam
size="sm"
colorVariant="mono"
strength={0.45}
active={focused && !reduceMotion}
className="beam-inline"
onFocusCapture={() => setFocused(true)}
onBlurCapture={(event) => {
const next = event.relatedTarget as Node | null;
if (!next || !event.currentTarget.contains(next)) setFocused(false);
}}
>
<button className="beam-surface">Run</button>
</BorderBeam>
保留常规的 :focus-visible 轮廓。仅指针悬停不应触发高强度光束;按下状态仍应立即提供缩放、填充或对比度反馈。
状态重叠时,请明确解决冲突:
const beamState =
pending ? "loading" :
selected ? "selected" :
focused ? "focus" :
"idle";
const beamProps = {
loading: {
size: "pulse-inner",
colorVariant: "ocean",
duration: 2.6,
strength: 0.65,
},
selected: {
size: "md",
colorVariant: "mono",
duration: 4.2,
strength: 0.42,
staticColors: true,
},
focus: {
size: "sm",
colorVariant: "mono",
duration: 2.4,
strength: 0.45,
staticColors: true,
},
} as const;
const activeProps = beamState === "idle" ? null : beamProps[beamState];
<BorderBeam
{...(activeProps ?? { size: "md" as const })}
active={activeProps !== null && !reduceMotion}
>
<div className="beam-surface" data-state={beamState}>
{children}
</div>
</BorderBeam>
除非产品的状态模型另有规定,否则优先级依次为:加载高于选中,选中高于焦点,焦点高于悬停。
处理减少动态效果设置
在 prefers-reduced-motion: reduce 下,脉冲预设会停止动画。旋转和线条预设也应由使用方禁用。请使用项目的媒体查询 Hook 或简易客户端 Hook:
function useReducedMotion() {
const [reduced, setReduced] = useState(false);
useEffect(() => {
const media = window.matchMedia("(prefers-reduced-motion: reduce)");
const update = () => setReduced(media.matches);
update();
media.addEventListener("change", update);
return () => media.removeEventListener("change", update);
}, []);
return reduced;
}
在子表面提供静态回退样式:
.beam-surface {
border: 1px solid rgb(255 255 255 / 0.12);
}
[data-state="selected"] {
border-color: rgb(140 155 255 / 0.7);
box-shadow: 0 0 0 3px rgb(100 120 255 / 0.12);
}
:focus-visible {
outline: 2px solid currentColor;
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
[data-state="loading"] {
border-color: rgb(100 150 255 / 0.68);
}
}
API 参考
| 属性 | 类型 | 默认值 | 约定 |
|---|---|---|---|
children | ReactNode | 必填 | 内容由一个生成的 div 包裹 |
size | "sm" | "md" | "line" | "pulse-outside" | "pulse-inner" | "md" | 效果系列和几何预设 |
colorVariant | "colorful" | "mono" | "ocean" | "sunset" | "colorful" | 光束调色板 |
theme | "dark" | "light" | "auto" | "dark" | 根据背景调整不透明度和颜色处理 |
strength | number | 1 | 光束图层的不透明度;限制在 0–1;不会改变子元素的不透明度 |
duration | number | 1.96 旋转,3.1 线条,2.3 脉冲 | 动画周期,单位为秒 |
active | boolean | true | 启动淡入或淡出,并控制持续动画 |
borderRadius | number | 自动检测 | 包装元素的圆角半径,单位为像素 |
brightness | number | 因预设而异,通常为 1.3 | 发光亮度倍率 |
saturation | number | 因主题而异,深色主题通常为 1.2 | 发光饱和度倍率 |
hueRange | number | 30 | 色相偏移范围,单位为度;line 的上限为 13 |
staticColors | boolean | false | 禁用色相偏移,但不禁用移动或脉冲动画 |
className | string | — | 生成的包装元素上的类名 |
style | CSSProperties | — | 生成的包装元素上的内联样式 |
onActivate | () => void | — | 0.6s 淡入完成时触发 |
onDeactivate | () => void | — | 0.5s 淡出完成时触发 |
即使 staticColors 为 false,mono 也始终使用静态颜色。转发的 ref 指向包装元素。其他标准 HTMLDivElement 属性和事件也会被转发。
该包还导出 sizePresets、sizeThemePresets 和已弃用的 themeColors。请将它们视为实现参考;优先使用组件属性,而不要修改导出的预设对象。
注意包装元素
BorderBeam会渲染一个div。请将其放在li、td或label等必需的语义化父元素内;不要用它替代这些元素。- 包装元素默认采用块级布局。紧凑控件请使用
display: inline-block或inline-flex,卡片请使用width: 100%。 - 请让第一个子元素与包装元素的边界对齐。如果子元素较小而包装元素被拉伸,光束就会围绕空白区域显示。
- 边框圆角会从第一个子元素的计算后左上角圆角读取。如果各角圆角不同、圆角在运行时发生变化,或后续加载的样式导致检测不可靠,请显式设置
borderRadius。 sm、md、line和pulse-inner会裁剪溢出内容。如果菜单或工具提示必须显示在包装元素之外,请勿将其放入这些包装元素内。pulse-outside使用overflow: visible。其子元素必须是不透明的,以免内部光晕透出;周围布局也必须允许光晕溢出。- 请为
pulse-outside的子元素添加独立的细微1px边框或内嵌环形边框。该预设不会绘制单独的静止细线,这是有意为之。
.beam-inline {
display: inline-block;
}
.beam-card {
display: block;
width: 100%;
}
.beam-surface {
width: 100%;
border-radius: inherit;
background: rgb(18 18 20);
}
保持克制
- 每个视口只使用一条主导的动画光束。多条光束同时运行会削弱层级感,并增加绘制开销。
- 调整亮度或饱和度之前,优先调整
strength。 - 选择光束应比加载光束更慢、更柔和。
- 不要同时使用完整光束、另一条动画渐变边框、大面积脉动阴影和移动背景。
- 让
pulse-outside延伸到真正的空白区域;切勿意外裁切。 - 预期脉冲实例会共用一个帧率受限的动画循环,并在移出屏幕时暂停。旋转和线条使用 CSS 动画,同样会在组件移出屏幕时暂停。
验证
测试:
- 初始未激活、淡入、持续激活、淡出和快速状态反转。
- 加载成功、出错、重试和取消,期间不应突然卸载。
aria-busy、aria-selected、aria-current或aria-pressed语义是否正确。- 禁用光束以及启用减少动态效果时的键盘焦点。
- 在实际表面上测试深色、浅色和
auto主题。 - 在 320、768 和 1440 宽度下测试
sm、md、line、pulse-inner和pulse-outside。 - 边框圆角对齐、父级溢出、脉冲外侧的不透明子元素以及紧凑包装器的尺寸。
- 长标签、200% 缩放、大量列表项、移出屏幕后暂停、路由清理和控制台错误。
将 REFERENCES.md 保留为仅含链接的来源列表。