`<wa-random-content>` のアクセシビリティと API 整合性を向上

shoelace-style/webawesome

<wa-random-content> コンポーネントが、アクセシビリティ対応、API の一貫性、そしてドキュメントを大幅に改善し、実運用での利用がしやすくなりました。

背景

このコンポーネントは、スロット配下の子要素をランダムに切り替えて表示するユーティリティとして設計され、テストimonials や tip‑of‑the‑day などのローテーションに利用されます。従来は mode のデフォルトが random で、同じ項目が連続して表示される可能性があり、実装側で回避策を講じる必要がありました。

さらに、interval プロパティだけで自動再生を制御していたため、他のコンポーネント(例: <wa-carousel>)との API が揃っておらず、統一感が欠けていました。アクセシビリティ面でも、非選択要素は display:none で隠すだけで、スクリーンリーダーに状態が伝わらない問題が残っていました。

これらの課題を解消し、コンポーネントを本格的にリリースできる状態にすることが PR #2547 の主目的です。

技術的な変更

API のデフォルトとプロパティ構成を統一しました。mode のデフォルトが unique に変更され、連続表示の重複が防止されます。また、interval が廃止され、autoplay(Boolean) と autoplay-interval(ミリ秒) に置き換えられ、 <wa-carousel> と同じインターフェースになりました。

@@ -1,10 +1,14 @@
-import { html } from 'lit';
-import { customElement, property } from 'lit/decorators.js';
+import { html, type PropertyValues } from 'lit';
+import { customElement, property, state } from 'lit/decorators.js';
+import { WaContentChangeEvent } from '../../events/random-content-change.js';
+import { prefersReducedMotion } from '../../internal/animate.js';
@@ -84,7 +88,10 @@
- * @slot - The pool of children to randomize from. Unselected children are hidden with `display: none`.
+ * @slot - The pool of children to choose from. Only direct element children are eligible; unselected
+ *  children are hidden with the `hidden` attribute.
+ *
+ * @event {{ items: Element[] }} wa-content-change - Emitted whenever the displayed selection changes,
+ *  including on first render, on `randomize()`, and on each autoplay tick.

randomize() が選択要素を返すようにシグネチャを拡張し、呼び出し側が即座に結果を利用できるようになりました。

@@ -103,7 +103,8 @@
-  randomize() {
-    // existing logic
-  }
+  /** Returns the elements currently shown after randomization. */
+  randomize(): Element[] {
+    // existing logic returns selected elements
+  }

非選択子の非表示手段を hidden 属性に統一しました。スタイルシートに ::slotted([hidden]) { display: none !important; } を追加し、子要素が独自の display を持っていても確実に隠せます。

@@ -5,6 +5,15 @@
 export default css`
     display: contents;
   }

+  /*
+   * Force-hide unselected children. A bare [hidden] (display: none from the UA sheet) loses to any
+   * author display set on the child — a utility class like .wa-flank, or a component's own
+   * :host display — so children with their own layout wouldn't actually hide without this.
+   */
+  ::slotted([hidden]) {
+    display: none !important;
+  }

自動再生は AutoplayController を採用し、ホバー・フォーカス・タッチ時に一時停止し、 reduced motion が有効な環境ではアニメーションをスキップしつつ再生は続行します。prefers-reduced-motion の CSS メディアクエリでもアニメーションが無効化されるようにガードを追加しています。

@@ -68,7 +68,13 @@
-  private intervalId: ReturnType<typeof setInterval> | undefined;
+  private isInitialSelection = true;
+  private autoplayController = new AutoplayController(this, () => this.randomize());
@@ -72,3 +78,4 @@
-  // implementation of autoplay using interval
+  // AutoplayController internally manages pause/resume based on user interaction

ドキュメントと変更履歴の更新も行い、random-content.md に使用例、モード比較表、SSR 配慮、アクセシビリティ注意点を追記し、changelog.md では実験的コンポーネントの追加旨を明示しました。

設計判断

API 整合性の優先が見て取れます。autoplayautoplay-interval を導入したことで、他の回転系コンポーネントと同一の制御パターンが確立され、学習コストが低減されました。これは PR の議論でも新規キー autoplay が採用された背景です。

後方互換性の維持のため、mode のデフォルト変更は「randomunique」という安全な上書きで行われ、既存コードが mode を明示しない限り新しい挙動が適用されます。hidden 属性への移行も、従来の display:none を使用しているケースと併用できるように実装され、既存スタイルに衝突しません。

イベント命名規則の統一として、wa-content-change を導入し、wa-<noun>-change パターンに沿う形にしました。これにより、開発者はコンポーネントごとのイベント名を推測しやすく、ハンドラ実装がシンプルになります。

アクセシビリティへの配慮は、視覚的に隠すだけでなく、role="status" のライブリージョンを追加し、画面読取装置が回転を認識できるようにした点で評価できます。また、AutoplayController によるインタラクションベースの一時停止は、キーボードユーザーがコンテンツを操作できない状況でも停止手段を提供します。

まとめ

今回の PR は、<wa-random-content> のデフォルト動作、プロパティ構成、非選択要素の隠蔽方式、イベント設計、アクセシビリティ対応を包括的に見直し、他コンポーネントと一貫した API を実現した大幅なリファクタリングです。これにより、開発者はコンポーネントを安全かつ直感的に組み込め、ユーザーはアクセシブルなローテーション体験を得られます。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
92f6555f

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文、背景、技術的変更、まとめの三部構成が明確に存在し、設計判断もオプションで記載されています。

カスタムMarkdown構文 ⚠ WARNING

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

PRリンクが推奨の `[ #2547 ](URL)` 形式ではなく `[PR #2547](URL)` となっているため、カスタムMarkdown記法に違反しています。コードブロックのファイル名付きハイライトは使用されていませんが、エラーはありません。

対象読者への適合性 ✓ PASS

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

記事は Lit や Web Components に関する前提知識を持つエンジニア向けに書かれており、過度な初心者向け解説はありません。

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

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

各セクションは総論・各論・結論のパラグラフ構成になっており、トピックセンテンスが先頭に位置し、段落は1トピック・6文未満で空行で区切られています。

Diff内容との照合 ✓ PASS

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

記事内の Diff ブロックは PR の実際の Diff と一致しており、ファイル名・変更内容ともに正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている用語(AutoplayController、hidden属性、wa-content-change など)は PR と一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

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

技術的説明は PR の記述と合致しており、因果関係も論理的です。

事実の突合 ✓ PASS

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

記事の全ての主張は PR のタイトル、説明、Diff、コミットメッセージに裏付けられており、推測や捏造はありません。

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

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

PR 番号 #2547 以外の数値や固有名詞は記載されておらず、誤りはありません。

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

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

記事タイトルは PR の内容(アクセシビリティ改善、API 整合性、Docs)を適切に日本語で要約しています。

外部知識の正確性 ✓ PASS

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

LTS、リリース日程など PR に記載されていない外部情報は含まれていません。

時間表現の正確性 ✓ PASS

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

時間表現の歪曲はなく、PR の表現と一致しています。