【CSS】CSS 界隈で大きな話題になっている Houdini って何?

ウェブのフロントエンドを仕事にしている方で業界のトレンドに敏感な方であれば Houdini を耳にしたことがあるのではないでしょうか。英語の記事を見ると Next Big Thing なんて言われるほどです。今回は、そんな世界中で話題の Houdini について概要を解説します。

Houdini の始まり

国際標準化団体 W3C では、Houdini は私が知っているだけでも 2015 年にはすでに大きな話題になっていました。ということは、さらに前から Houdini は誕生していたことになりますから、コンセプトの誕生から 7 年以上が経過していることになります(この記事は 2022 年 2 月に執筆)。

昔話になりますが、2013 年にウェブ標準化界隈で Extensible Web Manifesto が大きな話題になりました。当時は HTML5 が大流行しており、様々な機能が WHATWG や W3C などの国際標準化団体に提案されていきました。しかし国際標準化はあまりに時間がかかるうえに、ブラウザーに実装されたときには不要の産物にになるかもしれない、というジレンマを抱えていました。そこで、標準化に関わっていた著名な人たちは、低レベルの API を提供した方が健全ではないかと考え、それに基づいてウェブの設計方針を掲げたのが、この Extensible Web Manifesto でした。

おそらく、この流れの中で CSS にも低レベル API を提供するべく Houdini の誕生の機運が高まったのだと思います。

国際標準化界隈ではそんな昔から話題になっていたのに、なぜ近年になって Houdini が話題になっているかというと、Chromium にすでに実装され始めているからです。つまり、Chromium ベースの Chrome や Edge ですでに試すことが可能だからです。さらに Apple も Houdini は前向きのようで、一部の機能は WebKit に実装され、ほかの機能も開発が進んでいます。

現場のウェブ開発者にとっては、いま動かせるのかが最大の興味であるはずです。それゆえに、国際標準化界隈の人だけでなく、現場の開発者が注目し始めているわけです。

Houdini の呼び方と由来

Houdini の呼び方は「フーディーニ」です。名前の由来は、西暦 1900 年前後に米国で活躍した奇術師のハリー・フーディーニではないかと思います。W3C の CSS-TAG Houdini Task Force の Wiki でも “magic” という言葉を使っているくらいです。(違ってたらすみません。)

標準化の関係者は、魔法と呼びたくなるほどの期待感を持って仕様策定にあたっているのでしょう。

ざっくりと Houdini って何?

Houdini とは、CSS の低レベル API の総称です。もっと分かりやすく言うと、私たち自身で CSS のプロパティを発明して実装できてしまう枠組みのことを指します。

具体例を見たほうが分かりやすいでしょう。次の例は波線を引く CSS の例です。

Houdini.how – curved-line

ご存じの通り、波のカーブの大きさを自由に指定して波線を引く CSS プロパティは存在しません。しかし、Houdini の API を使うと、上図の右側に表示されているようなプロパティを使って波線を描くことができるようになります。

どうやって実現するのかというと、JavaScript を使ってプロパティを定義して、描画部分まで自分で作ります。描画部分は実際には Canvas API と同じです。そのため、JavaScript の知識や Canvas の知識は必須です。

デザインが中心で主に HTML と CSS を使っている人にとっては関係がないと思われるかもしれませんが、そんなことはありません。Houdini を使って発明された CSS の機能は JS ファイルなどで配布可能ですので、オープンソースで様々な CSS 機能が公開されるでしょう。自身で作らないかもしれませんが、使うことはあるかもしれません。

API仕様

現在のところ W3C では Houdini に関係して 8 つの API 仕様が策定されています。「Is Houdini ready yet‽」というサイトを見ると、どの API がどのブラウザーに実装されており、W3C での標準化策定状況がどうなっているのかが分かります。2021 年 5 月から情報が更新されていないようで、情報が古く、7 つの API 仕様しか掲載されていませんが、参考になるでしょう。

Is Houdini ready yet‽

Chromium は積極的に Houdini の実装を進めていることが良く分かります。また Safari も非常に前向きに進めているようです。残念ながら Firefox はさほど積極的ではないようです。

以下、実際に Chrome でも動作する API を優先して、5 つの API について具体的なコードも交えて簡単に紹介します。

CSS Painting API Level 1

要素のバックグラウンド、ボーダー、コンテンツ領域に直接的にビジュアルを描きます。background-image プロパティや border-image プロパティなど、画像の URL などを指定するプロパティに対して、独自のビジュアルを描くことができます。Canvas API と同じ形でビジュアルを描画します。

次の例は、Google が公開している記事で紹介されているサンプルです。

<!doctype html>
<style>
  textarea {
    background-image: paint(checkerboard);
  }
</style>
<textarea></textarea>
<script>
  CSS.paintWorklet.addModule('checkerboard.js');
</script>

この例では textarea 要素に対して、background-image プロパティに paint(checkboard) を指定しています。この paint() が CSS Painting API で新たに導入されたメソッドですが、引数には checkboard と指定されています。

一方、JavaScript では checkboard.js という外部の JS ファイルを読み込んでいます。この JS ファイルで、先ほど paint() の引数に指定した checkboard とは何かを定義しています。

