`<wa-random-content>` のアクセシビリティと API 整合性を向上
<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 整合性の優先が見て取れます。autoplay と autoplay-interval を導入したことで、他の回転系コンポーネントと同一の制御パターンが確立され、学習コストが低減されました。これは PR の議論でも新規キー autoplay が採用された背景です。
後方互換性の維持のため、mode のデフォルト変更は「random → unique」という安全な上書きで行われ、既存コードが mode を明示しない限り新しい挙動が適用されます。hidden 属性への移行も、従来の display:none を使用しているケースと併用できるように実装され、既存スタイルに衝突しません。
イベント命名規則の統一として、wa-content-change を導入し、wa-<noun>-change パターンに沿う形にしました。これにより、開発者はコンポーネントごとのイベント名を推測しやすく、ハンドラ実装がシンプルになります。
アクセシビリティへの配慮は、視覚的に隠すだけでなく、role="status" のライブリージョンを追加し、画面読取装置が回転を認識できるようにした点で評価できます。また、AutoplayController によるインタラクションベースの一時停止は、キーボードユーザーがコンテンツを操作できない状況でも停止手段を提供します。
まとめ
今回の PR は、<wa-random-content> のデフォルト動作、プロパティ構成、非選択要素の隠蔽方式、イベント設計、アクセシビリティ対応を包括的に見直し、他コンポーネントと一貫した API を実現した大幅なリファクタリングです。これにより、開発者はコンポーネントを安全かつ直感的に組み込め、ユーザーはアクセシブルなローテーション体験を得られます。