タグ別アーカイブ: Bootstrap

box-sizing : border-box 最近はpaddingとborderを幅・高さから引かない

凄く今更な話だけれども、数年くらいWEB制作から離れていると見落としがちな常識の転換。

旧世代CSS:要素の幅・高さはあらかじめ余白と境界を引いた値にしよう

CSSで要素に対して幅や高さを指定する際に、要素の周囲を囲むborderの太さならびに境界内部の余白paddingの値は、あらかじめ目星をつけておかないといけない。というのも、widthやheightといったプロパティで指定された値に、borderとpaddingそれぞれの幅を加えたものが要素が親要素内で占有する大きさとなるからである。

旧padding,border計算方法

旧padding,border計算方法

上図の例で言うと、要素に対してwidthとheightで指定された幅200px高さ150pxという値は、余白paddingや境界線borderの幅がいくら変わっても保証される。その代わり要素自体の大きさは指定幅に加算されていくので、たとえば親要素の幅が400pxのところにこの.classが指定されたボックスをfloatを使って2つ並べたい場合、paddingとborderの幅を取ってしまうと2つ目のボックスが下に落ちてしまう。
そこで、ボックス落ちを防ぐためにはwidthとheightの値を、あらかじめpaddingとborderにあてがう幅分引いて指定しないといけない。上の例で言うならば、widthを150(200-20*2-5*2)px、heightを100(150-20*2-5*2)pxで指定しないと、ボックス落ちである。そして後々ページの見映えを変えたくて、paddingやborderの幅を変更したくなったらもう大変。変更の都度widthとheightの値を動かさないといけない。うわー面倒臭い。

かつてこんな感じに解決してました

この面倒臭さを解決する方法。一つは、クライアントにレビューの際にIE6を使わせる。IE6の場合paddingとborderはwidth,heightから引くという超モダンな解釈をしてくれる。そこで上の例で言えばwidthを200px、heightを150pxと指定しておいて、後からpaddingとborderを変更してもボックス落ちしない。クライアントには、IE6こそが先進的ブラウザで、その他99.9%のブラウザはゴミですと言い聞かせよう。
と、まあ冗談はさておき。問題を分割して、padding部分だけでも解決する場合。要素内にもう一つの要素を放り込んで、余白部分は内側の要素のmarginで指定するようにする。inner_**とかそういった命名の要素があれば、この問題を解決しようと苦心している可能性が高い。
そしてもう一つスマートな解決法として、CSSの変数を使用するというものがあった。CSS標準のものであれば、CSS Variables。

 
:root{
--tekitou-padding-value: 20; /* 変数名は--から始まる適当な名前 */
--tekitou-border-value: 5;
}
 
.class{
width: calc(200 - var(--tekitou-padding-value) * 2 - var(--tekitou-border-value) * 2);
height: calc(150 - var(--tekitou-padding-value) * 2 - var(--tekitou-border-value) * 2);
padding: var(--tekitou-padding-value);
border: var(--tekitou-border-value);
/* var(変数)で変数を定義された値に展開 */
/* calc(数式)で数式を展開 */
}

このようにすれば、後でpaddingとborderの値を変更しても、自動的にwidthとheightの値も連動する。
ただし、このCSS Variablesやcalc関数については対応ブラウザの問題がある。ベンダープレフィックスをつけないと動かない可能性は勿論のこと、CSS Variablesの方はEdgeの登場によりお役御免となったIEでサポートされてないのが痛い。ということで、変数を使う場合は主にSassやLESSのようなCSSプリプロセッサに頼ることになるだろう(コード例は省略)。CSSプリプロセッサを使う理由の全てがこの変数と数式計算という人も、きっと多いはず。

新世代CSS:box-sizing : border-boxで余白と境界をサイズに含めない

この余白と境界の値をあらかじめ考慮しないとボックス落ちしてしまうという仕様は、レスポンシブデザインにおいてはたいへん都合が悪かった。というのも、画面幅に応じて要素の幅も伸縮するレスポンシブデザインでは、幅の指定にパーセントを使用するからである。ボックス落ちしない余白と境界の値をあらかじめ知るのは不可能なので、デザインに遊びを持たせるか、フラットデザインのようにして影響を最小限にするなどしなければならなかった。勿論、CSSプリプロセッサで多少解決出来る部分もあるけれど、それはそれ。
かつてIE6的な余白と境界の解釈を標準化という錦の御旗のもと根絶してしまってから、実はそっちの方が都合が良かったと気付いたのだ。そこでCSS3ではbox-sizingというプロパティが出来て、余白と境界をサイズに含めない計算方法も選べるようになった。

box-sizing : border-boxを指定した場合の計算方法

box-sizing : border-boxを指定した場合の計算方法