// checkerboard.js
class CheckerboardPainter {
  paint(ctx, geom, properties) {
    // Use `ctx` as if it was a normal canvas
    const colors = ['red', 'green', 'blue'];
    const size = 32;
    for(let y = 0; y < geom.height/size; y++) {
      for(let x = 0; x < geom.width/size; x++) {
        const color = colors[(x + y) % colors.length];
        ctx.beginPath();
        ctx.fillStyle = color;
        ctx.rect(x * size, y * size, size, size);
        ctx.fill();
      }
    }
  }
}

// Register our class under a specific name
registerPaint('checkerboard', CheckerboardPainter);

御覧の通り、checkboard.js では、Canvas API を使うかのようにビジュアルを描いています。この結果は次の通りです。

このサンプルは Chrome で実際に動作します。

上記のサンプルは静的なビジュアルを描画したに過ぎません。そのため実は画像ファイルを用意すればよくて、わざわざ CSS Painting API を使う必要はありません。この API を使う意義は、独自プロパティを用意して、それを変数として利用可能な点です。

<style>
  textarea {
    --checkerboard-spacing: 10;
    --checkerboard-size: 32;
    background-image: paint(checkerboard);
  }
</style>

これは、格子と格子の間隔を 10 px に、格子の一辺の長さを 32 px と指定しています。これが実現できると、汎用性が高い CSS プロパティとして役に立つでしょう。

CSS Properties and Values API Level 1

独自の CSS プロパティを作り上げることができます。実在の CSS プロパティと同様に、構文、継承、初期値を定義します。以前より CSS カスタムプロパティ(CSS 変数)というものがありましたが、それを拡張したものです。

CSS プロパティの定義は JavaScript で行います。プロパティ名は先頭に 2 つのハイフンが必要です。以下の例では –stop-color というプロパティを定義しています。

CSS.registerProperty({
  name: "--stop-color",
  syntax: "<color>",
  inherits: false,
  initialValue: "rgba(0,0,0,0)"
});

定義した CSS プロパティは次のようにして利用します。

.button {
  --stop-color: red;
  background: linear-gradient(var(--stop-color), black);
  transition: --stop-color 1s;
}

.button:hover {
  --stop-color: green;
}

これによって、class 属性に “button” がセットされた要素の背景は赤と黒のグラデーションで表示されますが、マウスポインタを乗せると、赤が緑に 1 秒かけて変化します。

このサンプルは Chrome で実際に動作します。

CSS Typed OM Level 1

値の型を意識して CSS データにアクセスする手段を提供します。なお、Typed OM の “OM” は Object Model のことです。

これまで CSS のプロパティの値を JavaScript から取得すると文字列でした。それを、数値であるなら Number 型として取り出せるようになるなど、JavaScript では非常に扱いやすくなります。もちろん、プロパティ値をセットすることも可能です。

const el = document.querySelector('h1');

el.attributeStyleMap.set("opacity", CSS.number(0.5));
el.attributeStyleMap.set("z-index", CSS.number(100));

console.log(el.attributeStyleMap.get("opacity").value); // 0.5
console.log(el.attributeStyleMap.get("z-index").value); // 100

このサンプルでは、h1 要素の文字が半透明になり、もし他の要素と重なりがあれば、手前にレンダリングされます。このサンプルは Chrome で実際に動作します。

CSS Layout API Level 1

CSS の display プロパティを拡張して、独自のレイアウトを定義して利用することができます。まず JavaScript で独自レイアウトを定義します。

registerLayout('centering', class {
  async layout(children, edges, constraints, styleMap) {
    ...
  }
});

CSS では次のように使います。

.centering {
  display: layout(centering);
}

CSS Parser API

ブラウザーの CSS パーサーに CSS ルールまたは CSS ルールセットの文字列を渡して、その解析結果を取得します。

var background = window.cssParse.rule("background: green");
console.log(background.styleMap.get("background").value) // "green"

var styles = window.cssParse.ruleSet(".foo { background: green; margin: 5px; }");
console.log(styles.length) // 5
console.log(styles[0].styleMap.get("margin-top").value) // 5
console.log(styles[0].styleMap.get("margin-top").type) // "px"

CSS プロパティの値は基本的に文字列です。それを数値の部分と単位の部分に分離するだけでも面倒なことが多いのですが、それをブラウザーの CSS パーサーの力を借りて楽をすることができます。

Polyfill

Houdini の API のいくつかは Chromium ベースのブラウザーに実装されているとはいえ、そのほかのブラウザーに実装されていないとなると、なかなか現場では使いづらいでしょう。実は、CSS Painting API と CSS Typed OM に関しては Polyfill が存在します。

当然、ブラウザーがネイティブでサポートしているのと比べるとパフォーマンスは悪いでしょうが、多くのブラウザーで同じ結果が得られるのであれば、役に立つシーンもあるでしょう。

まとめ

Houdini で誰もが CSS プロパティを発明し実装できるという期待感を感じていただけたでしょうか。

前述の通り、Chromium ベースのブラウザーであれば、CSS Painting API、CSS Properties and Values API、CSS Typed OM はもう動作します。今のうちに CSS Painting API を使って、アッと驚くような CSS プロパティを開発してみるのも面白いかもしれませんね。

ブラウザーの実装が進めば、改めてそれぞれの API の解説記事を本ブログで書いていく予定です。

今回は以上で終わりです。最後まで読んでくださりありがとうございました。それでは次回の記事までごきげんよう。

Share