ランダムコンテンツコンポーネントの追加
Web Awesome に新たに wa-random-content コンポーネントが実装され、スロットされた子要素をランダムに表示し、残りを自動で非表示にできるようになりました。これにより、テストimonial のローテーションや「今日の一言」など、静的ページに変化をもたらすユーティリティが標準化されます。
背景
Stretch goal #1076 で掲げられた 14 方向の新コンポーネントのうち、ランダムコンテンツは「ページに刺激を与える」役割を担うことが期待されていました。既存のコンポーネント群は明示的なレイアウトやインタラクションに特化していたため、同一ページ内で表示内容を周期的に変える手段が不足していました。この PR は、そのギャップを埋めるために作成されました。
技術的な変更
実装概要:wa-random-content は WebAwesomeElement を継承した Lit カスタムエレメントとして実装され、属性 items, mode, animation を公開します。また、手動トリガー用メソッド randomize() と、表示が変わるたびに発火するカスタムイベント wa-content-change を提供します。
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';
import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import visuallyHidden from '../../styles/component/visually-hidden.styles.js';
import { AutoplayController } from '../carousel/autoplay-controller.js';
import styles from './random-content.styles.js';
/**
* @summary Selects one or more child elements at random and displays them, hiding the rest.
* @status experimental
* @since 3.9
*/
@customElement('wa-random-content')
export default class WaRandomContent extends WebAwesomeElement {
@property({ type: Number }) items = 1;
@property({ type: String }) mode: 'random' | 'unique' | 'sequence' = 'random';
@property({ type: String }) animation = 'none';
@state() private _shown: Element[] = [];
/** Imperatively trigger a new selection using the current mode. */
randomize() {
this._choose();
}
// ...内部ロジックは省略
}
スタイルと非表示ロジック:::slotted([hidden]) で選択されなかった子要素を display:none !important にし、スロットされたコンテンツが意図通り隠れるようにしています。さらに、アニメーション用の @keyframes をドキュメントスコープとシャドウルート双方に注入し、Chromium と WebKit の挙動差を吸収しています。
:host { display: contents; }
::slotted([hidden]) { display: none !important; }
@keyframes wa-rc-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes wa-rc-fade-up { from { opacity: 0; transform: translateY(var(--animation-translate, 0.5em)); } to { opacity: 1; transform: translateY(0); } }
/* その他方向の keyframes も同様 */
カスタムイベント:表示が変わるたびに WaContentChangeEvent が生成され、detail.items に現在表示中の要素配列を格納します。イベントはバブルし、コンポジションツリー全体で購読可能です。
export class WaContentChangeEvent extends Event {
readonly detail: { items: Element[] };
constructor(detail: { items: Element[] }) {
super('wa-content-change', { bubbles: true, cancelable: false, composed: true });
this.detail = detail;
}
}
SSR エクスポート:packages/webawesome/src/ssr/all.ts に WaRandomContent が追加され、サーバーサイドレンダリングでも利用できるよう統合されています。
export { default as WaRandomContent } from '../components/random-content/random-content.js';
テスト網羅:random-content.test.ts ではアクセシビリティ、初期表示、属性変化、各モードの振る舞い、randomize() の即時効果、wa-content-change の発火、そして prefers-reduced-motion に対する配慮まで 500 行近くにわたって検証しています。
設計判断
属性設計:items、mode、animation を単一コンポーネントに集約することで、既存の属性パターンと一貫性を保ちつつ、拡張性を確保しています。items は 1 以上子要素数以下にクランプし、mode は列挙型で明示的に管理することで不正入力を防止しています。
モード実装:random がシンプルな乱択、unique が直前の選択を除外、sequence が順次巡回という形で、利用シーンに応じたアルゴリズムを提供しています。これらは内部で同一ロジックを状態管理 (_shown とインデックス) に集約し、重複ロジックを排除しています。
アニメーション戦略:アニメーション名を CSS カスタムプロパティで切り替える設計にし、animation 属性に none|fade|fade-up|... を指定するだけでエントランス効果を変更可能にしています。さらに、prefers-reduced-motion が有効な環境ではアニメーションを自動的に無効化し、アクセシビリティ要件に合致させています。
再利用性と SSR:コンポーネントは WebAwesomeElement と AutoplayController を組み合わせ、他のカルーセル系コンポーネントと同様の自動再生ロジックを共有しています。SSR エクスポートへの追加は、ビルド時のツリーシェイクに影響しないようシンプルな再エクスポートで実装されています。
まとめ
wa-random-content はランダム表示という汎用ユーティリティを公式コンポーネントとして提供し、属性ベースの設定・手動トリガー・イベント通知という標準的なインターフェースを備えました。スタイルとアニメーションのクロスブラウザ対応、アクセシビリティ考慮、SSR への統合といった設計判断により、既存の Web Awesome エコシステムにシームレスに組み込める形で実装されています。