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

概要:
「普通でない」場合の`vertical-align`の動作。`white-space`プロパティ。

出席

「普通でない」vertical-align プロパティ

お待たせいたしました。ここまではほとんどCSS2.1の普通の世界の話でしたが、ここからはいよいよお楽しみの「普通でないvertical-align」の話になります。

さて、前回言及したvertical-alignプロパティのtopやbottomという値についてなのですが、ここで仕様上「サブツリー」という言葉が出てきます。

CSS 2.1 - 10.8.1 Leading and half-leading

The following values align the element relative to the line box. Since the element may have children aligned relative to it (which in turn may have descendants aligned relative to them), these values use the bounds of the aligned subtree. The aligned subtree of an inline element contains that element and the aligned subtrees of all children inline elements whose computed ‘vertical-align’ value is not 'top’ or 'bottom’. The top of the aligned subtree is the highest of the tops of the boxes in the subtree, and the bottom is analogous.

top: Align the top of the aligned subtree with the top of the line box.

bottom: Align the bottom of the aligned subtree with the bottom of the line box.

topとbottomの場合、このサブツリーがあると一気に難しくなります。これは何かというと、ある親の下にあるサブツリーの子要素がそれぞれ持つline-heightを束ねて (bound)、それによってある「高さ」が定まります。topとかbottomというのはその高さの範囲内でのtopとbottomです。

vertical-align: topを指定されたインライン要素の「子孫要素」は独自のサブツリーを形成します。同様にvertical-align: bottomを指定したインライン要素の「子孫要素」も独自のサブツリーを形成します。

そして注意しないといけないのは、配下にサブツリーを持つ要素に対してvertical-alignをtopやbottomにすると、サブツリーは親要素から除外というか切り離され、ひとかたまりの要素として振る舞うという点です。仕様で言うところのaligned subtreeとはこのことなのです。

これはvertical-alignプロパティにある値のうち、値がtopとbottomの場合のみの挙動です。

そして、このvertical-align: topvertical-align: bottomの動作が実装によって結構違っているのです。FirefoxとIEでは正常ですが、Chromeでは残念な結果になります。

1
2
3
4
5
6
7
8
9
10
11
12
  <div class="container">
    Standard
    <span class="top">
      Top
      <span class="bottom">
        Descendant1
          <span class="bottom2">
            Descendant2
          </span>
      </span>
    </span>
  </div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    .container {
      display: inline;
      border: 1px #f00 solid;
      font-size: 4em;
    }
    span.top {
      color: blue;
      font-size: 0.5em;
    }
    span.bottom {
      font-size: 0.5em;
      color: green;
    }

    span.bottom2 {
      font-size: 0.5em;
      color: pink;
    }

上のコードをFirefoxで表示すると以下のようになります。(大きな赤枠と赤い文字は注釈です)

Vertical-align: none

右の色付きの文字 (Top, Descendant1, Descendant2) が、親要素(Standardという文字)に対してサブツリーを形成しています。 ここでspan.topにvertical-align: top;を指定すると以下のようになります。わかりやすくするために指定なしと指定ありの画像をGIFアニメーションにしてみました。

Vertical-align: top, FF

topを指定すると、右にあるサブツリーの3つの色付き文字全体が上に移動します。サブツリーがひとかたまりとして扱われているのがわかると思います。

同じものをChromeで表示するとvertical-align: topを指定しても以下のように振る舞い、サブツリーがひとかたまりになっていません。つまり仕様に沿っていないのです。 Vertical-align: top, Chrome

ところで、サブツリーの文字の方を親よりも大きくしてvertical-align: topを指定すると興味深い動作が見られます(図はFirefoxの表示)。

Vertical-align: top, FF, not defined

topを指定すると左の「Standard」という文字を含む親ボックスだけが持ち上がっていますが、実際は「Top」以下のサブツリーがひとかたまりのalignedサブツリーとして振舞っているので、相対的に持ち上がって見えています。そしてvertical-alignにtopやbottomを指定したときの親要素(図左の'Standard'という文字)の高さは、以下の仕様では未定義となっています。つまり実装に依存します。

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