指定方法は、box-sizingプロパティの値をborder-boxと指定するだけ(無指定の値はcontent-boxで、これは今までの計算方法だ)。これにより要素のwidthとheightで指定されたサイズが保証されて、paddingとborderはその値から減算することになる。後付けで値の変更を行っても、影響は最小限だ。

box-sizing : border-boxをどこで宣言するか

ちなみにこのbox-sizingプロパティにはinheritという値もあり、これが指定されていると上位要素で宣言された値を継承する。ページ全体にわたってbox-sizingを有効にする場合、一番簡単なのは、全称セレクタを使って全要素に明示的に指定することだけれども、

 
/* 全称セレクタだけでは疑似要素に適用されないので、beforeとafterもついでに指定 */
/* 疑似要素使わないなら指定の必要なし */
 
*, *:before, *:after{
box-sizing: border-box;
}

ただこれだとcontent-box指定を前提として書かれたCSSを拝借してくる場合などに、対象となる全要素をいちいちcontent-box指定に直すのが面倒臭い。そこで、inherit指定を上手く活用した方がbetter。

 
/* 最上位要素のhtmlに対してborder-boxを指定 */
 
html{
box-sizing: border-box;
}
 
/* 全ての要素のbox-sizingがinheritとなるように指定 */
 
*, *:before, *:after{
box-sizing: inherit;
}

このようにすれば、拝借してきたCSSがcontent-box指定の場合でも、その上位要素に対してcontent-boxを指定し直すだけでよい。

box-sizing : border-box入りのリセット/ノーマライズCSS

全称セレクタで全要素に対して指定をするならば、どうせならリセット/ノーマライズCSSで同時にこれをやってくれるものの使用も選択肢として考えたい。確認した範囲で以下のリセット/ノーマライズCSSが最新版で対応している(リンク先はGitHub)。

  • sanitize.css(全要素にinherit&html要素に対してborder-box指定)
  • minireset.css(全要素にinherit&html要素に対してborder-box指定)
  • ress(全要素にinherit&html要素に対してborder-box指定)
  • Marx(全要素にinherit&:root疑似要素に対してborder-box指定)

で、上の画像にも付記してあることだけれども、みんな大好きなTwitter Bootstrapではv2.3.2までにはborder-box指定がなく、2013年8月にリリースされたv3.0.0以降からborder-box指定が入る(全要素にinherit&html要素に対してborder-box指定)。

世代間の壁の存在に留意が必要

そんなわけで、昨今WEB制作の道に入門してくる人達は完全にborder-boxの常識でコーディングを行うだろうし、それ以前の常識で書かれたコードやWEB上のサンプルコードなどはcontent-boxの場合が多い。ここで混乱が起こる可能性がありそう(特にWordPressテーマとプラグインの関係で顕著)。
一応頭でinherit指定を行っておけば世代の異なるCSSコードの付け足しの際、直上の親要素に明示的指定を打って流用することが出来るけれども、付け足しが多数になってくるとあとあと訳が分からなくなってしまう。
そこで、出来ることならば新世代の常識の方に寄せる方向で旧世代コードも手直ししておいた方が良いのではないかと思う。Bootstrap等が軒並み新世代であることや、ブラウザのベンダープリフィックスなしbox-sizing対応がほぼ完了したことも鑑みて(box-sizingの各ブラウザ実装状況)。あと、購入する参考書の発行年などにも今後注意が必要だね。


BootstrapとGoogle Maps API同時使用時の表示崩れを修正する

Twitter社の提供するCSSフレームワーク、Bootstrap。これを使うと、面倒臭いWEBのGUIデザインを、要素へのクラス指定一発で解決することが出来る。以前紹介を行ったとおりCDNでの利用も可能なので、WEBサイトを作成する前にとりあえず(jQueryと一緒に)呪文のように読み込んでおくと、ページ制作中痒いところにも咄嗟に手が届き易い。

もう一つ、企業サイトであれば”会社概要”ページのアクセスマップなどで使われるであろう、Google Maps API。こちらも頻繁に利用されるものに違いないのだが、BootstrapとGoogle Maps APIを同時に利用して地図の表示を行うと、以下のスクリーンショットのように表示崩れが発生する。

Google Maps表示不具合

Google Maps表示不具合

まず由々しきこととして、左端の縮尺スライダーが表示されない。このような状態だと、スクリーンショットを貼っておいた方がまだ良かったのではないかという疑問も湧くだろう。また、右上の地図と衛星写真の切り替えメニューで、カーソルをホバーした時のドロップダウンメニューが少し見苦しい。具体的には、チェックボックスとラベルの間に改行が入ってしまうのである。

Google Maps表示不具合の原因

これらの不具合の原因は、Bootstrap側で読み込むCSSで、img属性やlabel属性など比較的影響の大きいところでスタイル指定を行っているからである。具体的には、

