ウェブ向けの雑誌風レイアウト(CSS の地域と除外設定を指定)

Christian Cantrell
Christian Cantrell

はじめに

ウェブはテキストにとって非常に強力なプラットフォームであり、Adobe はこの分野で豊富な経験と専門知識を有しています。Adobe がウェブを前進させる方法を模索していたとき、ウェブのテキスト機能をさらに進化させることは、当然の出発点のように思えました。 ウェブでは通常、テキストは 1 列の縦方向であると想定されています。グラフィックの周囲にテキストを配置したり、CSS を使用してテキストを複数の列に書式設定したりすることは可能ですが、ウェブ上で真の雑誌のようなレイアウトを実現することは依然として非常に困難です。Adobe は、CSS Regions と CSS Exclusions を使用して、デスクトップ パブリッシングの機能を最新のブラウザに導入する取り組みを主導しています。たとえば、下のスクリーンショットでは、CSS Exclusions を使用して、山の輪郭に沿ってテキストを流しています。

CSS 除外の実際の例
CSS 除外の実際の例

下のスクリーンショットのドキュメントでは、CSS Exclusions を使用して画像内の図形の周囲にテキストを回り込ませ、CSS Regions を使用してテキストを列に整形し、引用文の周囲に配置しています。

CSS リージョンの実際の例
CSS Regions の動作例

CSS のリージョン

CSS リージョンの詳細に入る前に、Google Chrome でリージョンを有効にする方法について説明します。CSS Regions を有効にしたら、この記事で紹介しているサンプルを試したり、独自のサンプルを作成したりできます。

Google Chrome で CSS リージョンを有効にする

Chrome のバージョン 20(正確にはバージョン 20.0.1132.57)以降、CSS Regions は chrome://flags インターフェースを介して有効になっています。CSS リージョンを有効にする手順は次のとおりです。

  1. Chrome で新しいタブまたはウィンドウを開きます。
  2. ロケーション バーに「chrome://flags」と入力します。
  3. ページ内検索(Ctrl / Command + F)を使用して、[試験運用版のウェブ プラットフォーム機能] セクションを検索します。
  4. [有効にする] リンクをクリックします。
  5. 下部にある [Relaunch Now] ボタンをクリックします。

Chrome のフラグについて詳しくは、Chrome フラグのすべてに関するブログ投稿をご覧ください。

ブラウザを再起動したら、CSS リージョンのテストを開始できます。

CSS リージョンの概要

CSS Regions を使用すると、意味的にマークアップされたテキストのブロックを「ボックス」(現在は要素)に自動的に流し込むことができます。次の図は、テキスト(フロー)とボックス(テキストが流し込まれる領域)の分離を示しています。

コンテンツが定義済みのリージョンに流れる
コンテンツが定義されたリージョンに流れる

CSS リージョンの実際のユースケースを見てみましょう。私は Adobe のデベロッパーであるだけでなく、SF 作家でもあります。私は、クリエイティブ・コモンズ ライセンスに基づいて自分の作品をオンラインで公開することがよくあります。できるだけ多くのデバイスやブラウザで作品を閲覧できるように、次のような非常にシンプルな形式をよく使用しています。

スタイルのない Human Legacy プロジェクトの例
スタイルなしの Human Legacy プロジェクトの例

CSS リージョンを使用することで、視覚的に面白く、操作しやすく、読みやすいため、より機能的なエクスペリエンスを作成できました。

リージョンを示す Human Legacy Project
Human Legacy Project with Regions.

デモ用に、このプロトタイプに CSS リージョンを表示する機能を追加しました。下のスクリーンショットは、中央のグラフィックと引用文を囲む列のように見えるように、リージョンが配置されている様子を示しています。

リージョンを示す Human Legacy Project
リージョンを示す Human Legacy Project

このプロトタイプはこちらで試すことができます(ソースコードも確認できます)。矢印キーを使用して移動し、Esc キーを押してリージョンを表示します。以前のプロトタイプはこちらから入手できます。

名前付きフローの作成

テキスト ブロックをリージョンに流し込むために必要な CSS は非常にシンプルです。以下のスニペットでは、「article」という名前付きフローを ID「content」の div に割り当て、同じ「article」という名前付きフローをクラス「region」の要素に割り当てています。その結果、「content」要素内のテキストは、「region」クラスを持つ要素に自動的に流し込まれます。

