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

Christian Cantrell
Christian Cantrell

はじめに

ウェブはテキスト向けの非常に強力なプラットフォームであり、Adobe は豊富な経験と専門知識を有しています。Adobe がウェブを進化させる方法を模索していたとき、ウェブのテキスト機能をさらに進化させることは、私たちにとって明確な出発点だと思われました。通常、ウェブでは 1 列、垂直方向のテキストが想定されています。グラフィックの周りにテキストを流し込んだり、CSS を使用してテキストを複数の列に書式設定したりすることは可能ですが、ウェブ上で真の雑誌のようなレイアウトを実現することはまだ非常に困難です。Adobe は、CSS RegionsCSS Exclusions という機能を使用して、デスクトップ パブリッシングの機能を最新のブラウザにもたらす取り組みを先導しています。たとえば、以下のスクリーンショットでは、CSS の除外を使用して、山の輪郭に沿ってテキストをフローしています。

CSS の除外の使用例
CSS 除外の使用例

以下のスクリーンショットのドキュメントでは、CSS の除外を使用して画像内の図形をテキストで囲むことができます。また、CSS Region を使用して、テキストを列形式にしたり、プルされた引用符を囲んだりしています。

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

CSS 地域

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

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

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

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

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

ブラウザを再起動すると、CSS Regions を自由にテストできるようになります。

CSS Regions の概要

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

コンテンツが定義された領域に流れ込む
定義されたリージョンへのコンテンツ フロー

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

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

CSS Regions を使用することで、操作しやすく読みやすくなったため、視覚的に面白く、機能性も増したエクスペリエンスを作成することができました。

地域を示すヒューマン レガシー プロジェクト
地域に関するヒューマン レガシー プロジェクト。

デモ用に、このプロトタイプに CSS Region を表示する機能を追加しました。以下のスクリーンショットは、リージョンがどのように配置されているかを示しており、中央にグラフィックを囲み、プルクォートが中央にあるような列であるような印象を与えます。

地域を示す「遺産」プロジェクト
地域を示すヒューマン レガシー プロジェクト

こちらから、このプロトタイプを試すことができます(およびソースコードを確認できます)。矢印キーを使用して移動し、Esc キーを押すと地域が表示されます。以前のプロトタイプはこちらでも利用できます。

名前付きフローの作成

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

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 つの名前付きフローを作成する方法を示しています。さらに、「combined-articles」という 3 つ目のセレクタを定義し、このセレクタ内に 2 つのフローを含めます。「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」構文を使用して定義されます。上記のコード スニペットでは、1 つの列にそれぞれ対応する 3 つのスロットを定義しています。「article-flow」のテキストは左右の列に流れ込み、「timeline-flow」のテキストは中央の列に表示されます。結果は次のようになります。

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

記事のテキスト(左右の列のテキスト)は英語で、中央のタイムラインはドイツ語です。さらに、ドキュメントのページを横方向にスクロールできます。JavaScript コードは必要ありません。すべてが CSS で宣言的に行われていました。

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

CSS Regions 全般について詳しくは、html.adobe.com の「CSS Regions」ページをご覧ください。

CSS の除外

真の雑誌のようなレイアウトを実現するには、リージョン間でテキストをフローできるだけでは不十分です。高品質で視覚的に魅力あるデスクトップ パブリッシングの重要な要素は、不規則なグラフィックや形状の周囲または内部にテキストが流れるようにすることです。CSS の除外によって、ウェブでもこのレベルの制作品質が実現します。

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

CSS の除外の使用例
CSS 除外の使用例

その逆は次のスクリーンショットのようになります。不規則な形のポリゴンの内側を流れるテキストです。

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

任意の図形の周囲または内部にテキストを流せるようにするには、まず必要なアルゴリズムを開発し、最適化します。Adobe は現在、WebKit に直接提供する実装に取り組んでいます。これらのアルゴリズムが最適化されると、その基盤として CSS 除外の残りの部分が構築されます。

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

CSS 地域と CSS 除外の現状

私が初めて CSS リージョンと CSS 除外について公式にお話ししたのは、Google I/O 2011 の Adobe Developer Pod でした。当時、私は独自のカスタム プロトタイプ ブラウザでデモを見せていました。反応は非常に好意的でしたが、私が紹介した機能がどの主要ブラウザでもまだ利用できないことに見物人が気づいたとき、目に見える失望感がありました。

私は今年(2012 年)に再び Google I/O に参加しました。今回は、同僚の Vincent HardyAlex 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 の地域と CSS の除外に関する取り組みの進展について、詳細なタイムラインを以下に示します。

リージョンと除外の進行状況
リージョンと除外の進捗状況

おわりに

Adobe には、テキスト、フォント、InDesign などのツールを使ったデスクトップ パブリッシング全般に関する豊富な経験があります。ウェブはすでに非常に強力なテキスト プラットフォームとなっていますが、私たちはその知識と経験を活かしてテキスト表示をさらに推し進めたいと考えています。CSS Regions と CSS Exclusions により、コンテンツを意味的に構造化したまま、雑誌のような本物のレイアウトを実現し、最終的にはウェブをより表現豊かにすることができます。