The inline-level boxes are aligned vertically according to their 'vertical-align’ property. In case they are aligned 'top’ or 'bottom’, they must be aligned so as to minimize the line box height. If such boxes are tall enough, there are multiple solutions and CSS 2.1 does not define the position of the line box’s baseline (i.e., the position of the strut, see below).

こんな微細な仕様を問題にしているのはもしかすると私たちぐらいしかいないかもしれません(笑)。

white-spaceプロパティ

では続いてwhite-spaceプロパティにいきましょう。

なぜか値は5つ

さきほどどこかで、テキストは折り返しができるということをお話しいたしましたが、white-spaceプロパティはこれを制御することができます。CSS2.1ではnormal/pre/nowrap/pre-wrap/pre-lineという値を指定できます。

16.6 White space: the 'white-space’ property

This property declares how white space inside the element is handled. Values have the following meanings:

normal: This value directs user agents to collapse sequences of white space, and break lines as necessary to fill line boxes.

pre: This value prevents user agents from collapsing sequences of white space. Lines are only broken at preserved newline characters.

nowrap: This value collapses white space as for 'normal’, but suppresses line breaks within text.

pre-wrap: This value prevents user agents from collapsing sequences of white space. Lines are broken at preserved newline characters, and as necessary to fill line boxes.

pre-line: This value directs user agents to collapse sequences of white space. Lines are broken at preserved newline characters, and as necessary to fill line boxes.

normalは普通の折り返しです。HTMLの中でEnterやReturnで改行してもブラウザ上では改行されないのは皆様御存知かと思いますが、それがこの仕様です。

折り返しをさせたくない場合は、pre/nowrap/pre-wrapのいずれかを指定します。

また、改行コードがある場合に改行するかどうかも指定できます。

そして見てのとおり、複数の連続したスペース文字を1つに詰める(collaplse)かどうかも指定できます。preとpre-wrapは、複数の連続スペースを1つに詰めず、それ以外は詰めます。

それにしても、折り返しの制御と、改行コードで改行するかしないかの制御と、連続スペースの表示制御がこうやって1つのプロパティに相乗りしているのはなかなか不思議です。2つの状態を持つ対象が3つあるのだから値の数は2×2×2=8つになるのかと思いきや5つしかありません(笑)。これで足りないのか足りているのかはよくわかりません。

5つの組み合わせは仕様にもある表にありますので補いつつ以下に引用します。

改行の扱い スペースやタブの連続 行が長いときの折り返し
normal スペースとして扱う 1つのスペースにまとめる 許可
pre 改行する そのまま表示 禁止
nowrap スペースとして扱う 1つのスペースにまとめる 禁止
pre-wrap 改行する そのまま表示 許可
pre-line 改行する 1つのスペースにまとめる 許可

ともあれ、この中でwhite-space: nowrapはフロントエンジニアやデザイナーにとっても結構使い道があると思います。たとえば、メニュー項目のようなinline-blockを必ず横に並べたい(リサイズしたときに折り返したくない)場合に、white-space: nowrapで折り返しを行わないようにできるのはご存じと思います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  <ul class="mylist">
    <li>item1</li>
    <li>item2</li>
    <li>item3</li>
    <li>item4</li>
    <li>item5</li>
    <li>item6</li>
    <li>item7</li>
    <li>item8</li>
    <li>item9</li>
    <li>item0</li>
    <li>item1</li>
    <li>item2</li>
    <li>item3</li>
  </ul>
1
2
3
4
5
6
7
     .mylist {
      white-space: nowrap;
    }
    li {
      display: inline-block;
      border: 1px #000 solid;
    }

white-space: preは、HTMLのいわゆるpreタグと同じ挙動になります。

以上で、CSS部第一回における基本的な部分のお話を終わりにしたいと思います。

高度な話題の片鱗

今後の部会で扱うであろう高度なレイアウトの話題について、少しだけ触れておきましょうか。

position: absolute