<!DOCTYPE html>
<html>
<head>
    <style>
    #content {
        { % mixin flow-into: article; % }
    }

    .region {
        { % mixin flow-from: article; % }
        box-sizing: border-box;
        position: absolute;
        width: 200px;
        height: 200px;
        padding: 10px;
    }

    #box-a {
        border: 1px solid red;
        top: 10px;
        left: 10px;
    }

    #box-b {
        border: 1px solid green;
        top: 210px;
        left: 210px;
    }

    #box-c {
        border: 1px solid blue;
        top: 410px;
        left: 410px;
    }
    </style>
</head>
<body>
    <div id="box-a" class="region"></div>
    <div id="box-b" class="region"></div>
    <div id="box-c" class="region"></div>
    <div id="content">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eleifend dapibus felis, a consectetur nisl aliquam at. Aliquam quam augue, molestie a scelerisque nec, accumsan non metus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin cursus euismod nisi, a egestas sem rhoncus eget. Mauris non tortor arcu. Pellentesque in odio at leo volutpat consequat....
    </div>
</body>
</html>

結果は次のようになります。

上記のコードの結果
上記のコードの結果

「content」div 内のテキストは、その表示方法を認識していません。つまり、さまざまなリージョンを通過しても、意味的に完全にそのままの状態を維持できます。また、リージョンは単なる要素であるため、他の要素と同様に CSS を使用して配置やサイズ設定を行うことができ、レスポンシブ デザインの原則に完全に適合します。要素を名前付きフローの一部として指定すると、指定したテキストが自動的に要素に流し込まれます。

CSS オブジェクト モデル

CSS オブジェクト モデル(CSSOM)は、CSS を操作するための JavaScript API を定義します。CSS リージョンに関連する新しい API の一覧を以下に示します。

  • document.webkitGetNamedFlows(): ドキュメントで使用可能な名前付きフローのコレクションを返す関数。
  • document.webkitGetNamedFlows().namedItem("article"): 特定の名前付きフローへの参照を返す関数。この引数は、flow-into と from-from の CSS プロパティの値として指定された名前に対応します。上記のコード スニペットで指定された名前付きフローへの参照を取得するには、文字列「article」を渡します。
  • WebKitNamedFlow: 次のプロパティと関数を持つ名前付き floe のオブジェクト表現。
    • firstEmptyRegionIndex: 名前付きフローに関連付けられた最初の空のリージョンのインデックスを指す整数値。リージョンのコレクションを取得する方法については、以下の getRegions() をご覧ください。
    • name: フローの名前を含む文字列値。
    • overset: 次のブール値プロパティ。
      • false: 名前付きフローのコンテンツが関連付けられたリージョンに収まる場合
      • true: コンテンツが収まらず、すべてのコンテンツを収めるためにさらにリージョンが必要な場合。
    • getContent(): 名前付きフローに流入するノードへの参照を含むコレクションを返す関数。
    • getRegions(): 名前付きフローのコンテンツを保持するリージョンへの参照を含むコレクションを返す関数。
    • getRegionsByContentNode(node): 指定されたノードを含むリージョンへの参照を返す関数。これは、名前付きアンカーなどを含むリージョンを見つける場合に特に便利です。
  • webkitregionoversetchange イベント。このイベントは、関連付けられたコンテンツのレイアウトが何らかの理由(コンテンツの追加または削除、フォントサイズの変更、領域の形状の変更など)で変更され、かつ領域の webkitRegionOverset プロパティが変更されるたびに、WebkitNamedFlow でトリガーされます。このイベントは、大まかなレイアウト変更をリッスンする場合に役立ちます。これは、重要なことが発生し、レイアウトに注意が必要な可能性があることを示すインジケーターです。たとえば、より多くのリージョンが必要である、一部のリージョンが空である可能性がある、などです。
  • webkitregionfragmentchange イベント。この編集の時点では実装されていません。このイベントは、webkitregionoversetchange と同様に、関連するコンテンツのレイアウトが何らかの理由で変更されるたびに WebkitNamedFlow でトリガーされますが、webkitRegionOverset プロパティの変更とは無関係です。このイベントは、名前付きフローのレイアウト全体に必ずしも影響しない、きめ細かいレイアウト変更をリッスンするのに役立ちます。たとえば、コンテンツが 1 つのリージョンから別のリージョンに移動しても、全体的なコンテンツはすべてのリージョンに収まります。
  • Element.webkitRegionOverset: 要素に flow-from CSS プロパティが割り当てられると、要素はリージョンになります。これらの要素には webkitRegionOverset プロパティがあり、名前付きフローの一部である場合、フローのコンテンツがリージョンからオーバーフローしているかどうかを示します。webkitRegionOverset の値は次のとおりです。
    • 「overflow」: 領域に収まらないコンテンツがある場合
    • コンテンツが領域の末尾より前に終了する場合は「fit」
    • コンテンツがリージョンに到達していない場合は「empty」

