【ITニュース解説】A delete button you have to hold: React + Tailwind, no animation library
2026年10月31日に「Dev.to」が公開したITニュース「A delete button you have to hold: React + Tailwind, no animation library」について初心者にもわかりやすく解説しています。
ITニュース概要
ReactとTailwind CSSで、アニメーションライブラリを使わず「長押し削除」ボタンを実装する方法を紹介する。誤操作を防ぐこのボタンは、長押しで削除、途中で離すとキャンセル。初心者向けに実装の詳細やコード例を解説する。
ITニュース解説
多くのウェブサイトやアプリケーションで見かける削除ボタンは、通常「本当に削除しますか?」といった確認メッセージを表示するモーダルウィンドウを伴うことが多い。しかし、ユーザーはこのモーダルを繰り返し見ると、内容を読まずに「はい」とクリックしてしまい、意図しない削除を引き起こす可能性がある。この問題に対し、ボタンを「長押し」することで削除を実行する新しいUIパターンが提案されている。これは、視覚的なフィードバックを通じてユーザーに操作の重要性を認識させ、誤削除を防ぐことを目的とする。
この長押し削除ボタンは、ユーザーがボタンを押し続けると、内部のバーが徐々に赤く塗りつぶされていき、最後まで塗りつぶしが完了すると削除が実行される仕組みである。もし途中で指を離すと、バーは元の状態に戻っていくため、意図しない削除が起こりにくい。また、従来のモーダルウィンドウが不要になるため、UIがよりシンプルになるという利点もある。
このボタンの実装には、JavaScriptライブラリのReactと、CSSフレームワークのTailwind CSSが用いられている。特筆すべきは、Framer Motionのようなアニメーション専用のライブラリを一切使わず、Reactの状態管理と標準的なCSS、そしてブラウザの描画機能(requestAnimationFrame)のみで、全てのインタラクションとアニメーションを実現している点である。
ボタンの見た目の変化は、次のようになる。普段は小さな丸いゴミ箱アイコンの形をしているが、マウスカーソルを合わせたり、キーボードでフォーカスしたりすると、ボタンが横に伸びて「Hold to delete」というテキストが表示される。そして、実際に長押しすると、ボタンが赤く塗りつぶされていくと同時に、ゴミ箱の蓋が少し開くような視覚効果が加わる。削除が完了すると「Deleted」と表示され、しばらくすると再び元のアイコンに戻る。
この複雑な振る舞いを実現するために、ReactのuseStateとuseRefが効果的に使い分けられている。useStateは、ボタンが現在「待機中(idle)」「長押し中(holding)」「戻り中(draining)」「完了(done)」といった、ボタン全体の大きな状態(フェーズ)を管理するために使われる。一方、useRefは、長押しによる塗りつぶしの「進捗度(progress)」のように、毎フレーム頻繁に更新されるが、その更新が直接Reactの再描画(レンダリング)を必要としない値の管理に使われる。これにより、毎フレームの進捗更新でコンポーネント全体が再描画されるのを防ぎ、パフォーマンスを最適化している。
アニメーションの核となるのは、requestAnimationFrameというブラウザのAPIである。これは、ブラウザが次の画面を描画する直前に指定した関数を実行するもので、これを利用することで、画面のフレームレート(描画速度)に合わせて滑らかなアニメーションを実現できる。ボタンの進捗度合いは、このrequestAnimationFrameのループ内で計算され、JavaScriptからCSSカスタムプロパティ(--progress)としてボタン要素のスタイルに直接書き込まれる。そして、この--progressの値に応じて、ボタン内の赤い塗りつぶし要素がCSSのscale-xプロパティを使って横方向に拡大縮小する。この方法により、Reactの再描画を最小限に抑えつつ、効率的にアニメーションを動かしている。
ボタンの形状変化やテキストの表示、アイコンの動きなどは、Tailwind CSSとdata-phase属性を組み合わせて実現されている。Reactがボタンの現在のフェーズをdata-phase属性としてHTML要素に設定すると、Tailwind CSSのクラスセレクタ(例:data-[phase=holding]:w-40)がその属性値を検知し、対応するCSSスタイル(例:幅を40pxに設定)を適用する。これにより、JavaScriptの状態とCSSの見た目が常に同期し、整合性の取れたUIを提供できる。
このボタンは、ユーザーの様々な入力方法に対応している。マウスやタッチスクリーンでの操作はonPointerDown(ボタンが押された時)とonPointerUp/onPointerLeave/onPointerCancel(ボタンが離されたり、カーソルが外れたりした時)といったイベントハンドラで処理される。特にタッチ操作では、意図しないスクロールで削除がキャンセルされたり、長押しメニューが表示されたりするのを防ぐための工夫も施されている。また、キーボードユーザーのためにSpaceキーやEnterキーでの操作にも対応しており、onKeyDownとonKeyUpイベントを使って長押しと解除が実現されている。
Next.jsのようなフレームワークでこのコンポーネントを利用する際には、「クライアントコンポーネント」としての指定が必要である。これは、ユーザーとのインタラクションを伴う部分が、ブラウザ(クライアント側)で実行される必要があるため、コンポーネントファイルの先頭に"use client"というディレクティブを記述する。もし、親コンポーネントがサーバー側で動作する「サーバーコンポーネント」の場合、関数を直接子コンポーネントに渡すことができないため、リストのレンダリングや削除処理をクライアントコンポーネント内にカプセル化するなどの対応が必要になる。
アクセシビリティ(誰もが使いやすいこと)にも配慮がなされている。このコンポーネントは標準の<button>要素として実装されているため、キーボードでのフォーカスや操作が可能である。スクリーンリーダーのユーザーには、ボタンの状態に応じてaria-label属性が「Hold to delete」「Keep holding」「Deleted」と変化して、現在の状況を正確に伝える。また、キーボードでのフォーカス時には分かりやすいアウトラインが表示され、ユーザーがシステムの「動きを減らす」設定を有効にしている場合は、一部のアニメーション(幅や蓋の動き)が停止し、過度な視覚的刺激を避けるように設計されている。ただし、長押し操作は、運動機能に障がいを持つ一部のユーザーにとっては難しい場合があるため、そのようなケースでは削除後に「元に戻す」機能を一時的に提供するなど、追加の配慮が推奨される。
この長押し削除ボタンは、長押し時間や色、表示するアイコンやテキストを変更することで、「アーカイブ」や「送信」など、削除以外の確認を要する様々な操作にも応用できる柔軟性を持っている。シンプルな実装でありながら、ユーザー体験、パフォーマンス、アクセシビリティに深く配慮された、現代的なUIコンポーネントの好例と言える。