第一回部会 (2014/9/30) - part 2

概要:
インラインレベル要素の縦方向の位置を決定する要素。`vertical-align`の常識的な動作の解説。

出席

ブロックレベル要素の位置調整方法

さて、ブロックレベル要素の位置調整方法とひとことで言っても、実際には10とおりほど場合分けがあってなかなか大変です。

10.3 Calculating widths and margins

  1. inline, non-replaced elements
  2. inline, replaced elements
  3. block-level, non-replaced elements in normal flow
  4. block-level, replaced elements in normal flow
  5. floating, non-replaced elements
  6. floating, replaced elements
  7. absolutely positioned, non-replaced elements
  8. absolutely positioned, replaced elements
  9. ‘inline-block’, non-replaced elements in normal flow
  10. 'inline-block’, replaced elements in normal flow

画像のようなreplaced element (置換要素) であってもintrinsic dimension (固有の寸法) があるとは限らないことがあります (2.1では置換要素しか固有寸法を持てませんが)。

3.1 Definitions

Replaced element

An element whose content is outside the scope of the CSS formatting model, such as an image, embedded document, or applet.(略)

Replaced elements often have intrinsic dimensions: an intrinsic width, an intrinsic height, and an intrinsic ratio.(略)

Intrinsic dimensions

The width and height as defined by the element itself, not imposed by the surroundings. CSS does not define how the intrinsic dimensions are found. In CSS 2.1 only replaced elements can come with intrinsic dimensions. For raster images without reliable resolution information, a size of 1 px unit per image source pixel must be assumed.

先ほど(前回)、margin: 0 autoでボックスを水平方向の中央に配置できるという話をしました。では次に、これと似たmargin: auto 0;というプロパティを考えてみましょう。

このmargin: auto 0は一見して「ボックスの位置を上下方向に中央に置きたい」という意図があると思われますが、普通はこの方法で縦方向の中央に置くことはできません。ご存じの方も多いと思いますが。

ところで、縦書きだとこのmargin: auto 0プロパティが効きます。日本語の縦書きであれば、行は右から左に進みますので、この「左から右」が縦書きの場合の進行方向になります。このとき、見た目における縦書きの一行の高さは、論理的には横書きにおける幅(css3-sizingで言うinline-sizeです)に相当します。ついでながら、以下の==で結ばれた用語は基本的に同じものを指しています。

  • logical width == inline size == measure
  • logical height == block size == extent

これにより、このcontaining block (そのブロックを含むブロック。包含ブロックなどと訳されます) の高さは定まり、margin: auto 0;で中央寄せができます。

なお、margin: auto 0margin: 0 0と同等、margin: auto autoと書いた場合はmargin: 0 autoと同等です。


余談

CSS 3のことを少しだけ触れておくと、条件によってはCSS 3の仕様で上書きされたり、フォールバックしたりすることがあります。フォールバックとは何かというと、2.1では値がゼロになってしまった場合にinitial containing blockの値を使用するというのがあります。initial containing blockはデフォルトでviewportのサイズが使用されるので、確実に値があります。

なおフォールバックサイズの定義はCSS3では今のところ以下のようになっています。それにしても「Some sizing algorithms do not work well with an infinite size.」には苦笑するしかないですね。

CSS Intrinsic & Extrinsic Sizing Module Level 3 Terminology

fallback size: Some sizing algorithms do not work well with an infinite size. In these cases, the fallback size is used instead. Unless otherwise specified, this is the size of the initial containing block.

indefinite size: A size that is not definite. An indefinite available size is essentially infinite.

サイズが無限または不定になりやすい場合にフォールバックサイズが登場します。典型例はwriting-mode 3の7.3.1で言及されています。

CSS Writing Modes Level 3, 7.3.1 Auto-sizing in Orthogonal Flows

7.3.1 Auto-sizing in Orthogonal Flows

It is common in CSS for a containing block to have a definite measure, but not a definite extent. This typically happens in CSS2.1 when a containing block has an auto height, for example: its width is given by the calculations in 10.3.3, but its extent depends on its contents. In such cases the available measure is defined as the measure of the containing block; but the available extent, which would otherwise be the extent of the containing block, is infinite.


いずれにしろ、CSS 3ではこのブロックレベル要素の位置調整のあたりがCSS 2.1からごっそり書き換えられています。

私たちの仕事に関係のある部分で言うと、どういう部分が縦書きでは変わらないのかというのが仕様からはわかりにくいです。

initial containing block

ここでinitial containing blockについて簡単に補足します。

ごく簡単にいえば、ブラウザのウィンドウがinitial containing blockと考えていただければよいでしょう。ウィンドウのサイズを変えればこの値も変わります。プリンタなどページに分割されるデバイスでは1つのページがinitial containing blockになります。

インラインレベル要素の位置調整

