🎄Open UI Advent Calendar: Day 14 / Customizable Select Element Ep.12

Published on December 14, 2024

Customizable Select Elementの関連仕様: `<selectedcontent>` - `part`属性をCSEのUA実装で使用することの問題から、`part`属性を`behavior`属性にリネームへ。`<option>`のvalueのみならず、内部コンテンツ自体を`<selectedcontent>`に反映する仕様策定の示唆

Table of Contents

Table of Contents

はじめに

Ep.11では、<selectedcontent>とはどんな要素なのか、その契機となった Issue の紹介、part属性とslot属性についてお話ししました。

2024/12/9時点でのselectの各パーツの定義 2024/12/9時点でのselectの各パーツの定義

part属性を使用することの問題

Ep.11でも述べたように、<selectmenu>内の<slot>で置き換える要素をカスタマイズ可能にしたい場合、その要素にpart属性を追加する必要があります。これは、::part()を使用して、Shadow DOM にスタイルを適用するためです。

しかし、<selectmenu>自体が別の Shadow Root にラップされていた場合はどうでしょうか?

Issue では、例えば、<my-custom-select>という別の Custom Element の Shadow Root 内に<selectmenu>がある場合、その中の<div slot="button" part="button"><my-custom-select>の外部からスタイル適用される可能性があるとしています。

CSS でmy-custom-select::part(button)というセレクタを使用すると、<my-custom-select>の Shadow Root 内の要素へのスタイル適用を許してしまうことになります。

<my-custom-select>
  <template shadowroot=open>
    <selectmenu>
      <div slot="button" part="button">Custom button</div>
      <option>Cat</option>
      <option>Dog</option>
    </select>
  </template>
</my-custom-select>

<style>
  my-custom-select::part(button) {
    /*This will match the button inside the custom element*/
    background-color: red;
    padding:20px;
  }
</style>

この問題を解決するために、part属性ではない別の属性名の立案が求められ、Open UI 内での議論へ波及します。

Rename part to behavior

Open UI 内での議論では、次のようなpartの代替案となる属性名が提案されます。

[SELECT] The use of "part" clashes with custom elements containing <selectmenu> · Issue #354 · openui/open-ui
Copy/pasted from MicrosoftEdge/MSEdgeExplainers#483 so we can discuss/bikeshed names in OpenUI. @mfreed7 says: According to the explainer, here, when authors "replace" a part of the <selectmenu> us...
[SELECT] The use of "part" clashes with custom elements containing <selectmenu> · Issue #354 · openui/open-ui favicon github.com
[SELECT] The use of "part" clashes with custom elements containing <selectmenu> · Issue #354 · openui/open-ui

最終的にbehavior属性が採用されることになり、WPT にも反映されることになります。

要素をCloneしてカスタマイズ可能にする<selectedcontent>の提案

さて、ここまでで、<selectedcontent>の背景で述べた Issue 提案時の状態になりました。

Issue の期待は、「選択された<option>のスタイルが、<select>自体のスタイルよりも優先されて表示されるようにするべき」というものだったのに対し、behavior属性とslot属性を用いると、<selectmenu>内の要素をカスタマイズ可能にすることができます。

その際に問題なのが、「選択された<option>をどう<select>自体のスタイルよりも優先させるか」つまり、「<option>の内部をどう<select>のボタンに持ってくるか」という部分でした。

仮に、「<option>の内部」を「選択された<option>の value のみ」というスコープに留めると、<option>要素が選択された際に、<slot name="selected-value">に、選択された<option>の value を反映することでカスタマイズを可能にする、というワークアラウンドが考えられている、と Greg は述べています。

<selectmenu>
  <div slot="button">
    <button behavior="button">Open</button>
    <span behavior="selected-value" slot="selected-value"></span>
  </div>
  <option style="color: blue;">Option 1</option>
  <option style="color: red;">Option 2</option>
  <option style="color: green;">Option 3</option>
</selectmenu>
let s = document.querySelector('selectmenu');
let sv = document.querySelector('[behavior=selected-value]');
let possibleOptions = document.querySelectorAll('option');

s.addEventListener('change', () => {
  possibleOptions.forEach((option) => {
    if(option.value == s.value) {
      sv.style.color = option.style.color;
    } 
  });
});

しかし、上記は単なる限定的な範囲でのワークアラウンドに過ぎず、<select>のボタン部分に反映できるのは、選択された<option>valueのみです。

もともとこの Issue を出した人が、「選択された<option>のスタイルが、<select>自体のスタイルよりも優先されて表示されるようにするべき」の達成期待値をどこまで持っていたかは不明ですが、その時点での<selectmenu>では、<option>に任意のコンテンツ/スタイルを設定できるように仕様が固まりつつあったため、「<select>のボタン部分には、選択された<option>の value に限らず、<option>内のコンテンツを反映できる方法を考えるべきか?」と、Greg から、Issue の返信として意見が募られます。

すべてのコンテンツを複製して反映するのか、複製するとしたらデフォルトなのか、オプトインなのか、それとも複製せずに value だけを反映するのか、この Issue を皮切りに議論が展開されていくことになります。


それでは、また明日⛄

See you tomorrow!

Appendix