CSSOM の主な用途の一つは、webkitregionoversetchange イベントをリッスンし、さまざまな量のテキストに対応するためにリージョンを動的に追加または削除することです。たとえば、書式設定するテキストの量が予測できない場合(ユーザーが生成したテキストなど)、ブラウザ ウィンドウのサイズが変更された場合、フォントサイズが変更された場合などには、フローの変更に対応するために領域を追加または削除する必要が生じることがあります。さらに、コンテンツをページに整理する場合は、リージョンだけでなく DOM も動的に変更するメカニズムが必要になります。

次の JavaScript コード スニペットは、CSSOM を使用して必要に応じてリージョンを動的に追加する方法を示しています。なお、このコードはデモ用であり、簡略化のため、リージョンの削除や、リージョンのサイズと位置の定義は行っていません。

var flow = document.webkitGetNamedFlows().namedItem("article")
flow.addEventListener("webkitregionoversetchange", onLayoutUpdate);

function onLayoutUpdate(event) {
    var flow = event.target;
    
    // The content does not fit
    if (flow.overset === true) {
    addRegion();
    } else {
    regionLayoutComplete();
    }
}

function addRegion() {
    var region = document.createElement("div");
    region.style = "flow-from: article";
    document.body.appendChild(region);
}

function regionLayoutComplete() {
    // Finish up your layout.
}

その他のデモについては、CSS Regions のサンプルページをご覧ください。

CSS ページ テンプレート

CSSOM を活用することは、ページングやレスポンシブ レイアウトなどを実装するうえで、おそらく最も強力かつ柔軟な方法です。しかし、Adobe はテキストやデスクトップ パブリッシング ツールを長年扱ってきた経験から、デザイナーやデベロッパーが比較的汎用的なページング機能をより簡単に実現する方法も求めていることを知っています。そのため、Google は CSS ページ テンプレートと呼ばれる、ページング動作を完全に宣言的に定義できる提案に取り組んでいます。

CSS ページ テンプレートの一般的なユースケースを見てみましょう。以下のコード スニペットは、CSS を使用して「article-flow」と「timeline-flow」という 2 つの名前付きフローを作成する方法を示しています。さらに、2 つのフローを格納する「combined-articles」という 3 つ目のセレクタを定義します。「combined-articles」セレクタ内に overflow-style プロパティを単純に含めることで、コンテンツが X 軸に沿って(水平方向に)自動的にページ分割されることを示します。

<style>
    #article {
    { % mixin flow-into: article-flow; % }
    }

    #timeline {
    { % mixin flow-into: timeline-flow; % }
    }

    #combined-articles {
    overflow-style: paged-x;
    }
</style>

フローを定義し、目的のオーバーフロー動作を指定したので、ページ テンプレート自体を作成できます。

@template {
    @slot left {
    width: 35%;
    float: left;
    { % mixin flow-from: article-flow; % }
    }

    @slot time {
    width: 25%;
    float: left;
    { % mixin flow-from: timeline-flow; % }
    }

    @slot right {
    width: 35%;
    float: left;
    { % mixin flow-from: article-flow; % }
    }
}

ページ テンプレートは、新しい「at」構文を使用して定義されます。上記のコード スニペットでは、3 つのスロットを定義しており、それぞれが列に対応しています。「article-flow」のテキストは左右の列に流れ、「timeline-flow」のテキストは中央の列に流れます。結果は次のようになります。

