`<wa-button>`にCSSカスタムステートを追加

shoelace-style/webawesome

<wa-button>コンポーネントにdisabledicon-buttonlinkloadingの4つのCSSカスタムステートが追加されました。これにより、属性セレクタや内部実装に依存せず、:state()擬似クラスを使ってボタンのスタイリングが行えるようになります。

背景

CSSカスタムステート(Custom State Pseudo-Class)は、カスタム要素が内部状態を外部のCSSに公開するための仕組みです。これまで<wa-button>では、disabled属性やhref属性の有無に依存したセレクタでスタイルを記述する必要がありました。discuss:2185では、こうした状態をより簡潔にスタイリングする手段として@cssstateによるカスタムステートの公開が提案されていました。

すでに<wa-copy-button>では:state(success):state(error)が実装されており、今回の変更はその方針を<wa-button>に拡張したものです。コンポーネント間でスタイリングのアプローチが統一されることで、ユーザーは一貫したAPIでスタイルをカスタマイズできるようになります。

技術的な変更

button.tsに状態の同期ロジックが追加され、各プロパティの変更時にcustomStatesへ値が反映されるようになりました。

disabledloadingについては、既存の@watchデコレータのハンドラにcustomStates.set()の呼び出しを追加しています。

@watch('disabled', { waitUntilFirstUpdate: true })
handleDisabledChange() {
  this.customStates.set('disabled', this.disabled);
  this.updateValidity();
}

@watch('loading', { waitUntilFirstUpdate: true })
handleLoadingChange() {
  this.customStates.set('loading', this.loading);
}

linkステートは新たに追加されたhandleHrefChange()ハンドラで管理されます。this.isLink()の戻り値を使うことで、hrefの有無に基づくリンクモードへの切り替えを正確にステートに反映します。

@watch('href')
handleHrefChange() {
  this.customStates.set('link', this.isLink());
}

icon-buttonステートはスロットの内容変化を検出する既存のロジック内で更新されます。アイコンのみでテキストや他の要素を含まない場合にtrueとなるthis.isIconButtonの値をそのまま使用しています。

// It's only an icon button if there's an icon and nothing else
this.isIconButton = hasIcon && !hasText && !hasOtherElements;
this.customStates.set('icon-button', this.isIconButton);

これらの変更により、ユーザーは以下のようなセレクタでスタイルを記述できます。

wa-button:state(loading) {
  opacity: 0.7;
}

wa-button:state(icon-button) {
  border-radius: 50%;
}

設計判断

今回の変更では、新しい状態管理の仕組みを導入せず、既存の@watchパターンにcustomStates.set()の呼び出しを追加する方式が採用されました。

各ステートの更新は、それぞれ対応するプロパティの変更ハンドラに委ねられており、状態の同期ポイントが分散しています。一方で、この設計はそれぞれのハンドラが単一の責務を持つという既存のアーキテクチャとの一貫性を保っています。icon-buttonのようにプロパティではなくスロット内容に依存する状態も、既存のスロット変化検出ロジックの中で自然に扱われています。

また、JSDocコメントに@cssstateタグでステートが文書化されており、ツールによる自動検出やドキュメント生成への対応も意識された変更となっています。

まとめ

今回の変更は、既存の内部状態管理の仕組みを活かしながら、最小限の追加コードでCSSカスタムステートを公開するものです。<wa-copy-button>で確立したパターンを<wa-button>に適用することで、コンポーネントライブラリ全体でのスタイリングAPIの一貫性が高まります。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
f9c107d4

この記事は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)の形式、PR番号のリンク記法([PR #2214](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

CSSカスタムステートやWeb Componentsのデコレータなど、専門知識を持つエンジニアを対象とした適切な技術レベルで記述されています。

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

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

各セクションが総論→各論の構造を持ち、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則を遵守しています。非常に可読性が高いです。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロックは、提供されたDiffの内容と完全に一致しており、ファイルパスも正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「CSSカスタムステート」「:state()擬似クラス」「@watchデコレータ」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

各プロパティの変更が`customStates`に反映される仕組みについての説明は、Diff内のコード変更と完全に整合しており、技術的に正確です。

事実の突合 ✓ PASS

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

記事内のすべての主張(変更の目的、発端となったDiscussion、先行実装の存在など)は、PRのDescriptionやDiffの内容によって裏付けられており、ハルシネーションは検出されませんでした。

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

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

PR番号(#2214)、Discussion番号(2185)などの数値や固有名詞はすべて正確に記載されています。

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

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

記事のタイトル「`<wa-button>`にCSSカスタムステートを追加」は、PRのタイトル「Add custom states to <wa-button>」の内容を正確に反映しています。

外部知識の正確性 ✓ PASS

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

記事はPR情報にない外部知識(バージョンのサポート状況、リリース日程など)を付加しておらず、事実に基づいています。「CSSカスタムステート」の一般的な説明は、文脈を補うための適切な範囲です。

時間表現の正確性 ✓ PASS

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

「すでに追加された」「すでに実装されており」といった時間表現は、PRがマージ済みであるという事実と一致しており、正確です。