それでは、いよいよ今回の部会の目玉である「インラインレベル要素の位置調整」に入りましょう。

インライン要素は横方向に進むと先ほど申し上げました。横方向の位置については単純で、margin/padding/border/widthで決まります。

フォントのメトリックス

line-height

問題は縦方向の位置なのですが、これについて説明する前に基本的な部分をおさらいしましょう。インラインレベル要素は基本的に文字であり、行というものを持っていますので、行の高さ (line-height) というものを持っています。

インラインレベル要素の位置調整

このline-heightはデフォルトではnormalという値になっています。これはブラウザが決めていいということになっており、推奨値は「文字サイズ/フォントサイズから1.2倍まで」なのですが、実際には実装によってまちまちです。フォントに埋め込まれている値が使われることもあります。

10.8 Line height calculations: the 'line-height’ and 'vertical-align’ properties

Tells user agents to set the used value to a “reasonable” value based on the font of the element. The value has the same meaning as <number>. We recommend a used value for 'normal’ between 1.0 to 1.2. The computed value is 'normal’.

他のメトリックスについても説明しましょう。上の図をご覧ください(図はこの後も繰り返し引用します)。

ascentとdescent

inline level elements

  • フォントの高さとは、ascentdescentを足したものです。
  • ascentとは、baselineより上からフォントの最上部までの高さです。
  • descentとは、baselineから下にはみだす部分です。英語のアルファベットであれば、小文字の“g"の下の部分がここにかかる代表的な文字です。
  • baselineascentdescentによって決定される二次的な値です。

定義上は、すべてのフォントのグリフはこのascent + descentの高さに収まることになります(補足: 10.8.1ではsTypoAscender/Descenderという別の値が推奨されており、これらがない場合はAscent/Descentを使うとなっています)。

10.8.1 Leading and half-leading

Note. It is recommended that implementations that use OpenType or TrueType fonts use the metrics ”sTypoAscender“ and ”sTypoDescender“ from the font’s OS/2 table for A and D (after scaling to the current element’s font size). In the absence of these metrics, the ”Ascent“ and ”Descent“ metrics from the HHEA table should be used.

しかし実際のグリフがどのようなものであれ、インラインレベル要素ではフォントが持つascentdescentbaselineというサイズ・位置情報(メトリックス)がフォントごとにあることが前提とされます。

なおフォントサイズごとにascentdescentが変わることもあります。

inline level elements

そしてそれらに図のように行間(leading)が足されるとそれが行の高さ(line-height)になります。見てのとおり、leadingは上と下に半分ずつ足されています。line-height = ascent + descent + leadingは定義です。

なお一応フォントにもデフォルトのleadingが含まれており、ブラウザの実装によってはline-heightにnormalを指定した場合にフォントが持つleading情報が使われることもあります。フォントに「メイリオ」を指定するとやたらと行間が空くのは、デフォルトのleading値が大きいためです。

たとえばline-heightを2emにすると、1emはフォントの高さ(ascent + descent)そのものですから、上のleadingに0.5em、下のleadingに0.5emが割り当てられることになります。

baseline

そしてフォントが最終的に描画されるときには、baselineの座標を基準とします。サイズや種類の異なるフォントがインラインで並ぶ場合には、このbaselineに沿って並べられます。アルファベットのフォントデザイン自体も、baselineに沿って並べられることが前提になっています。

日本語フォントの漢字などの場合はdescentにはみ出す要素はありませんので、ascentだけがあるような状態です。ただし日本語フォントには英語フォントも含まれていたりするので、descentはゼロではないことにご注意ください。"あいうえおxyz"などと入力すれば、yの下の部分がdescentに置かれているのがわかると思います。

そして、インラインレベル要素はline-heightによって高さが決まるので、この要素に通常のheightを指定しても無効です。皆さんも一度はインラインレベル要素にheightを指定してしまったことがあるのではないかと思います。

フォントが混在する場合

インラインレベル要素の位置調整

1行の中にサイズの違うフォントがあると、行の高さはその行にあるインライン要素のうちで一番line-heightが高いものに合わせて大きくなります。

ここでご注意いただきたいのは、ブロック要素の場合とインライン要素の場合でline-heightの指定の仕方が異なるということです。

line-heightをブロック要素に指定した場合は最小のline-heightが設定されます。ブロック要素に指定する場合は、単にミニマムの高さを指定するだけなので、文字がそれより大きくなれば何も指定しなかったのと同じ状態になります。

10.8.1 Leading and half-leading

On a block container element whose content is composed of inline-level elements, 'line-height’ specifies the minimal height of line boxes within the element.

インライン要素に指定した場合は、そのインライン要素の「実際の」行の高さが設定されます。

