カードリンクのホバー演出をユーティリティクラスに統一

shoelace-style/webawesome

ドキュメントサイト各所で個別に記述されていたカードホバーの CSS を、hover-growhover-emphasize-border の2つのユーティリティクラスに集約しました。これにより、散在していたインライン定義が廃止され、ホバー演出の一貫性が担保されます。

背景

ホバー演出の定義が複数箇所に分散し、実装が揺れていました。docs.css にはカードグリッド向けのホバースタイル(border-color 変更と box-shadow によるリング)がインラインで記述されており、#1085 でドキュメントインデックスを復元した際に導入されたものです。一方、hover-grow(スケールアニメーション)は webawesome-prosite.css にのみ存在し、webawesome 側のドキュメントからは参照できない状態でした。

同一のカードグリッドパターンが /docs/components/docs/tokens/docs/utilities/docs/frameworks の4箇所に存在するにもかかわらず、ホバー演出の実装方法が統一されていなかったことが、このリファクタリングの直接的な動機です。

技術的な変更

utils.css に2つのユーティリティクラスが追加され、docs.css からはインライン定義が削除されました。

hover-grow / hover-grow-from-direct-childwebawesome-prosite.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-colorvar(--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 }}">

設計判断

outlinebox-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 への切り替えは実装上の正確性を高めるとともに、リング幅のトークン化によってテーマとの一貫性も強化されています。

記事メタデータ

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

この記事は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リンク記法の正確性

ファイル名付きシンタックスハイライト(`css:ファイルパス`)およびGitHubのPR番号へのリンク記法(`[#1085](URL)`)が正しく使用されています。

対象読者への適合性 ✓ PASS

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

CSSユーティリティクラス、カスタムプロパティ、`outline`と`box-shadow`の使い分けなど、専門知識を持つエンジニアを対象とした適切な技術レベルで記述されています。

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

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

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

Diff内容との照合 ✓ PASS

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

記事内のすべてのコードブロック(追加されたCSSユーティリティ、削除されたインラインCSS、変更後のHTML)が、提供されたDiff情報と正確に一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「ユーティリティクラス」「CSSカスタムプロパティ」「トークン」「:has()セレクター」などの技術用語が文脈に応じて正確に使用されています。

説明の技術的正確性 ✓ PASS

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

`box-shadow`が既存の影を上書きする問題点と、`outline`を用いることでの解決策についての説明が技術的に正確かつ論理的です。

事実の突合 ✓ PASS

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

記事内のすべての主張(`hover-grow`の昇格、`#1085`で導入されたルールの置換など)が、PRのDescriptionやDiffによって裏付けられており、ハルシネーションは検出されませんでした。

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

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

PR番号(#2390)、参照PR番号(#1085)、CSSのプロパティ値(`1.025`, `0.0625rem`)など、数値や固有名詞はすべて正確です。

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

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

記事のタイトル「カードリンクのホバー演出をユーティリティクラスに統一」は、PRの主題「Standardizing Hover Utilities for Card Links」を的確に表現しています。

外部知識の正確性 ✓ PASS

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

記事の内容は提供されたPR情報に完全に準拠しており、バージョンサポート状況やリリース予定など、PR外の知識の追記は見られませんでした。

時間表現の正確性 ✓ PASS

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

変更前(「〜でした」「〜存在し」)と変更後(「〜しました」)の状態を示す時間表現が、PRの文脈と一致しており、正確です。