ページ テンプレートの例
ページ テンプレートの例

記事のテキスト(左右の列のテキスト)は英語で、中央のタイムラインはドイツ語です。また、JavaScript コードを必要とせずに、ドキュメントを水平方向にページ分割します。すべてが CSS で宣言的に行われました。

CSS ページ テンプレートはまだ提案段階ですが、JavaScript の「shim」(別名 polyfill)を使用して今すぐ試すことができるプロトタイプが用意されています。

CSS リージョンの詳細については、html.adobe.com の CSS リージョンのページをご覧ください。

CSS の除外

真の雑誌のようなレイアウトを実現するには、テキストを領域に流し込むだけでは不十分です。高品質で視覚的に興味深いデスクトップ パブリッシングの重要な要素は、不規則なグラフィックや形状の周囲または内部にテキストを流し込む機能です。CSS Exclusions は、このレベルの制作品質をウェブにもたらします。

下のスクリーンショットは、CSS Exclusions のプロトタイプから取得したもので、大きな岩の輪郭に沿ったパスの周りにテキストが動的に流れている様子を示しています。

CSS 除外の実際の例
CSS 除外の実際の例

次のスクリーンショットは、その逆の例です。不規則な形状のポリゴンの内側にテキストが流し込まれています。

不規則な形状の多角形に流し込まれたテキスト
不規則な形状の多角形に流し込まれたテキスト

任意の形状の周囲または内部にテキストを流し込むための最初のステップは、必要なアルゴリズムを開発して最適化することです。Adobe は現在、WebKit に直接貢献する実装に取り組んでいます。これらのアルゴリズムが最適化されると、CSS Exclusions の残りの部分を構築するための基盤となります。

CSS Exclusions について詳しくは、html.adobe.com の CSS Exclusions ページをご覧ください。CSS Exclusions の基盤となるテクノロジーに関する Adobe の取り組みについて詳しくは、Hans Muller 氏のブログ投稿「Horizontal Box: Polygon Intersection for CSS Exclusions」をご覧ください。

CSS Regions と CSS Exclusions の状況(2012 年)

私が CSS Regions と CSS Exclusions について初めて公の場で話したのは、Google I/O 2011 の Adobe Developer Pod でした。当時、私は独自のカスタム プロトタイプ ブラウザでデモを行っていました。非常に熱狂的な反応が得られましたが、私が紹介した機能が主要なブラウザではまだ利用できないことを知った聴衆は、明らかに失望していました。

私は今年(2012 年)も Google I/O に参加しました。今回は、同僚の Vincent Hardy と Google の Alex Danilo とともにプレゼンターとして参加しました(プレゼンテーションはこちらでご覧いただけます)。それからわずか 1 年で、CSS Regions 仕様の約 80% が WebKit に実装され、Google Chrome の最新バージョンにもすでに組み込まれています(現時点では、CSS Regions を有効にするには chrome://flags を使用する必要があります)。CSS Regions の予備的なサポートは、Chrome for Android にも導入されています。

Chrome for Android のリージョン
Chrome for Android のリージョン

さらに、CSS Regions と CSS Exclusions はどちらも Internet Explorer 10 プレビューに実装されており、現在、Mozilla の 2012 年の Firefox ロードマップに掲載されています。Safari の次のメジャー バージョンでは、CSS Regions 仕様の大部分がサポートされる予定です。その後のアップデートで、残りの部分もサポートされる予定です。

2011 年 4 月に W3C に最初の提案を行って以来、CSS Regions と CSS Exclusions の開発は以下のようなタイムラインで進められてきました。

地域と除外の進捗状況
地域と除外の進捗状況

まとめ

Adobe は、InDesign などのツールを通じて、テキスト、フォント、デスクトップ パブリッシング全般に関する豊富な経験を持っています。ウェブはすでにテキストにとって非常に強力なプラットフォームですが、Google は知識と経験を活かして、テキストの表示をさらに進化させたいと考えています。CSS Regions と CSS Exclusions を使用すると、コンテンツのセマンティックな構造を維持しながら、雑誌のようなレイアウトを実現できます。これにより、ウェブの表現力が大幅に向上します。