たとえば、インライン要素では巨大なフォントに対してline-heightをたとえば0.5emなどと指定してしまえば、文字よりも高さの低い行、つまり行の上下にフォントがはみだした状態を作ることができてしまいます。

(追記: 10.8.1によればline-heightのinheritanceはyesであり、これはanonymous inline boxにも適用されます。

On a non-replaced inline element, 'line-height’ specifies the height that is used in the calculation of the line box height.

従って、以下のような例では、pはブロック要素であるにもかかわらず、最小のline-heightではなく、上で引用した"the height that is used in the calculation of the line box height."の方が適用されるので、文字が重なります。結果的には自然な動作ですが。)

1
<p style="line-height:0.5em">ほげ<br>ぴよ</p>

block element with no min-line-height

leading

inline level elements

インライン要素では、行と行はぴったりくっついています。leadingはその中で上下に半分ずつ配置されているので、上の行の下leading(半分)と下の行の上leading(半分)が合わさって行間が完成することになります。

なので、上の行と下の行でleadingが異なればそれも反映されます。上の行のleadingが1emで下の行のleadingが0.5emなら、上の下半分0.5emと下の上半分0.25emを合わせたものが行間になります。

以上が、基本的なインラインレベル要素の高さが決定されるしくみです。

text-alignプロパティ

これは簡単です。インラインレベル要素が横にたくさん連なっているときにそれらを行の中でどう配置するか (右寄せ、左寄せ、中央寄せ、均等寄せ) というものです。

vertical-alignプロパティ (non-replaced elementsに対する場合)

これは厄介です。

インラインレベル要素の位置調整

まず、vertical-alignのデフォルト値はbaselineです。他に以下の値が指定可能です。

  • baseline/middle/super/sub/text-top/text-bottom/(パーセント)/(長さ)
  • top/bottom

topとbottomだけあえて分けてあるのは、これらは上の値とはまったく異なる概念に基いているからです。プロパティは1つなのに値に複数の概念が含まれてしまっているわけですが、これらの値は互いに排他にする必要があるので、一度に1つしか指定できないようにこうしたのだと思います。

これらの値の中でよく間違えられるのがmiddleで、これを使うと上下中央揃えできそうな気がするのですが、仕様にはそんなことはまったく書いていません。ここでいうmiddleとは「baselineから、小文字のxという文字の高さまでの半分」のことです。

10.8.1 Leading and half-leading

middle

Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent.

上の仕様にあるように、正確にはmiddleを指定すると、「middleが指定されたボックスの高さの中点を、親ボックスのベースラインと親ボックスの小文字のxの高さの半分を足した高さに合わせる」ということです。

vertical-align: middle

上の図の右の「アラインしたいボックス」(親から見た子要素)の上下方向の位置は以下のように決まります。

vertical-align: middle

見てのとおり、まず親ボックスの中に例としてWyxという文字があり、そのベースラインの上に乗った小文字xの半分の高さに対して、子要素のボックスの高さの中点が合わさります。なかなかアクロバティックな仕様になっています。

この仕様からわかるように、middleという言葉とは裏腹に、アラインしたい子要素のボックスが親ボックスの上下方向の中間に置かれる保証はまったくありません。何となく真ん中に置かれるような感じで、そうなることもあるのかもしれませんが、baselineの位置と小文字のxのデザイン次第で変わってしまうものなのです。

そしてこの子要素のボックスとは、具体的には「line-height」のボックスなのです。なので、その中にまたascentやdescentやleadingがあるわけです。先ほどの上の図を拡大して再度引用します。

inline level elements

上の図を、先ほどのvertical-alignの図にはめ込むと以下のようになります。

vertical-align: middle

おわかりでしょうか。見てのとおり入れ子になっています。 このように、子要素の中でさらにleading/ascent/descentによってbaselineが定まって初めて子要素のフォントの上下位置が定まるわけです。

  1. 親ボックスで、leading/asccent/descentによってbaselineの位置(高さ)が定まる
  2. そのbaselineを基準に、小文字xの半分の高さが定まる
  3. その高さに子要素のボックス (line-heightのボックス) の高さの中点を合わせる
  4. 子要素のボックスの上下位置が定まったら、そこから再度leading/ascent/descentによって子要素のbaselineを定める
  5. 子要素のbaselineが定まったことで、子要素のフォントの位置がやっと定まる

以上のように、親要素のテキスト要素に対して子要素でvertical-align: middleを指定するというただそれだけのことなのに、これほど複雑な手順を経由しているのです。

そしてここまででおわかりのとおり、この親は基本的にインライン要素です。インライン要素に別のインライン要素をvertical-align: middleで上下を合わせようとしても思い通りにならないわけです。

replaced elementsに対するvertical-alignプロパティ

さて、ここまでに申し上げたvertical-align: middleの仕様は、アラインしたい子要素のボックスがnon-replaced elements (非置換要素)、ひらたくいえば文字の場合です。それではこれがreplaced elementsの場合はどうなるでしょうか。