先ほども、Normal flowという通常のフロー以外にOut of flowというフローがあり、上下方向の中央揃えを実現するためにposition: absolute;を使うというお話をしたかと思います。そのときにcontaining block (包含ブロック) のお話をいたしました。containing blockはCSS2.1の10.1で定義されています。

10.1 Definition of “containing block”

If the element has 'position: absolute’, the containing block is established by the nearest ancestor with a 'position’ of 'absolute’, 'relative’ or 'fixed’, in the following way:

  1. In the case that the ancestor is an inline element, the containing block is the bounding box around the padding boxes of the first and the last inline boxes generated for that element. In CSS 2.1, if the inline element is split across multiple lines, the containing block is undefined.
  2. Otherwise, the containing block is formed by the padding edge of the ancestor.

つまり、position: absoluteの場合は、「positionがabsolute/relative/fixedのいずれかである直近の先祖要素」がcontaining blockになるというわけです。だから、absoluteを使うにはどこかの先祖でrelativeを指定しておかないと、そのrelativeの中での上下中央を実現できないのです。

position: fixed

ところで、今この10.1の仕様を眺めていて気付いたのですが、position: fixedではICB(initial containing block)がcontaining blockになるのかと思いきや、viewportがcontaining blockになってますね。

If the element has 'position: fixed’, the containing block is established by the viewport in the case of continuous media or the page area in the case of paged media.

昔のiOSのSafariで、position: fixedが効かないということがあったのを覚えていらっしゃる方はおいででしょうか。Androidでもありましたね。

PCのブラウザではviewportは固定で、その中で表示が動きます。ところがiOSやAndroidのブラウザではviewport自体が動いていたのです。だからposition: fixedを指定するとviewportと一緒に動いてしまい、さっぱり固定できないということが起きてしまったのです。

ということはあの動作はバグではなく仕様どおりだったということになりますね。なおその後はviewportの扱いが変わりました。

(追記: 後日10.3.7に以下の記述を見つけてしまいました。こちらではviewpointではなくICBがfixedのcontaining blockということになっています。)

But rather than actually calculating the dimensions of that hypothetical box, user agents are free to make a guess at its probable position.

For the purposes of calculating the static position, the containing block of fixed positioned elements is the initial containing block instead of the viewport, and all scrollable boxes should be assumed to be scrolled to their origin.

floatによる回り込み

次はfloatプロパティによる回り込みです。

floatでブロックを横並びにして、テキストのように回り込ませるというのは広く行われていることですが、要素を横並びにしたい場合は基本的にはdisplay: inline-blockを使用する方がfloatよりも扱いやすいのではないかと思います。もっともinline-blockも実装レベルではいろいろ怪しいところがあるのですが。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  <div class="parent">
  Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
  tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
  quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
    <div class="floating"></div>
    <div class="floating"></div>
    <div class="floating"></div>
    <div class="floating"></div>
    <div class="floating"></div>
    <div class="floating"></div>
    <div class="floating"></div>
    <div class="floating"></div>
    <div class="floating"></div>
    <div class="floating"></div>
  </div>
1
2
3
4
5
6
7
8
9
10
11
    .parent {
      border: 2px #f00 solid;
      height: auto;
      width: 50%;
    }
    .floating {
      display: inline-block;
      border: 2px #000 solid;
      width: 50px;
      height: 50px;
    }

上のCSSのように子要素にdisplay: inline-blockがあると、リサイズ時に以下のようになります(Chromeで表示)。 wrap with inline element

ここで子要素のdisplay: inline-blockfloat-leftに置き換えると以下のようになります。

1
2
3
4
5
6
    .floating {
      float: left;
      border: 2px #000 solid;
      width: 50px;
      height: 50px;
    }

wrap with float left

いずれにしろ、floatについてまじめに考えているときりがありません。

追記

