CSSだけでradio chackboxをカスタマイズする内容ってほとんどがHTML構造気持ち悪いよね。
マークアップエンジニアの です。
CSSだけでRadioボタンの作り方みたいな内容の記事をよくお見かけしますが殆どのマークアップが気持ち悪く感じるのは僕だけでしょうか?
大体が
<section> <input type="radio" name="hoge" value="テストRadio" id="radio_item" checked /> <label for="radio_item" class="radio">TestRadio</label> </section>
繰り返す様ですが
コレって気持ち悪いし使いづらいと思うのは僕だけでしょうか?
気持ち悪いと思う点
- labelタグの使い方
- チェックさせる為にid属性をわざわざ振っちゃう
- radioボタンに紐づくtextが存在しない場合にどうする気ですか?
- Tab移動意識してますか?
で、気持ち悪い気持ち悪いと言っていても問題は解決しないので
マークアップ構造が美しくRadio CheckBoxをデザイン的にする方法を考えました。
こうできたら良いなと思う点
- マークアップが美しい
- id属性とかわざわざ振らない
- textが存在しなくても空の要素とかできない
- Tab移動意識する
実際にやってみてできたやつ
このイクラみたいなRadioボタンがデザイン的であるかはさておき、
こうできたら良いなという点は全てクリアしています。
実際にやってみたソースコード
HTML
<section> <label class="radio"> <input type="radio" name="hoge" value="hoge" checked> </label> <label class="radio"> <input type="radio" name="hoge" value="hoge"> </label> </section> <section> <label class="radio_text"> <input type="radio" name="hogehoge" value="hoge" checked>TestRadio1 </label> <label class="radio_text"> <input type="radio" name="hogehoge" value="hoge">TestRadio2 </label> </section> <section> <label class="checkbox"> <input type="checkbox" name="huga" value="hoge" checked> </label> <label class="checkbox"> <input type="checkbox" name="huga" value="hoge"> </label> </section> <section> <label class="checkbox_text"> <input type="checkbox" name="hugahuga" value="hoge" checked>TestCheck1 </label> <label class="checkbox_text"> <input type="checkbox" name="hugehuge" value="hoge" checked>TestCheck1 </label> </section>
こんな感じでlabelタグでinputタグを囲ってTextデータが存在しなくても空の要素が作成されませんし、わざわざid属性を振るなんてばかばかしい事をする必要がありません。
先ほどのよくあるコードでTextデータが存在しないかつCSSを適用したい場合のマークアップはおそらく
こんな感じになるんじゃないでしょうか?
<input type="radio" id="hoge" name="hoge" > <label for="hoge"> </label>
id属性って(W3C的に)重複が認められないのでチェックボックスが羅列されてるフォームの場合、よくあるコードを使っているマークアップエンジニアさんはどのように実装しているのでしょうか?
私の環境では、連番のid属性で実装とかしていたらフロントエンドエンジニアに椅子を投げつけられてしまいますので私は怖くてできません。
「マークアップはこれで、どうやってCSSやるの?無理じゃん」
となるかもしれませんが、要件がIE9以降であるならば
さいつよなCSSプロパティbox-shadowが使用できます。
/*Radioのみ*/ label.radio { position : relative; display : inline-block; width : 15px; height : 15px; border : 1px solid #666; border-radius : 100%; overflow : hidden; cursor : pointer; } label.radio:before { content : ''; display : block; width : 11px; height : 11px; border-radius : 100%; position : absolute; top : 2px; left : 2px; z-index : 1; background-color : #D65; } label.radio input[type="radio"] { -moz-appearance: none; -webkit-appearance: none; margin : 0px; position : absolute; z-index : 2; top : -2px; left : -23px; width : 20px; height : 20px; display : block; box-shadow : 20px 0px #FFF; } label.radio input[type="radio"]:checked { box-shadow : none; } label.radio input[type="radio"]:focus { box-shadow : 20px 0px #FFF; opacity : 0.2; }
基本はこんな感じです。
これは「Text無しのRadio」です。
なんか「壊れかけのRadio」と語感がよく似ていますが
「Text無しのRadio」です。
必要ないとは思いますが解説すると
- labelでクリックできる領域とチェックされている状態(便宜上イクラと呼びます。)の描画を行います。
- あとはinputの実体(?)をposition指定で表示領域外に表示させます。
- 表示領域外のinput実体(?)に「display:block;」をかけます。
- labelのbeforeに設定されているイクラに対して、「Text無しのRadio」がチェックされていない場合は、背景白のbox-shadow上からかぶせて隠しています。
「壊れかけのRadio」「Text無しのRadio」がチェックされたらbox-shadowがnoneになります。- チェック動作に関しては
「本当の幸せ教えてよ」「Text無しのRadio」がlabelで囲われているので上に被さっているlabelをクリックしたらチェックされます。 - Tab移動に気を使い横に
「Text無しのRadio」「壊れかけのRadio」を吹っ飛ばします。(縦にすると移動時に適切な領域が表示されない為)
/*RadioとText*/ label.radio_text { cursor : pointer; position : relative; padding-left : 5px; margin-right : 20px; overflow : hidden; padding-left : 20px; display : inline-block; } label.radio_text:before { position : absolute; width : 15px; height : 15px; border : 1px solid #666; border-radius : 50%; left : 0px; top : 4px; content : ''; z-index : 3; } label.radio_text:after { content : ''; position : absolute; width : 11px; height : 11px; border-radius : 100%; left : 3px; top : 7px; background-color : #D65; z-index : 1; } label.radio_text input[type="radio"] { -moz-appearance: none; -webkit-appearance: none; position : absolute; z-index : 2; width : 20px; height : 20px; left : -23px; top : 1px; margin : 0px; box-shadow : 20px -1px #FFF; } label.radio_text input[type="radio"]:checked { box-shadow : none; } label.radio_text input[type="radio"]:focus { opacity : 0.2; box-shadow : 20px -1px #FFF; }
こちらも基本構造は殆ど同じです。
ただ違う点は、Textが入っているので
beforeでイクラの大枠を作って
afterでイラクの赤丸を作っています。
で、input部分については先ほどのradio単体の場合と相違ありません。
CheckBoxのText有無も同じ様に実装します。
/*CheckBox */ label.checkbox { cursor : pointer; width : 20px; height : 20px; border : 1px solid #B3B3B3; background : #fff; overflow : hidden; position : relative; display : inline-block; box-sizing : border-box; } label.checkbox input[type="checkbox"] { -moz-appearance: none; -webkit-appearance: none; margin : 0; padding : 0; position : absolute; left : 20px; width : 20px; height : 20px; left : -40px; box-shadow : 39px 0px #FFF; z-index : 2; } label.checkbox input[type="checkbox"]:checked { box-shadow : none; } label.checkbox input[type="checkbox"]:checked:focus { box-shadow : 39px 0px #666; opacity : 0.1; } label.checkbox input[type="checkbox"]:focus { box-shadow : 39px 0px #EEE; } label.checkbox:after { content : ''; position : absolute; top : 40%; left : 5px; display : block; margin-top : -8px; width : 8px; height : 12px; border-right : 3px solid #D65; border-bottom : 3px solid #D65; transform : rotate(45deg); -webkit-transform : rotate(45deg); -moz-transform : rotate(45deg); z-index : 1; } /*CheckBoxとText */ label.checkbox_text { cursor : pointer; position : relative; padding-left : 25px; margin-right : 20px; overflow : hidden; position : relative; padding-left : 25px; display : inline-block; box-sizing : border-box; } label.checkbox_text:before { content : ''; position : absolute; width : 20px; height : 20px; left : 0px; top : 0; border : 1px solid #B3B3B3; z-index : 3; } label.checkbox_text:after { content : ''; position : absolute; top : 40%; left : 6px; display : block; margin-top : -8px; width : 8px; height : 12px; border-right : 3px solid #D65; border-bottom : 3px solid #D65; transform : rotate(45deg); -webkit-transform : rotate(45deg); -moz-transform : rotate(45deg); z-index : 1; } label.checkbox_text input[type="checkbox"] { -moz-appearance: none; -webkit-appearance: none; position : absolute; left : -40px; width : 20px; height : 20px; display : block; box-shadow : 41px 0px #FFF; z-index : 2; margin : 0px; padding : 0px; } label.checkbox_text input[type="checkbox"]:checked { box-shadow : none; } label.checkbox_text input[type="checkbox"]:checked:focus { box-shadow : 40px 0px #666; opacity : 0.1; } label.checkbox_text input[type="checkbox"]:focus { box-shadow : 41px 0px #EEE; }
こんな感じで疑似要素でチェック後の表示を行って
box-shadowでそれを隠す感じでCSSを書く。
反省点・改善点
まとめ
無理している感は否めませんが、前述した問題点は解決しています。
これならわざわざid属性振る様な事をしなくても良いし、見比べてもどこからどこまでがform controlの領域なのかが明確でマークアップが健全な感じがします。*1
id振るパティーン
<label for="test">テスト</label> <input type="radio" value="1" id="test" />テスト
*1:「id振るパティーン」がW3C的に健全ではないとかではないです。※あくまで個人の感想で個人差があります。ただ、1ページにユニークなid属性を多用してしまう構造になりがちな点では「id振るパティーン」ではid属性の管理が難しいのではないかと思います。http://waic.jp/docs/WCAG-TECHS/H91.html