本文へスキップ

フローティングラベル

フォーカスするとラベルが枠の上にスッと浮き上がる入力欄。省スペースで、いま何を入力中か直感的に分かる。

プレビュー

入力

操作実際に入力して試せます。

使いどころ

「省スペースでラベルを常に見せておきたいが、初期状態ではプレースホルダー的な軽さも欲しい」というフォームに向く。この実装は氏名・メールの2フィールドだけの短いフォームを想定しており、フィールドは24pxのgapで縦に並ぶ。入力済みかフォーカス中かで浮上するので、値が最初から入っている編集フォーム(プロフィール編集など)でも初期表示からラベルが正しい位置に来る。逆に、ラベルの文言が長い日本語(「配送先住所(建物名・部屋番号含む)」のような長文ラベル)には向かない。浮上後のフォントサイズは11pxまで縮むため、長い文字列は入力欄の幅に収まらずはみ出すか折り返して隣のフィールドを侵食する。またチェックボックスやセレクトのように「値がある/ない」の概念が薄い入力には流用できない。

仕組み

土台は position: relative の .field の中に input と label を並べ、label を absolute配置で input の中央に重ねる構成。初期状態は top: 50%; transform: translateY(-50%) で入力欄のプレースホルダー位置に見せかけ、.field:focus-within か .field.is-filled が付くと top: 0; font-size: 11px; color: #2b50c7 に切り替わる。JS側はラベルの浮上そのものは操作せず、is-filled クラスの付け外しだけを担当する。input.value.length > 0 を input イベントで監視するが、IME変換中(composing フラグ)は refresh() を止め、compositionend で改めて評価する。これにより変換中の1文字目を打った瞬間に浮上→未確定文字が消えて沈む、というちらつきを防いでいる。label の background: #fff と padding: 0 2px は、浮上時にラベルが入力欄の枠線を横切る位置に来るための仕掛けで、枠線の上にラベル文字が乗って見えるのはこの背景色で線を隠しているだけである。

実装の注意

最も起きやすい事故は input に placeholder を追加すること。ラベルが浮上する前は結局プレースホルダーと同じ位置にラベルテキストが重なって表示されているため、そこにさらにプレースホルダーを足すと2つの文字列が同じ場所に重なって読めなくなる。次に多いのが .field-label の background: #fff の固定値で、フィールドをカード背景がベージュや暗い配色のコンテナに置くと、浮上したラベルの背景だけ白く浮いて見える。コンテナ側の背景色に必ず追従させる必要がある。またJSで input.value = "既存の値" のようにプログラムから直接値を入れても input イベントは発火しないため is-filled が付かず、浮上しないままラベルと文字が重なる。サーバーから初期値を描画するときはページ読み込み時に明示的に refresh() を呼ぶか、is-filled を初期HTMLに含めておく必要がある。

アクセシビリティ

label 要素は for属性で input と正しく紐付けられているため、浮上のアニメーションを一切無視しても、スクリーンリーダーは「お名前、編集、テキスト」のようにラベルを正しく読み上げる。視覚効果とアクセシブルネームの経路が分離されているのがこの実装の良い点である。一方でフォーカス時の視覚的な手がかりは border-color の変化とラベルの移動だけで、フォーカスリング(outline)が明示的に用意されていないため、色の識別が難しいユーザーには今どこにフォーカスがあるかが伝わりにくい。:focus-visible で outline か box-shadow を足すべきである。また top / font-size / color のトランジションは prefers-reduced-motion を考慮していないため、動きに敏感なユーザー向けに @media (prefers-reduced-motion: reduce) でトランジションを無効化する分岐を追加するのが望ましい。

調整して使う

浮上のトリガーは「フォーカス中」と「値がある」の2条件のORで構成されているが、値があるときだけ浮上させ未入力時はフォーカスしても浮上させない設計に変えることもできる(この場合ラベルは常にプレースホルダー代わりになり、フォーカス中でも空なら位置を変えない)。色は現状フォーカス時のボーダーと同じ #2b50c7 をラベルにも使っているが、エラー状態を追加するなら .field.has-error 用に赤系の色分岐を足すのが自然。トランジション時間0.2秒は標準的だが、フィールド数が多いフォームで一斉に描画されるとチラつくため、初期表示時だけトランジションを無効化する初期化フラグを設けると滑らかになる。CSS単体で完結させたい場合は :placeholder-shown を使う手もあるが、その場合は placeholder=" "(半角スペース)が必須になり、IME対応も自前では効かなくなる点に注意する。

コード(コピーして使えます)

<div class="form-wrap">
  <div class="field">
    <input id="fl-name" class="field-input" type="text" />
    <label for="fl-name" class="field-label">お名前</label>
  </div>
  <div class="field">
    <input id="fl-email" class="field-input" type="email" />
    <label for="fl-email" class="field-label">メールアドレス</label>
  </div>
</div>

タグ

同じカテゴリ「フォーム入力」のほかのパーツ

関連パーツ