Edge の PM (Program Manager) やってる方のツイートが目に留まったので内容をざっくり読んでみた、という話です。
該当のプロポーザルが置かれているリポジトリ https://github.com/MicrosoftEdge/MSEdgeExplainers から、Edge チームが中心になって検討を進めているプロポーザルのようですが、冒頭の Authors を見る限り他にも Google , Salesforce の人が関わってます。
※以下、 https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ControlUICustomization/explainer.md の内容をかいつまんで説明したものであり、また追加で調べたことなど自由に補足を入れているため、完全な翻訳ではありません。
Introduction
http://gwhitworth.com/surveys/controls-components/ より引用
開発者がスクラッチ でコントロール を再実装すると、web プラットフォームがやってくれているパフォーマンス、信頼性(reliability)、アクセシビリティ の最適化の恩恵を受けられない
注: "they're not able to leverage the work done on the Web Platform to optimize performance, ..." なので、「プラットフォームの取り組みを活用してパフォーマンス、... を最適化することができない」と訳した方が適切?
このプロポーザルによって、開発者はプラットフォームの恩恵を受けつつ、自分たちのサイトにフィットするようネイティブコントロール をカスタマイズすることが可能になる
Goals
開発者がネイティブコントロール の任意の箇所をスタイリングできる
開発者がネイティブコントロール の好きな部分に任意のコンテンツを挿入できる
複数のパーツからなるコントロール に対し、開発者はすべてのUIをリライトすることなく特定の箇所をスタイリングできる
開発者はデータモデルやユーザー入力にリーチするためのコードを再実装することなく、UIをカスタマイズできる。これはカスタムエレメントとしてスクラッチ 実装するような今のアプローチとは対照的である
カスタマイズしたコントロール はデフォルトでアクセシブルである
Incremental Approach
このドキュメントの目的は上述したゴールを達成でき、かつすべてのコントールに適用できるようなモデルを検討することだが、実際にはモデルの適用はそれぞれのコントロール ずつ - つまり各コントロール ごとに固有のプロポーザル - になるだろう
ロールアウトは開発者の要求(demand)や各コントロール の複雑度合によるし、一部のコントロール には適用されない(実装されない)可能性もある
このドキュメントはコンセプトの例として <select>
と <input type="range">
を多用するが、これらのコントロール の詳細なスペックを決めることがゴールではない
Use Cases
主要なユースケース は「コントロール を自分のサイトに配置したいが、コントロール の見た目に高いレベルでの柔軟性を必要としているWeb開発者」
現在、そのようなシチュエーションでは、コントロール のscriptable model(?)とUIをリンクするコードや、アクセシブルにするためのARIA 属性などを含めたコードをスクラッチ で実装する必要がある
このプロポーザルにより開発者はカスタムのマークアップ とスタイルのみ提供し、残りはプラットフォームが提供する(というように役割分担がされ、開発者は見た目のカスタマイズに注力できるようになる)
Proposed Solution
Custom content and styles via standardized parts, named slots, and shadow DOM replacement
開発者がネイティブコントロール をカスタマイズする方法には3つの選択肢がある:
標準化されたパーツと状態(standardized parts and states)を使い、擬似クラスと擬似要素でネイティブコントロール のスタイルを上書きする
擬似クラス: :hover
や :first
など (MDN )
擬似要素: ::after
など (MDN )
名前つき <slots>
を使い、ネイティブコントロール の一部はそのままに、一部のパーツを開発者が作成したコンポーネント に置き換える
ネイティブコントロール のUI全体を開発者が提供する shadow root で置き換える
下にいくほど自由度が高くなるイメージ。
Standardized control UI anatomy, parts, and behavior
上のいずれの方法を取るにしても、今のコントロール の概念的なパーツやふるまい、ならびに各パーツの名称についての標準化が必要。
このプロセスは現在 https://open-ui.org/ によって進められている。
標準化の例として、たとえば <select>
については
以下のパーツで構成される
1つの "button" という名前のパーツ
1つの "selected-value " という名前のパーツ
1つの "listbox" という名前のポップアップパーツ
0〜N 個の "option" という名前のパーツ
期待するふるまいは以下の通り
"button" がクリックされるとリストを表示する
"selected-value " は内部テキストを更新し現在選択中のオプションの値を表示する
開いているリストの外をクリックすると折りたたむ
etc.
開発者はここで定義されたパーツからなるカスタムUIを提供すれば、プラットフォームがよしなにイベントハンドラ ーやARIA 属性を付与して期待するふるまいやアクセシビリティ を担保してくれる、というしくみ。
なおネイティブのスタイルは標準化される予定がなく、ブラウザによって今後も差異がある予定。
選択肢1のアプローチ(Styling native parts using pseudo-classes and the part pseudo-element)
先ほどの標準化によって決定された各パーツの名称と、 part
という擬似要素を使って、以下のように今より細かい粒度でスタイルを適用できる。
< style >
.styled-select ::part(button ) {
background-color : red
}
</ style >
< select class = "styled-select" >
< option > choice 1</ option >
< option > choice 2</ option >
</ select >
選択肢2のアプローチ(Named slots)
標準化によって決定された各パーツの名称に対応するように slot の name 属性も標準化することで、開発者はカスタマイズしたいパーツを slot name で指定して任意のコンポーネント に置き換えられるようになる。
たとえば <select>
であれば "button" や "listbox" という名前が標準化されるので、
< style >
.custom-button {
}
option {
}
.option-text {
}
</ style >
< select >
< div slot= "button" part= "button" class = "custom-button" > Choose a pet</ div >
< div slot= "listbox" part= "listbox" class = "custom-listbox" >
< option >
< img src = "./cat-icon.jpg" />
< div class = "option-text" > Cat</ div >
</ option >
< option >
< img src = "./dog-icon.jpg" />
< div class = "option-text" > Dog</ div >
</ option >
</ div >
</ select >
こういったコードを書くことで、↓のようなドロップダウンが作れる。
https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ControlUICustomization/explainer.md より引用
コンテンツを与えなかった slot についてはデフォルトのUIにフォールバックされる。
選択肢3のアプローチ(Shadow DOM replacement)
注:3つの選択肢のうち、これだけ親となるセクションが異なっていて、文章のつながりにいまいち自信が持てなかった。けど、おそらく上述した選択肢の3番目の説明にあたると思われる。
attachShadow()
を呼ぶことで、UIをまるごと開発者が用意したコンポーネント で置き換えちゃうアプローチ。
let customSelect = document .createElement('select' );
customSelect.setAttribute("custom" , "" );
let selectShadow = customSelect.attachShadow({ mode: 'open' } );
selectShadow.innerHTML = `My custom select UI`;
document .body.appendChild(customSelect);
今はコントロール に対して attachShadow()
を実行すると例外を throw するが、カスタマイズができるようになるとこのメソッドが実行可能になる。
この場合も、プラットフォーム側は開発者が渡した shadow DOM にコアとなるパーツ(<select>
なら part="button"
と part="listbox"
)が含まれているかどうかチェックする。
もし必須のパーツが渡されなかった場合は、shadow DOMはレンダーされない。
その他
ここまでで背景やどういう解決方法になりそうなのかはなんとなく把握できましたが、元のドキュメントの内容的には半分ぐらいです。
残りはざっと読んでいくつか気になった部分をメモします。
先ほどの選択肢2のようにカスタマイズしたい内容をコントロール の子要素として配置していくアプローチの問題点として、 <input>
には子要素を配置できないという問題がある。
この問題に対するワークアラウンド として、 <input>
の各 type に対応する新しい HTMLElement を標準化することを提案している。
新しい element は対応する <input>
element と等価だが、その type と無関係なメソッドやプロパティは取り除かれたものになる。
例: <input type="range">
に対して HTMLRangeElement
を新しく導入する。これは <input type="range">
と同じメソッド、プロパティ、ふるまいを持つが、その他の input type にのみ関係するメソッド・プロパティは除外されている。この element は以下のように使うことができる。
< range>
< div slot= "thumb" part= "thumb" >< svg> </ svg></ div >
</ range>
このプロポーザルの key goal に「カスタムコントロール はデフォルトでアクセシブルである」というのがあった。
これを達成するために、part
属性の値に応じて ARIA 属性などをプラットフォーム(ブラウザー )側が暗黙的に適用する。
プロポーザル中では implicit accessibility semantics という用語を使っており、これは HTML AAM というドキュメントに定義されている implicit semantics と同じ意味合いだそう。(該当のドキュメントを読んでいないので詳しいことはわかりません)
ARIA 属性は HTML に実際に追加されるわけではないが、プラットフォームは適切な ARIA 属性が付与されているかのようにみなしてアクセシビリティ ツールに伝える。
たとえば、先ほどの
< range>
< div slot= "thumb" part= "thumb" >< svg> </ svg></ div >
</ range>
の場合、プラットフォームは <div part="thumb">
について以下のような implicit accessibility semantics をアクセシビリティ ツールに伝える。
slider
という ARIA role
aria-valuenow
aria-valuemax
aria-valuemin
ブラウザ側がこの機能に対応しているかどうかを判定する方法について。
新しい HTMLElement が導入されている要素(先ほどの、<input type="range">
に対する <range>
のように)であれば、以下のようにその要素が存在するかどうかでチェックできる。
if (!window .hasOwnProperty("HTMLRangeElement" )) {
}
そうでない場合、 attachShadow()
が例外を投げるかどうかで判断できる。
function hasCustomSelectFeature() {
try {
document .createElement("select" ).attachShadow({ mode: "open" } );
return true ;
} catch (e) {
return false ;
}
}
コントロール の中には、サードパーティ のコードにそのまま提供されていると危険な、特権的な振る舞いを持つものがある。
たとえば <select>
のドロップダウンリストはブラウザのウィンドウや iframe の外に飛び出すことが可能
このプロポーザルによって任意のコンテンツを <select>
に渡せるようになると、OSのUIになりすましたり、iframeから外のコンテンツになりすますといったことが可能
すべてのコントロール タイプは、このような潜在的 に悪用される可能性のある特権的な振る舞いのケースを慎重に調査する必要がある