カードリンクのホバー演出をユーティリティクラスに統一
ドキュメントサイト各所で個別に記述されていたカードホバーの CSS を、hover-grow と hover-emphasize-border の2つのユーティリティクラスに集約しました。これにより、散在していたインライン定義が廃止され、ホバー演出の一貫性が担保されます。
背景
ホバー演出の定義が複数箇所に分散し、実装が揺れていました。docs.css にはカードグリッド向けのホバースタイル(border-color 変更と box-shadow によるリング)がインラインで記述されており、#1085 でドキュメントインデックスを復元した際に導入されたものです。一方、hover-grow(スケールアニメーション)は webawesome-pro の site.css にのみ存在し、webawesome 側のドキュメントからは参照できない状態でした。
同一のカードグリッドパターンが /docs/components・/docs/tokens・/docs/utilities・/docs/frameworks の4箇所に存在するにもかかわらず、ホバー演出の実装方法が統一されていなかったことが、このリファクタリングの直接的な動機です。
技術的な変更
utils.css に2つのユーティリティクラスが追加され、docs.css からはインライン定義が削除されました。
hover-grow / hover-grow-from-direct-child は webawesome-pro の site.css から utils.css へ昇格したクラスです。CSS カスタムプロパティ --hover-grow-scale(デフォルト 1.025)で拡大率をインスタンスごとに上書きできます。:hover と :focus-visible の両方をトリガーとし、hover-grow-from-direct-child は :has() セレクターで直接子要素の a または button のホバーを親要素のスケールに連動させます。
.hover-grow,
.hover-grow-from-direct-child {
--hover-grow-scale: 1.025;
transform-origin: center;
transition: transform var(--wa-transition-slow) var(--wa-transition-easing);
}
.hover-grow:is(:hover, :focus-visible) {
transform: scale(var(--hover-grow-scale));
}
/* Scale when a direct child link or button is hovered */
.hover-grow-from-direct-child:has(> :is(a, button):hover),
.hover-grow-from-direct-child:has(> :is(a, button):focus-visible) {
transform: scale(var(--hover-grow-scale));
}
@media (prefers-reduced-motion: reduce) {
.hover-grow,
.hover-grow-from-direct-child {
transition: none;
}
/* ... */
}
hover-emphasize-border は今回新設されたクラスです。子要素の wa-card に対してホバー時に border-color を var(--wa-color-brand-border-loud) へ切り替え、さらに outline でリングを追加します。リングの幅はテーマ変数 --wa-border-width-s に従います。
.hover-emphasize-border > wa-card {
outline: var(--wa-border-width-s) solid transparent;
transition:
border-color var(--wa-transition-normal),
outline-color var(--wa-transition-normal);
}
.hover-emphasize-border:is(:hover, :focus-visible) > wa-card {
border-color: var(--wa-color-brand-border-loud);
outline-color: var(--wa-color-brand-border-loud);
}
変更前の docs.css では box-shadow でリングを模倣していましたが、box-shadow はカードの静止時のエレベーション影を上書きしてしまう問題がありました。
/* 変更前(docs.css)*/
a:hover wa-card,
a:focus-visible wa-card {
border-color: var(--wa-color-brand-border-loud);
box-shadow: 0 0 0 0.0625rem var(--wa-color-brand-border-loud);
}
outline を用いることで、wa-card が持つエレベーション影(box-shadow)はすべてのアピアランスで維持されます。また、リング幅が固定値 0.0625rem からトークン --wa-border-width-s に変わったため、テーマ変更に追従するようになっています。
適用箇所では、各テンプレートファイルのアンカータグに hover-grow hover-emphasize-border クラスが追加されました。
<!-- 変更前 -->
<a href="{{ page.url }}">
<!-- 変更後 -->
<a class="hover-grow hover-emphasize-border" href="{{ page.url }}">
設計判断
outline を box-shadow の代替として採用した点 がこの変更の核心的な設計判断です。box-shadow はスタッキングコンテキストに依存せず視覚的なリングを表現できますが、コンポーネントが持つ既存の影を同一プロパティで上書きするリスクがあります。outline はレイアウトに影響せず(box-sizing の外側に描画される)、box-shadow とは独立したプロパティであるため、カードの静止状態の影を保持しながらホバー時のリングを重ねることができます。
hover-emphasize-border が子セレクター > wa-card を直接ターゲットにしている点も注目すべき選択です。ユーティリティクラス自体はアンカー(<a>)に付与されますが、視覚的な変化は内包する wa-card に適用されます。これにより、リンクのクリック領域とカードの視覚的境界を分離しつつ、ホバー状態を親から子へ伝搬する構造を実現しています。
prefers-reduced-motion: reduce への対応も utils.css 内に含まれており、スケールアニメーションを無効化するアクセシビリティ配慮が hover-grow の定義と同じ場所に集約されています。
まとめ
分散していたホバー実装を utils.css の2クラスに集約することで、カードグリッドの演出定義が単一の場所に管理されるようになりました。box-shadow から outline への切り替えは実装上の正確性を高めるとともに、リング幅のトークン化によってテーマとの一貫性も強化されています。