Badgeコンポーネントにstartとendスロットを追加

shoelace-style/webawesome

Web Awesome の <wa-badge> コンポーネントは、アイコンなどの装飾要素を配置するための startend スロットを新たに提供します。これまでCSSのみで実装されていたスロット内アイコンのスタイリングを廃止し、明示的なスロット指定による確実な配置制御を可能にした変更です。

背景

従来の実装では、::slotted(wa-icon) セレクタを使用してデフォルトスロット内のアイコンに自動的にマージンを適用していました。しかしこのアプローチには2つの問題がありました。

第一に、マージン適用が一方向のみを想定していました。margin-inline-end: var(--wa-space-2xs, 0.25em) の指定は、アイコンが常にバッジコンテンツの先頭に配置されることを前提としており、末尾に配置する場合には対応できません。

第二に、CSSの :first-child:last-child による条件付きスタイリングが機能しませんでした。<wa-tab> などの他のコンポーネントでは以下のような実装を試みていました:

::slotted(wa-icon:first-child) {
  margin-inline-end: 0.5em;
}

::slotted(wa-icon:last-child) {
  margin-inline-start: 0.5em;
}

しかし、デフォルトスロットにテキストノードとアイコンが混在する場合、アイコン要素は :first-child:last-child の両方にマッチしてしまい、意図しない両側へのマージン適用が発生していました。#2072 がこの問題を指摘しています。

技術的な変更

packages/webawesome/src/components/badge/badge.ts において、レンダリングロジックが3つのスロットを持つ構成に変更されました。

変更前:

render() {
  return html` <slot part="base" role="status"></slot>`;
}

変更後:

render() {
  return html`
    <slot name="start" part="start"></slot>

    <slot part="base" role="status"></slot>

    <slot name="end" part="end"></slot>
  `;
}

スタイルシートでは、::slotted(wa-icon) への自動スタイリングを削除し、名前付きスロットへの明示的なマージン指定に置き換えました:

/* Slots */
slot[name='start']::slotted(*) {
  margin-inline-end: 0.375em;
}

slot[name='end']::slotted(*) {
  margin-inline-start: 0.375em;
}

また、バッジ要素自体に vertical-align: middle を追加し、内部コンテンツに依存しない一貫した垂直配置を実現しています。従来の実装では、スロット内アイコンのサイズ調整(height: 0.85em)によって配置を制御していましたが、この方法では他のコンテンツタイプに対応できませんでした。

使用例として、以下のようにアイコンを配置できます:

<wa-badge>
  <wa-icon slot="start" name="acorn"></wa-icon>
  Start
</wa-badge>
<wa-badge>
  <wa-icon slot="end" name="tree-deciduous"></wa-icon>
  End
</wa-badge>
<wa-badge>
  <wa-icon slot="start" name="cow"></wa-icon>
  <wa-icon slot="end" name="ufo-beam"></wa-icon>
  Both
</wa-badge>

startend はCSSパーツとしても公開されているため、プロジェクト固有のスタイルカスタマイズが可能です。

設計判断

明示的なスロット指定によるオプトイン方式が採用されました。

PRの説明では、CSSのみでの自動スタイリングを「needlessly opinionated and fragile(不必要に独断的で壊れやすい)」と評価しています。:first-child:last-child の擬似クラスは、Shadow DOMのスロット投影とテキストノードの組み合わせにおいて予測可能な動作を保証できません。

名前付きスロットの導入により、開発者は装飾要素の配置を明示的に制御できるようになりました。これは、デフォルトスロットへの暗黙的なスタイル適用よりも予測可能で、他のコンテンツタイプ(テキスト、カスタム要素など)との競合を回避できます。

PR作成者は「Slots give us much more reliable control of spacing」と述べており、この判断は <wa-tab> など他のコンポーネントへの適用も検討されています。Web Componentsのスロットメカニズムを活用した、より堅牢なAPI設計への移行といえます。

本PRは、CSS擬似クラスの限界を認識し、Web Componentsの機能を適切に活用してAPIの予測可能性を高めた変更です。暗黙的なスタイリングルールを削除し、明示的なスロット指定に置き換えることで、開発者はバッジ内の装飾要素の配置を確実に制御できるようになりました。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「総論→各論→結論」の構成が記事全体に適用されており、非常に構造的で理解しやすい。リード文、背景、技術詳細、設計判断、まとめの各要素がすべて含まれている。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```typescript:path/to/file.ts```)やGitHubのPR/Issueへのリンク記法([#2072](URL))が正しく使用されている。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Web ComponentsやShadow DOMに関する専門的な内容を、冗長な説明を省いて記述しており、対象読者であるエンジニアに適している。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落はトピックセンテンスで始まっている。1段落1トピックの原則も守られており、可読性が高い。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されているコード(コンポーネントのrenderメソッド、CSSスタイル、使用例)は、提供されたDiff情報と完全に一致している。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「::slotted()」「Shadow DOM」「擬似クラス」「CSSパーツ」などの技術用語が、文脈に応じて正確かつ適切に使用されている。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

「:first-childと:last-childがテキストノードと混在すると両方にマッチする」問題など、技術的な説明がPRの情報に基づいており、正確で論理的である。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張は、PRのタイトル、Description、Diffの内容によって裏付けられており、ハルシネーション(捏造)は見られない。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#2082)やIssue番号(#2072)などの数値・固有名詞は正確に記載されている。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「Badgeコンポーネントにstartとendスロットを追加」は、PRのタイトルと内容を正確に反映している。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR情報に含まれないバージョンサポート状況やリリース日程などの外部知識は記載されておらず、事実に基づいた内容となっている。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「〜しました」「〜が採用されました」といった過去形・完了形の表現が使われており、PRが完了した変更であることを正しく反映している。