HTMLフォーム作成はWeb開発において避けては通れないものですが、その作業が楽しいことはまれなことです。幸いなことに、CSSのFlexboxを使えばこれまで困難だったことでも解決できることがあります。
Flexboxを使用せずに、フォームの一般的なコーディング方法を考えてみます。次のようなフィールドをHTMLでどのようにマークアップしますか?
たいてい次のようにマークアップしますね。
<div>
<label for="name">name</label>
<input id="name" name="name" type="text" />
</div>
<div>
<label for="experience">experience</label>
<select id="experience" name="experience"><!-- options --></select>
</div>
<div>
<input id="html" name="html" type="checkbox" />
<label for="html">HTML</label>
</div>
<div>
<input id="css" name="css" type="checkbox" />
<label for="css">CSS</label>
</div>
<div>
<input id="javascript" name="javascript" type="checkbox" />
<label for="javascript">JavaScript</label>
</div>
CSSスタイリングを適用しようとすると、たくさんの課題が出てきます。
- ラベルとフィールドに関するHTMLソースの記述順は統一されておらず、typeによって異なる。一般にラベルはフィールドの前に来るが、チェックボックスやラジオボタンではあとのほうがよい。タイプを変更する場合、マークアップを並べ替えなければならない
- input、textarea、selectボックスでalignとsizeを統一するのは困難な場合がある。たいていはデフォルトのスタイルがあるが、厄介なことにブラウザーによってはスタイルが変わってしまう
- ラベルをフィールドのあとに配置する場合、正しい位置にするために試行錯誤が必要である
- ラベルを最初からフィールドのアクティブ状態やコンテンツの状況に従うようにスタイリングすることは(いまのところ)不可能である
これらの課題はFlexboxで解決できます。きれいな統一のとれたHTMLで対応するフィールドのあとにラベルを配置でき、しかもCSSのヘルパークラスを追加する必要もないのです。
Flexboxとはなにか
Flexboxはページ上で複数の要素をレイアウトできる驚くべき方法です。ほかの要素との関係における縦・横揃え、配置順、並び替え、サイズを指定できます。Flexboxは複雑で、使い始めて数か月たってもときどきドキュメントを参照する必要があるかもしれません。とはいえ、次のようにもっとも基本的なCSSでFlexboxを有効にできます。
.container {
display: flex;
}
これでOKです。.containerの子要素はこれでFlexboxのアイテムになり、デフォルトで1行分のサイズになります。
FlexboxがCSS Grid Layoutモジュール(『Flexboxよりも新しい!CSSレイアウトの最新仕様Grid Layout を先取りしよう』参照)の新しいグリッドシステムと異なる点は、Flexboxが1次元的であることです。Flexboxのアイテムは1直線に並び、必要に応じて回り込みます。グリッドは2次元的にカラムで定義され、次の行に回り込むことはありません。
Flexboxはグリッドほどパワフルではありませんが、ナビゲーションメニュー、リスト、フォームフィールドなど比較的小さいコンポーネントのレイアウトには理想的です。
Flexboxのツールとリソースを以下に示します。
- flexbox in 5 minutes
- Flexbox Froggy
- Solved by Flexbox
- Flexbox Playground
- flexplorer
- MDN Using CSS Flexible Boxes(MDN:CSS flexible boxの利用)
- How z-index and Auto Margins Work in Flexbox(Flexboxにおけるz-indexとmargin:autoの動作)
- CSS Grid Layout Module Level 1 specification(CSSグリッドレイアウトモジュール仕様レベル1)
「class」を使わない理由
SMACSSやBEMなどの「CSS哲学」を取り入れてきた人は、この記事でclass定義が使われていないことにショックを受けるかもしれません。HTMLやタグをターゲットとするCSSでは、コードをきれいに保ち、ややこしさを避けて分かりやすくすることを目指しています。
このコードをサイトですべてのフォームの基本スタイルとすることを検討してみてください。きっとレイアウトを全体的に統一できるでしょう。どうしても使わなければと感じるならclassを追加できますが、小さめのサイトやデモではおそらくその必要はありません。
Flexboxをフォームに実装する
上に紹介した画像に関するコードの実行結果は次のようになります。
HTMLを見ると、たとえば、次のように、タイプに関係なくlabel要素がいつもフィールドのあとに定義されているのとが分かります。
<div>
<input id="name" name="name" type="text" />
<label for="name">name</label>
</div>
<div>
<select id="experience" name="experience"><!-- options --></select>
<label for="experience">experience</label>
</div>
<div>
<input id="html" name="html" type="checkbox" />
<label for="html">HTML</label>
</div>
各<div>をFlexboxのコンテナとしalign-items: centerに設定すれば、ラベルとフィールドが確実に縦揃えされます。
fieldset div {
display: flex;
align-items: center;
}
これでフィールドとラベルがFlexboxアイテムになりますが、ラベルが先に来る必要があるので、たいていの場合下のように並び順が違ってしまいます。
ぴったりの名前が付けられた、orderプロパティを使って正か負の整数を設定し、並び順を修正できます。数が小さいほうから「先に」ページに表示されます。
label {
order: 1;
width: 10em;
padding-right: 0.5em;
}
input, textarea, select {
order: 2;
flex: 1 1 auto;
}
ラベルにwidthとpaddingを適用して確実に配置を統一します。Flexboxのデメリットの1つは、比較的長いテキストラベルの実装時に幅の調整が必要な場合もあることです。
input、textarea、selectフィールドがflex: 1 1 autoと設定されていることに注目してください。flexは利用可能なスペースを満たすためにアイテムの寸法をどのように変更できるか指定するショートハンドプロパティです。
- flex-grow:別のアイテムとの関係でflexアイテムが拡大できるスペースの大きさを示す。例えば値が2のアイテムは、値が1のアイテムの2倍の幅をとれる
- flex-shrink:同様に別のアイテムとの関係でflexアイテムが縮小できるスペースの大きさを示す
- flex-basis:flexアイテムの幅の初期値
flex: 1 1 autoは本質的に、「スペースが残っていればすべて使う」という意味です。フィールドの要素は同じサイズに揃っているのでpaddingとborderの調整は必要ありません!
残念なことに、チェックボックスフィールドの配置が正しくありません(そう、クラス名を使っていればこうはならないでしょう!)しかしチェックボックスとラジオボタンのフィールドを直接ターゲットとして修正できます。次のコードでフィールドを当初の位置に戻し、スペースをすべて削除して上で設定したラベル幅に合うようにマージンを適用します。
input[type="checkbox"], input[type="radio"] {
order: 1;
flex: none;
width: auto;
margin-left: 10em;
}
チェックボックスはHTMLソースでフィールドのあとに来るので、兄弟セレクタ(~でも+でも良い)の使用によっても関連するラベルをターゲットできます。デフォルトの幅の使用をやめ、小さい値のpaddingを適用できます。
input[type="checkbox"] ~ label, input[type="radio"] ~ label {
width: auto;
padding-left: 0.4em;
}
色とスタイルを少し整えれば、どんなフィールドを配置しても見栄えのする素晴らしいフォームができます。
ラベルのスタイル
ラベル(の設定)を最後にする別のメリットは、フィールドの状態に応じてラベルをスタイリングできることです。たとえば次のように、フィールドのフォーカス時にラベルが赤になるよう設定します。
input:focus ~ label, textarea:focus ~ label, select:focus ~ label {
color: #933;
}
または、次のようにするとチェック済みのチェックボックスやラジオボタンでラベルが太字になります。
input:checked ~ label {
font-weight: bold;
}
CSSに少しの変更を加えればラベルのサイズ変更、移動、フェードが可能になり、いろいろなアニメーション効果も実装できます。
Flexbox対応ブラウザー
現在、主要なブラウザーはすべてFlexboxに対応しています。IE11にはFlexbox関連の課題がいくつかありますが、この例で使われているシンプルなプロパティで問題になることはありません。IE10では-ms-プレフィックスをつければ動きます。
それ以前のブラウザーではすべてのフィールドがラベルの前に表示されますが、タブの並び順は正しく保たれ、どのフィールドを使うかによって並び順がはっきりしています。
唯一不安なのはスクリーンリーダーです。リーダーが、対応するラベルテキストではなくHTMLソースの並び順に依存していると、混乱が生じるおそれがあります。私はこの具体例を知りませんが、見かけたらぜひ教えてください!
最後に、この記事を読んでフォーム全般で広くFlexboxを使ってみようという気持ちになったなら、<fieldset>要素にはdisplay: flexを適用できないことに注意してください。ChromeとFirefoxではバグが出るからです。このヒントでいくらか時間の節約になれば幸いです。
(原文:Make Forms Fun with Flexbo)
[翻訳:新岡祐佳子/編集:Livit]