floatに関しては以下の話題も出たのですが、裏が取れていないのでメモとしてのみ残しておきます。

  • floatはブロックを横に並べるのに使われたりしますが、実のところ必ずしも横に並ぶとは限りません。
  • floatによる回り込みには「自動解除」と「強制解除」というものがあります。
  • 回り込みを引き起こす要素があり、そこに回り込ませるテキストなどがあるとします。その領域が親のブロック領域をはみ出ると、回り込むテキストが強制的に下に移動する、強制解除もあります。なので、float: leftを指定したブロックをたくさん積み上げていっても回りこみがそうやって解除されることがあるということです。ブラウザの実装では必ずしもそうなっていないことがありますが、仕様上ではそうなっています。
  • floatを使用してブロックを複数横に並べた場合、反対側の端に到達するまでは問題ありません。親ブロックを小さくするなどして、端まで到達したブロックが次の行に回り込んで複数行になった場合に保証外になってしまいます。

multi-column

multi-columnというのは新聞なんかのレイアウトで使われている、いわゆる「段組」ですね。ブロックレベル要素にたとえばcolumn-count: 3とすれば3段組みになります。今のところベンダープレフィックスを付けないといけませんが。

3.2. ‘column-count’

カラムがtableと違うのは、tableのセルのように孤立しておらず、カラム同士が内部的につながっていることです。これにより、DTPソフトのような「テキスト流し込み」ができます。

たとえば、column-fill: balancedを指定して複数カラムにテキストを同量ずつ割り当てたり、column-span: allを指定して複数カラムにまたがって表示したりということもできます。

7.1. ‘column-fill’

6.1. ‘column-span’

CSS3ではこのようなモジュールも出現し始めているということです。

圏点、傍点、カーニング

駆け足となりますが紹介だけしておきます。

CSS Fonts Module Level 3の4.7 Font featuresfont-variantfont-feature-settingsがあります。

カーニングは同じくCSS Fonts Module Level 3の6.3 Kerning: the font-kerning propertyに記載があります。

圏点・傍点 (emphasis mark) については、CSS Text Decoration Module Level 3の3. Emphasis Marksに記載されています。Text DecorationはもともとFontの仕様だったのですが大きくなりすぎたので分割されました。

今後の予定

私たちが関わっているEPUBと関連するCSS仕様を以下にリストアップします。確約はできませんが、今後の部会では主にこのあたりを扱うことになるかと思います。

CSS 3

CSS 2.1

その他関連する可能性のある仕様

以下雑談

(ここから先は部会終盤の雑談より拾い上げたトピックです)

CSSの仕様は、CSS1やCSS2.1ではひとまとまりでしたが、仕様が大きくなりすぎたためCSS3ではモジュールに分けて進められています。

CSS Color Level 3はredと書いたら赤くなるなど、Level 2と大して変わらないですね。ちなみに仕様のステータスは以下のように命名および色分けされています。

W3C Status Abbbreviation

この部会の時点では、ステータスが勧告まで進んだモジュールはCSS Color Level 3Selectors Level 3NamespacesMedia Queriesぐらいしかありません。なお、勧告(Recommendation)に至ったもののみが正式な仕様であり、勧告に至っていないものはすべて基本的にドラフトの状態です。

CR(Candidate Recommendation)は、大雑把には編集者レベルでひとまず合意が取れたという段階です。ここまでこぎつけたものはどんどん実装して使って欲しいと考えていることでしょう。

なお、CSS Ruby Layout Module Level 1はURLにcss3-rubyという字がありますがLevel 1ですのでご注意ください。

CSS Writing Modes Level 3は以下のような日本語サンプルがあちこちにあっていかにも日本人が書いたという感じがしますが、実際Level 3モジュールの多くの仕様も書いている石井さんが、fantasaiさんと共同で書いてます。凄い人だと思います。

Writing Modeサンプル

そしてSelectors Level 3は勧告に至った後早くもLevel 4に突き進んでいたり、CSS Paged Media Module Level 3はLevel 3を捨ててLevel 4に進もうとしていたり、いろいろあるようです。

次回予告

次回は、10/14に開催された第二回CSS研究部部会で扱った「Writing-mode 」を掲載します。

第一回の部会が長時間にわたり、さまざまな内容に言及していたため、まとめるのに時間がかかってしまったことをお詫びいたします。

お気付きの点などありましたらコメントをどうぞ。