beam-glow-states について
画面にアニメーションボーダーやグロー状態を加える開発者向けの、`border-beam` React 実装ガイドです。エフェクトのプリセット、コンポーネントのプロパティ、状態管理に加え、アクセシビリティとレイアウトの確認方法を説明します。
主な機能
- 015 種類のビームエフェクト
- 02色・テーマ・強度・時間のプロパティ
- 03有効化・無効化コールバック
- 04ローディングと選択状態の指針
- 05アクセシビリティ・レイアウト・モーションの確認
活用シーン
- 01ローディングや処理中状態の表示
- 02選択中カードや現在のパネルの表示
- 03フォーカスやアクティブ状態の短い強調
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ boraoztunc/ skills --skill beam-glow-statesインストール後、Claude Code で/beam-glow-statesを入力して読み込まれたか確認します。
同梱ファイルREFERENCES.md
詳細
ビームグローの状態
ビームは状態を際立たせる装飾として使います。アニメーションがない場合でも、テキスト、形状、コントラスト、適切なセマンティック属性で状態が理解できるようにしてください。
以下の 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 の state、エフェクト、オブザーバー、アニメーションフレームを使用するため、クライアントコンポーネントからレンダリングしてください。
"use client";
import { BorderBeam } from "border-beam";
エフェクトを選ぶ
size | 動き | 最適な用途 |
|---|---|---|
sm | コンパクトな枠線が移動 | アイコンボタン、ピル、コンパクトなコントロール |
md | 枠線全体が移動 | 選択中のカード、現在のパネル、主要なアクティブ領域 |
line | 下辺が移動 | 検索、プロンプト、入力、進捗、コマンドの領域 |
pulse-inner | 内側に収まる呼吸するグロー | ローディングカード、処理中のパネル、持続する選択状態 |
pulse-outside | 外側に広がる呼吸するハロー | 広がる余地のある、目立たせたいアクティブタスクやヒーローコントロールを 1 つだけ |
状態ごとに、次のデフォルトを使います。
- ローディング中または処理中:
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。1 つのビューにつき 1 つだけにしてください。
ニュートラルなプロダクト UI には colorVariant="mono"、落ち着いた技術的な状態には ocean、緊急性を温かみのある色で示す場合は sunset、まれな非常に目立たせたい場面には colorful から始めてください。
マウントしたラッパーを 1 つ使う
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 を使ってください。
ローディング状態を設定する
ビームを追加する前に、ローディング状態が正確に伝わるようにしてください。
<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以下で終わる処理には、ビームを表示しないでください。 - 表示したら、ちらつきを防ぐため、ローディング表示を約
400ms~600ms維持してください。 - 処理中と完了後の状態でレイアウトを維持してください。
- キャンセル、再試行、エラーのコントロールを操作可能なままにしてください。エフェクトのレイヤーには、すでに
pointer-events: noneが設定されています。
処理が 1 つの入力欄またはプロンプトバーに属する場合は 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>
- タブ、リストボックスのオプション、グリッドセルなどの選択ウィジェットには
aria-selectedを使います。 - 現在のページ、ステップ、日付、場所には
aria-currentを使います。 aria-pressedはトグルボタンにのみ使います。- 静的な選択背景またはアウトラインを維持してください。ビームは注意を引くためのものであり、それ自体に意味を持たせないでください。
- 持続して表示する選択状態のビームは、動きを遅くしてください。速いデフォルトの移動は、ローディング中または短時間のアクティブ状態に限ります。
項目数が多い場合は、新しく選択された項目だけを 800ms~1200ms アニメーションさせ、その後は静的な選択スタイルを維持してください。選択されたすべての項目でビームを無期限に動かし続けないでください。
フォーカスとアクティブ状態の制御
すべての標準 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 の場合、アニメーションを停止します。回転プリセットとラインプリセットも、利用側で無効にしてください。プロジェクトのメディアクエリーフック、または小さなクライアントフックを使用します。
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 | 必須 | 生成される 1 つの 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、ラインは 2.4、パルスは 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 のフェードアウト完了時に呼び出されます |
mono は staticColors が false でも、常に静的な色を使用します。転送された 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);
}
控えめにする
- ビューポートごとに、目立つアニメーションビームは1つにします。同時に複数のビームを使うと、階層が曖昧になり、描画処理が増えます。
- 明るさや彩度を変える前に、
strengthを調整します。 - 選択状態を示すビームは、読み込み中のビームより動きを遅く、控えめにします。
- ビーム全体を、別のアニメーション付きグラデーションボーダー、大きく脈動するシャドウ、動く背景と組み合わせないでください。
pulse-outsideは実際の余白に広がるようにします。意図せず切り取らないでください。- pulse のインスタンスは、フレームレートが制限されたアニメーションループを共有し、画面外では一時停止することを前提にしてください。rotate と line は CSS アニメーションを使用し、コンポーネントが画面外にある場合も一時停止します。
確認
次の項目をテストします。
- 初期の非アクティブ状態、フェードイン、アクティブ状態の維持、フェードアウト、状態の急速な切り替え。
- 読み込みの成功、エラー、再試行、キャンセルが、突然アンマウントされずに行われること。
aria-busy、aria-selected、aria-current、aria-pressedが適切なセマンティクスで使われていること。- ビームを無効にした状態、および動きを減らす設定を有効にした状態でのキーボードフォーカス。
- 実際の表示面でのダーク、ライト、
autoテーマ。 - 幅が 320、768、1440 の場合の
sm、md、line、pulse-inner、pulse-outside。 - ボーダー半径の整合性、親要素のオーバーフロー、外側のパルスを覆う不透明な子要素、コンパクトなラッパーのサイズ。
- 長いラベル、200% のズーム、多数のリスト項目、画面外での一時停止、ルートのクリーンアップ、コンソールエラー。
リンクのみのソース一覧として REFERENCES.md を維持してください。