入力フォームにフォーカスすると、入力フォームが拡大すると同時にラベルが斜めに動く。

Float Label Patternはかっこよくて、単にラベルをプレースホルダーにするよりはマシなので使いたくなる。しかしラベルとプレースホルダーは別に提供してやりたい。機能も違うものなので、その方がきっと良いはずだ。そこで別解として、ラベルが斜めに動くものを考えた。もちろんCSSのみで実装している。

Demo: Alternative Float Label Pattern

入力フォームにフォーカスすると、左にあるラベルが斜め右上に少し移動すると同時に入力フォームが左へ拡大する。これによりFloat Label Patternと同じような結果になるが、デフォルトの状態ではラベルとプレースホルダーを両立させることができる。

ラベルを入力フォームのフォーカスと連携させるには、隣接兄弟セレクターを使うくらいしか方法はなさそうなので、マークアップは入力フォーム→ラベルの順にする必要がある。またそうするためには、両者の関連付けもネストではなくfor属性を使う必要もある。

<form>
  <p>
    <input id="foo"
      type="text"
      placeholder="Input example for Foo">
    <label for="foo">Foo:</label>
  </p>
</form>

CSSでは親要素(ここではp要素)の高さと幅を制限した上で、margin-lefttopプロパティーなどをうまく使ってレイアウトしてやる。

仮に18em(iPhoneのportraitでも収まる幅)、ラベルで6emと入力フォームで12emを使うとすると以下の様なCSSになる。

p {
  width: 18em;
  height: 2em;
}

input,
label {
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  display: inline-block;
}

input {
  margin-left: 6em;
  width: 12em;
  height: 2em;
  line-height: 2;
}

label {
  position: relative;
  top: -1.5em;
  left: 0;
  height: 1em;
  line-height: 1;
}

入力フォームの位置はmargin-leftプロパティーでラベルの幅分の余白を作ってやり、widthプロパティーで12emを指定しておく。ラベルではposition: relativeを使い、位置を上にずらす。両者ともにheightline-heightプロパティーを使って適切な高さを指定しておくが、ラベルをちょっと重ねることを考えると、入力フォームは最低でも2em程度は必要になる。

あとはこれをinput:focusをトリガーにして、入力フォームとラベルの位置を動かしてやるだけだ。入力フォームはmargin-leftwidthプロパティーで、ラベルはtopleftプロパティーで動かす。

input:focus {
  margin-left: 0;
  width: 18em;
}

input:focus + label {
  top: -2.5em;
  left: 0.5em;
}

アニメーションはtransitionプロパティーを使って適当にやれば良い。元ネタのFloat Label Patternのように下線のみのフォーム要素の場合にはもうちょっと単純化できると思う。その一方で描画領域や親要素の大きさに合わせてフレキシブルに入力フォームの幅を調整するのは難しい(うまい方法が思いつかなかった)。


元々のFloat Label Patternの要件は以下の通りだと考えている。

このパターンの問題点は、ラベルとプレースホルダーを混同させずに両立させるのが難しいことだ。入力フォームに重なって表示された文字列をラベルと解釈させるには、アイコンの追加なども含め、かなり気を使って作りこむ必要がある。もしくはそのような形でのプレースホルダーの濫用による、ユーザーの慣れを期待することになる。

僕が考えたパターンは、元パターンの要件を満たした上でラベルとプレースホルダーの両立を実現している。

しかし同時に、入力フォームの入力開始位置が動くという別の新たな欠点も持ってしまう。元パターンでもラベルの移動により視線を奪うことは起こりうるので、元パターンとの比較では欠点とまではならないが、ユーザビリティ的な問題を解消しようとして別のユーザービリティ上の問題を抱えることになるので、あまり良い解とは言えないかもしれない。