画像で枠を作る方法

テーマを作成していると、画像で枠を作りたいと思うことが多々あります。このような時に使用するテクニックを紹介します。

目標

sidebar.png

HTMLのソース(詳細はこちら)。両サイドバー表示の左側です。

<div class="side-a">
	<div class="side-a-top"></div><div class="side-a2">
		サイドバーの中身
	</div><div class="side-a-bottom"></div>
</div>

部品の分解

まずサイドバーの枠画像を3つの部品に分解します。

side-top.gifside-middle.pngside-bottom.png
side-top.gif
side-middle.png
side-bottom.png
155×28155×1155×23

最初の画像だけgifになっているのは、左上部を透過させるためです。透過pngはIE6がまともに扱えません。各モジュールのタイトル部画像は次を使います。

side-title.png
side-title.png
137×40

CSSの記述

横幅は 155px ですから、まずそれに合わせて全体の外枠を決めます。

div.side-a {
	width:		155px;
}

このブロックの上下に画像を貼り付けます。HTMLとよく対比してみてください。

div.side-a-top {
	background-image:	url("side-top.gif");
	height:			28px;
	width:			155px;
}
div.side-a-bottom {
	background-image:	url("side-bottom.png");
	height:			23px;
	width:			155px;
}

中間部分はリピートさせ、また内部の要素が画像をはみ出さないように適切な padding を指定します。

div.side-a2 {
	background-image:	url("side-middle.png");
	background-repeat:	repeat-y;
	padding:		1px 9px 1px 9px;  /* 155-9-9 = 137px(内側) */
}

上下の「パディング 1px」を取らないとマージンの相殺が発生しうまく表示されなくなります。

モジュールの記述

モジュールは次のように記述しました。

div.hatena-module {  /* モジュール間の隙間 */
	margin-bottom:	1em;
}
div.hatena-moduletitle {
	background-image:	url("side-title.png");
	background-repeat:	no-repeat;  /* 文字がはみ出すとIEで領域が大きくなるため */
	color:			#444;
	min-height:		29px; /* = 40-11 */
	_height:		40px; /* IEが min-height 非対応のため */
	padding:		11px 0px 0px 0px;
	font-size:		115%;
	font-weight:		bold;
	text-align:		center;
}
/* sidebar項目本体 */
div.hatena-modulebody {
	color:			#644;
	padding:		0 0.4em 0.2em 0.4em;
	margin-top:		0;
}

テーマを作っているとIEの計り知れないバグの多さ(理解に苦しむレンダリングの挙動)に辟易するかも知れませんが、_ (アンダーバー付き)のプロパティで地道に修正するしかありません。

小技集

IEでのみ解釈される属性値

どのブラウザでもバクや非対応のプロパティは付き物ですが、IEの非対応プロパティの多さとバグの多さは他の追従を許さず抜きんでています。IEにはアンダースコアハックと言い、IEでのみ有効になるプロパティの指定方法があります。

span.test {
	color: blue;
	_color: red;
}

としたとき、span.test は IEでは赤く、IE以外では青く表示されます。IEではすべてプロパティにおいて「_(アンダースコア)」を付けることでIEにだけ読み込ませることができます。この方法を利用する際は、アンダースコア付きの属性を後に書くことに注意してください。

人によって、本来は記述する必要はないがIEのためだけに記述しなければならないプロパティを書く際に _ を付けたりします。

divなどの配置を確認する

テーマを開発していると、要素の配置状況かよく分からなくなることが多々あります。また厳密な境界位置が知りたいことも少なくありません。こんなときは

div.sidebar { border: 1px solid red; }

などとすることで、枠線を表示させてどこがブロック(や要素)の境界なのか簡単に知ることができます。むしろCSSをいじって配置を行うときは、border表示をさせたままの方が便利です。