img{
max-width:100%;
width:auto\9;
height:auto;
vertical-align:middle;
border:0;
-ms-interpolation-mode:bicubic;
}
 
label{
display:block;
margin-bottom:5px;
}

といったような記載のある箇所である。問題となっているのは、img属性のmax-width:100%指定と、label属性のdisplay:inline-block指定である。

特に対応せずにGoogle Mapsが表示できているケース

img属性の指定によってマップのスライダーが表示されない不具合については、Bootstrap側としても対応策をとっている(どのヴァージョンからかは、生憎把握していないが)。

#map_canvas img,.google-maps img{
max-width:none;
}

このようなCSSを追加して、Google Mapsのデフォルトで包括要素として使われるdivのid=”map_canvas”以下のimg属性に特別にスタイル指定している。そのため、素直にマップを表示する領域のidを#map_canvasにしている場合、不具合は起こらないはずだ。問題は、別のid名やクラス名をしている場合。Bootstrap側の付け焼き刃的対策では問題が解決しない。

解決策

解決策は、Bootstrapの付け焼き刃解決策と同じように、包括要素以下のimg属性・label属性にスタイルを指定してやれば良い。たとえば包括要素がid=”googlemapdiv”だった場合、スタイルシートに以下のように記述する。

#googlemapdiv img{
max-width : none;
}
 
#googlemapdiv label{ 
width : auto;
display : inline; 
}

また、マップのコントロール部分の上位要素であるclass=”gm-style”以下の各属性に指定する形でも良いだろう。

.gm-style img{
max-width : none;
}
 
.gm-style label{ 
width : auto;
display : inline; 
}

これで正常に表示されるはず。

GoogleMaps正常表示

GoogleMaps正常表示

BootstrapやGoogle Mapsなどの複数WEBサービスを組み合わせて利用する場合、どこかは絶対ぶつかるものだと最初から割り切っていた方が良いのだろう。ちなみに今回の問題はEXIFviewerに機能を追加する過程で確認したもの。そろそろ新ヴァージョンを公開できるはず。


Conceptual:OnlyCSViewに、ただ何となくBootstrapを適用

ユーザが指定したCSVファイルを表形式でAjax表示するだけのWEBサービス、OnlyCSView。作成して以来、大して弄ることも無く放置していたのだが、Twitter Bootstrapの雰囲気を掴むために、インターフェースだけ変更しようと思い、手入れをした。

そもよ、Twitter Bootstrapって何?

Twitter BootstrapというのはCSSフレームワークの一つで、かのTwitterが提供する無償フレームワーク。これを使えば、簡単にカラムレイアウトが実現できたり、レスポンシブデザインに対応できたり、Twitterのサービスで使っているようなGUI部品が要素へのクラス指定だけで実現できたり…とそこそこ楽しめる。CSSコーダなど、WEBデザイン・レイアウト方面の方々に知名度はある技術だと思うけど、生粋のWEBプログラマでは、Bootstrapの存在を知らないという場合も多い。

どういう部品が使えるのか、どういう機能が付けられるかについては、こちらのデモページが大変参考になるだろう。
ちなみに、OnlyCSViewでは「表示」ボタンと、テーブルの視認性を上げる奇数行ハイライトで明示的に使っている。それぞれ、class=”btn btn-primary”という指定と、class=”table table-striped”という指定をしているだけ。なお、文字コード選択のセレクトメニューの見た目も変化しているが、こちらはclass指定をせず勝手に変更されたもの。こういったおせっかいもあるので、それが気にならない人間がBootstrapに手を出せばよい。

BootstrapのCDNはよ

こういった仕事で使うにはちょっと…というおもちゃは、すぐに影響なく外せるよう、CDNで利用したい。Bootstrapにも勿論CDNがあるので、魔法の呪文を書いておこう。

<!--Bootstrapの本体(ヴァージョンは適宜)-->
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>

jQueryが必須になる。ちなみに、レスポンシブ対応させるには、もう一つ二つファイルを読み込まないといけない。そちらはCDNが無いので、ダウンロードしてくるしかないけど。
ちなみに、このCDNを提供しているNetDNAという会社は、本来はCDNを商売にしている会社なので、急にサービスの停止などされる可能性はある。自己責任で。

OnlyCSViewの機能増えてないけど?

本当は見た目の更新だけでなく、ファイルのドラッグ&ドロップに対応させようとして、仕組みは書いていたのだけど。ただ、一般的なファイルダイアログを使ったアップロードと、ドラッグ&ドロップによるアップロードで同じGUIを使うというところに無理があったため、どうしようかと思っている。賑やかしで置いているだけで、利用者もいないだろうし別の開発を優先する予定。