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

Christian Cantrell
Christian Cantrell

はじめに

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

CSS 除外の例
CSS 除外の例

以下のスクリーンショットのドキュメントでは、CSS 除外を使用して画像内の図形の周りにテキストを折り返しています。また、CSS リージョンを使用して、テキストを列に整形し、プルコートの周りに配置しています。

CSS リージョンの使用例
CSS リージョンの使用例

CSS リージョン

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

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

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

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

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

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

CSS リージョンの概要

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

コンテンツが定義されたリージョンに転送される
コンテンツが定義されたリージョンに転送される

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

スタイル設定されていない人間の遺産プロジェクトの例
スタイル設定されていない人間のレガシー プロジェクトの例

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

地域を示す Human Legacy Project
リージョンを使用する人間の遺産プロジェクト。

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

リージョンを示す Human Legacy Project
リージョンを示す Human Legacy プロジェクト

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

CSSOM の主な用途の 1 つは、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 地域のサンプル ページをご覧ください。

CSS ページ テンプレート

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

CSS ページ テンプレートの一般的なユースケースを見てみましょう。次のコード スニペットは、CSS を使用して「article-flow」と「timeline-flow」という 2 つの名前付きフローを作成する方法を示しています。また、「combined-articles」という 3 つ目のセレクタを定義し、その中に 2 つのフローを格納します。overflow-style プロパティを「combined-articles」セレクタ内に単純に含めると、コンテンツが 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 の「シム」(ポリフィル)を使用するプロトタイプが用意されているため、今すぐ試すことができます。

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

CSS 除外

雑誌のようなレイアウトを実現するには、テキストをリージョン間で流すだけでは不十分です。高品質で視覚的に魅力的なデスクトップ パブリッシングに欠かせない要素は、不規則なグラフィックや図形の周囲や内部にテキストを配置する機能です。CSS 除外機能は、このレベルの本番環境品質をウェブにもたらします。

次のスクリーンショットは CSS 除外のプロトタイプからのもので、大きな岩の輪郭に合わせてパスの周囲に動的に流れるテキストを示しています。

CSS 除外の例
CSS 除外の例

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

不規則な形のポリゴンにテキストが流れ込む
不規則な形状のポリゴンにテキストが流れ込む

任意の形状の周囲や内部にテキストを流すには、まず必要なアルゴリズムを開発して最適化する必要があります。Adobe は現在、WebKit に直接コントリビュートされる実装に取り組んでいます。これらのアルゴリズムが最適化されると、残りの CSS 除外が構築される基盤となります。

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

CSS リージョンと CSS 除外の現在の状態

CSS リージョンと CSS 除外について初めて公に話したのは、Google I/O 2011 の Adobe デベロッパー ポッドでした。当時、私は Google 独自のカスタム プロトタイプ ブラウザでデモを披露していました。非常に好評でしたが、私が紹介した機能がまだどの主要ブラウザでも利用できないことを視聴者が知ったとき、はっきりとした失望の表情が見られました。

今年(2012 年)も Google I/O に参加しました。今回は、同僚の Vincent Hardy と Google の Alex Danilo とともにプレゼンテーションを担当しました(プレゼンテーションはこちらで視聴できます)。わずか 1 年後、CSS リージョン仕様の約 80% が WebKit に実装され、最新バージョンの Google Chrome にすでに含まれています(現在、CSS リージョンは chrome://flags で有効にする必要があります)。CSS リージョンの暫定サポートは、Android 版 Chrome にも導入されています。

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

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

2011 年 4 月に W3C に最初の提案を行った後、CSS リージョンと CSS 除外に関する進捗状況の詳細なタイムラインは次のとおりです。

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

まとめ

Adobe は、テキスト、フォント、および InDesign などのツールを使用した一般的な DTP で豊富な経験を持っています。ウェブはすでにテキストを表示するための非常に強力なプラットフォームですが、Google は Google の知識と経験を活かして、テキストの表示をさらに進化させたいと考えています。CSS リージョンと CSS 除外を使用すると、コンテンツを意味的に構造化したまま、雑誌のようなレイアウトを実現できます。最終的には、より表現力豊かなウェブを実現できます。