replaced elementsの典型的なものは画像ですね。たとえば画像に対してvertical-align: middleを指定すると、その画像のmarginボックスの高さの中点がターゲットとして使用されます。

10.8.1 Leading and half-leading

In the following definitions, for inline non-replaced elements, the box used for alignment is the box whose height is the 'line-height’ (containing the box’s glyphs and the half-leading on each side, see above). For all other elements, the box used for alignment is the margin box.

上で説明したとおり、基準となる「小文字xの半分の高さ」はline-heightの中間でも何でもないので、画像のmarginボックスについても、そのボックスの高さの中点をそこに合わせたところで画像の上下位置はテキストの中間にアラインされないわけです。

中央揃えにするには?

しかし現実には画像の高さをテキストに対して上下中央揃えにしたいことはいくらでもあります。いったいどうすればよいでしょうか?

ひとつの方法はinline-blockを使うことでしょう。inline-blockのボックスをposition: relativeのボックスで囲み、それをさらにposition: absoluteのボックスで囲って、margin: autoで上下位置の中間に置くという方法が考えられます(10.6.4 Absolutely positioned, non-replaced elements)。


余談

上でCSS2.1のvertical-alignmiddleはありますが(text-alignにあるような)centerという値はありません。しかしながらCSS3 module: line 4.8. Vertical alignmentなどにはcentralという値が追加されています。

central Align the 'central’ baseline of the inline element with the central baseline of the parent.

CSS3 baseline

ここでいうbaselineは、CSS2.1と同じではなく拡張されたものです。4.1. Baseline information provided by fontsにあるideographic baselineというもので、漢字のような象形文字用のbaselineを定めています。

Western and most other alphabetic and syllabic glyphs are aligned to an "alphabetic” baseline, the above Indic glyphs are aligned to a “hanging” baseline and the East Asian glyphs are aligned to an “ideographic” baseline.


super/subプロパティ

本題に戻りましょう。vertical-alignのsuperやsubは、CSS2.1の仕様を見ると意外にも位置の定義に具体的な値の算出法がなく、適当に上げるとか適当に下げるというアバウトなものになっています(笑)。

10.8.1 Leading and half-leading

sub

Lower the baseline of the box to the proper position for subscripts of the parent’s box. (This value has no effect on the font size of the element’s text.)

super

Raise the baseline of the box to the proper position for superscripts of the parent’s box. (This value has no effect on the font size of the element’s text.)

今見てみると、CSS3の4.8. Vertical alignmentでもこの点は変わっていません。ただ、super/subにした場合にフォントのサイズを変更してはならないという点は重要です。

従って、vertical-alignのsuper/subの挙動は今のところ実装に依存しています。実装に依存せずに細かく制御したいのであれば、パーセンテージなどを使用する必要があるでしょう。

text-top/text-bottomプロパティ

vertical-alignの次の値はtext-topとtext-bottomです。2.1の仕様では、ボックスのtopを親のcontent boxのtopに合わせると指示されています。

10.8.1 Leading and half-leading

text-top

Align the top of the box with the top of the parent’s content area (see 10.6.1).

text-bottom

Align the bottom of the box with the bottom of the parent’s content area (see 10.6.1).

ここでいうコンテンツエリアにはleadingは含まれません。従って、以下の図で言う右のボックスのようにアラインされることになります。bottomはちょうど逆です。

vertical-align:top

従って、vertical-alignにtopを指定すると、その名前の印象に反して、逆にleadingの分だけ位置が下がってしまうということがしばしば発生します。bottomも同様です。

top/bottomの位置はcontent boxが基準になるので、子要素を親ボックスの一番上にアラインしたいのであれば、たとえばline-height: 1emと指定することでleadingをゼロにするという方法があります。言うまでもなく、1emはフォントサイズに等しいので、leadingは打ち消されてゼロになります。

次回予告

ここまでが「普通の」vertical-alignです。ここからは「普通でない」vertical-alignの世界をお見せします。長くなりましたので次回といたします。


Q&A

Q: ピクセル幅が2で割り切れないときはどうなりますか?

A: 近似が行われます。なお近似の結果となる値は6.1.4 Actual valuesと呼ばれていますが、近似の方法については言及されていません。

6.1.4 Actual values

A used value is in principle the value used for rendering, but a user agent may not be able to make use of the value in a given environment. For example, a user agent may only be able to render borders with integer pixel widths and may therefore have to approximate the computed width, or the user agent may be forced to use only black and white shades instead of full color. The actual value is the used value after any approximations have been applied.

Q: margin: auto 0は、外側のブロックの高さを定めても効かないのでしょうか?

A: 基本的には効きませんが、10.6.410.6.5を使えば効きます。