完全に配置が終わり、いざborderを外すとでたらめな位置にレンダリングされることがありますが、これはマージンの相殺によるものです(またborderで使用していた1px分だけ位置がずれるます)。そのときは「border: 1px」→「padding: 1px」に書き換えると良いでしょう。

カスケーディング

CSSには複数のプロパティ指定が重複した場合、どちらが優先するかを決める厳密なルールがあります。カスケーディングというのですが、これについて簡単に説明します。*1

*1 : 必ずしも正確ではない用語を使っていますが、分かりやすさを優先させているためです。ご了承ください。

カスケーディングの例

CSS
.color { color:red; }
span.color { color: blue; }
HTML
<span class="color">色付き文字</span>

としたとき、クラスcolor には2種類の色が指定されています。どちらの色が優先されて出力されるでしょうか?*2

色付き文字

赤という色指定と青という色指定が2つあって結果的には青が表示されました。これがカスケーディングです。<span class="color">色付き文字</span>という要素に対する指定は2種類ありましたが、要素名が付いているspan.colorの方が優先度が高いのです。

ではCSSを変更するとどうなるでしょう。

.color { color:red; font-weight: bold; }
span.color { color: blue; }

色付き文字

優先度の高いspan.colorで太字を指定してないにも関わらず太字になりました。これは、カスケーディングによる優先度評価が起こるのは競合する(同じ効果の)プロパティを指定したときのみ*3だからです。

*2 : HTMLを構成する都合上、本文のソースでは直接色などを指定しています

*3 : 「効果の」付くのは、例えば「margin」と「margin-left」は異なるプロパティですが重複したものを指定しているため(指定の競合がおこるため)化スケーディングが起こります。

優先度のルール

  1. IDによる指定
  2. クラスによる指定
  3. 親子関係による指定
  4. 同時クラス指定
  5. 順序

IDの指定

IDによる指定はすべてに優先します。

CSS
#color { color:red; } /* IDによる指定 */
span { color: blue; }
span.green { color:green; }
HTML
<span class="green" id="color">色付き文字</span>

色付き文字

クラスによる指定

CSS
span { color: blue; }
span.green { color:green; }
HTML
<span class="green">色付き文字</span>

色付き文字

親子関係による指定

CSS
div.red span { color: red; }
span { color: blue; }
HTML
<div class="red"><span class="green">色付き文字</span></div>

色付き文字

親子関係を指定した方が優先されます。親子関係の階層数は深ければ深いほど優先されます。すでに設定されている設定値を上書きする際に、よくクラス修飾を深くして記述するなどの方法が取られます。

クラス同時指定

CSS
span.red  { color: red; }
span.blue { color: blue; }
span.red.blue { color: magenta; } /* クラス同時指定 */
HTML
<span class="red blue">色付き文字</span>

色付き文字

順序

CSS
span.color { color: red; }
span.color { color: blue; }
span.color2 { color: green; }
HTML
<span class="color">色付き文字</span>
<span class="color color2">色付き文字</span>
<span class="color2 color">色付き文字</span>

色付き文字

色付き文字

色付き文字

同レベルの優先度ならば(CSS中で)後から指定された方が優先されます。

absolute位置指定

デザイン上、ある要素を「この場所に表示したい」と決めうちすることも少なくありません。このときに必要になるのが absolute による位置指定です。

absoluteの使い方

先に例を示してみましょう。

<div style="position: relative; height: 60px;">
<div style="margin-left: 200px;">margin-left: 200px;</div>
<div style="position: absolute; top: 0; width: 180px; border: 1px solid red;">
position: absolute;<br>top: 0;<br>width: 180px;</div>
</div>

として表示させると*1

margin-left: 200px;
position: absolute;
top: 0;
width: 180px;

となります。これはサイドバーの表示に使っている手法です。

親要素中の、position: relative;となっているものの(なければ表示領域中の)左上座標基準として、そこからの位置を position: abosluteとした上で top や left プロパティ使って指定します。

aboslute は非常に強力であり、各要素を HTMLの出現順序に関わらず自由な位置に配置することができます。

*1 : 表示部のソースでは背景色などを表示するため class="box2" が追加されています。

paddingとmargin相殺

CSSを理解する上で欠かせない要素がマージンとパディングです。

padding と margin

各要素は、マージンとパディング、そしてwidthというプロパティを持ちます。これは要素の配置に関連するプロパティです。まずは図をみてください。

padding.png

マージン(margin)は余白に相当します。他の要素と自分のブロックの隙間(マージン)をどれくらい取るべきかという指定です。

パディングは自分のブロック内の要素を表示するとき、内側に向けてどれだけ位置を詰めるか(どの部分を表示領域とするか)を指定します。

なぜ似たような指定が2つあるのかということですが、これには意味があります。

  • border(枠線)は margin の内側で padding の外側(2つの境界)に配置される。
  • padding は指定した値だけ必ず領域が確保されるが、marginは相殺される

marginの相殺

上の図で、div(A) の margin-bottom が "20px"、div(B)の margin-top が "10px" のとき、この2つdivブロックの空間は20pxです。10+20=30pxと思った人は間違えです。margin は2つの領域間の「空間の最低値」を指定するものだからです。

margin-bottom: 20px; → 下の要素と最低でも20pxは開けなさい

margin-top: 10px; →上の要素と最低でも10pxは開けなさい

という意味になります。

では次の図とCSSのとき、x, y の長さはそれぞれ何pxになるでしょうか?

margin.png

div.C { border: 1px solid blue; margin-bottom: 15px; }
div.A { border: none;           margin-top: 10px; }
div.B { border: 1px solid blue; margin-top: 15px; }

x の長さはすぐに分かりますよね。div.C の 15px と div.A の 10px で大きい方になりますから「x=15px」です。ではyはどうなるでしょうか? 正解はy も 15px です。つまり、この場合div.Aの上辺とdiv.Bの上辺は一致してしまいます

テーマを作成しているときに陥りやすい問題なのですが、子要素div.Cのマージンは親要素にpaddingもborderもないとき親要素のマージンを含めて他の要素と相殺します。別の言い方をすれば、中身のない要素同士は中身のある要素を見つけるまでどこまででもマージンを相殺します。

adiaryのテーマCSSをみていると、よく「padding: 1px;」という表記を見つけるのですが、これは内部の子要素がさらに外側のマージンまで相殺しないようにする工夫(細工)です。代わりに枠線(border)を付けても同じ作用を起こせますが、枠線は表示されてしまうため、(枠線を出したくないところでは)paddingを使って不用意な相殺が発生しないようにしています。

ここでは相殺を上下方向についてのみ説明しましたが、左右についても全く同じことが起こります。

ブロックのwidthとIEのバグ

ブロック(要素)の横幅を固定したいときはCSSの「width」プロパティを使います。例えば、横幅800px固定のテーマなど、この指定は非常によく使われます。

div-width.png

CSSにおけるwidthは該当ブロックからborderの太さとpaddingを除いた内部の横幅として定義されています。borderとpaddingはwidthに含まれないということを理解しておいてください。

ただしIE(の後方互換モード)ではこの解釈にバグがありborderとpaddingを含んだ幅をwidthとして指定します

ですから、ある要素をIEとそれ以外のモダンブラウザで正しく表示するためには、paddingとborder、widthの単位系をすべて統一して次のように細工しなければなりません。

div.w800 {
	padding: 1em 10px 1em 20px;  /* 上, 右, 下, 左 */
	border: 1px solid blue; 
	width: 728px; /* 800 - 20 - 10 - 1*2 = 728 */
	_width: 800px; /* IEでのみ有効な属性値 */
}

またIE6においてwidth値が瞬時に決まらないと表示されたり/消えたりするバグがあり、不必要に_widthを人間が計算して予め書いておく作業が必要になることがあります。