ボタンのホバー・アクティブフィードバックを `oklch()` とトランスフォームトークンで改善

shoelace-style/webawesome

デフォルトテーマのニュートラルボタンで発生していた視覚フィードバックの弱さを解消するため、color-mix() の混合色算出を oklch() ベースに刷新し、--wa-button-transform-hover / --wa-button-transform-active のデザイントークンを新設しました。

背景

#2255 で報告されたように、デフォルトテーマのライトモードにおけるニュートラルボタンのホバー・クリック時のフィードバックが極めて弱く、インタラクションが視覚的に判別しにくい状態でした。原因は --wa-color-mix-hover / --wa-color-mix-activeblack 固定の混合色を使用していた点にあります。

black を基準色として color-mix() で合成すると、有彩色のボタンでは彩度が著しく低下し(いわゆる「desaturation」)、元の色味とかけ離れた暗い色に変化してしまいます。特にアクセントカラー系のボタンで色崩れが顕著でした。

この問題はトークンの再定義という最小限の変更で解決できるため、PR #2360 では全テーマの該当トークンを一括更新し、併せてトランスフォームによる物理的フィードバックも追加しています。

技術的な変更

color-mix() の混合色を oklch() 相対色構文に変更

--wa-color-mix-hover--wa-color-mix-active の算出ロジックを black 固定から oklch() の相対色構文(Relative Color Syntax)に変更することで、ボタン本来の色相・彩度を保持したままフィードバック色を生成できるようになりました。

変更前(default / awesome テーマ共通):

--wa-color-mix-hover: black 10%;
--wa-color-mix-active: black 20%;

変更後(default テーマ、ライトモード):

--wa-color-mix-hover: oklch(from currentColor calc(1 - l) c h) 10%;
--wa-color-mix-active: var(--wa-color-surface-default) 10%;

--wa-color-mix-hover では oklch(from currentColor calc(1 - l) c h) という相対色構文を使用しています。これは currentColor の輝度 l を反転させた色(明るい色なら暗く、暗い色なら明るく)を混合色として生成します。色相 h と彩度 c はそのまま保持されるため、彩度の劣化が生じません。--wa-color-mix-active--wa-color-surface-default(ページ背景色)を混合することで、ボタンが背面に沈み込むような視覚効果を再現しています。

各テーマで採用された変換式は以下の通りです:

  • default / awesome テーマ: hover は calc(1 - l) で輝度を反転、active は --wa-color-surface-default で表面に押し込む効果
  • shoelace テーマ: hover / active ともに oklch() で輝度を ±0.1 シフトする独自実装(ライト・ダークモードで増減が逆転)

--wa-button-transform-hover / --wa-button-transform-active トークンの新設

--wa-button-transform-hover--wa-button-transform-active を新たなデザイントークンとして導入し、各テーマが独立してボタンのトランスフォーム挙動を定義できるようにしました。

各テーマの設定値は以下の通りです:

テーマ --wa-button-transform-hover --wa-button-transform-active
default none scale(0.9875)
awesome none translate(var(--wa-shadow-offset-x-s), var(--wa-shadow-offset-y-s))
shoelace none none

awesome テーマが以前から独自に持っていた transform: translate(...) によるアクティブ時の沈み込み効果は、このトークン化により他テーマと同じ仕組みで管理されるようになっています。元の実装では .button:active に直接 transform を記述していましたが、それが削除されトークン経由に統一されました。

button.styles.tsnative.css への transform 適用

トークンを実際に機能させるため、button.styles.tsnative.css の両方にトランスフォーム適用ロジックを追加しました。

/* transition-property に transform を追加 */
transition-property: background, border, box-shadow, color, opacity, transform;
transform-origin: center;

/* Hover and active transforms */
.button:not(.disabled):not(.loading) {
  @media (hover: hover) {
    &:hover {
      transform: var(--wa-button-transform-hover);
    }
  }
  &:active {
    transform: var(--wa-button-transform-active);
  }

  @media (prefers-reduced-motion: reduce) {
    &:hover,
    &:active {
      transform: none;
    }
  }
}

@media (hover: hover) でタッチデバイスでのホバー状態を除外し、@media (prefers-reduced-motion: reduce) でモーションを無効化できる点も考慮されています。ネイティブボタン向けの native.css にも同様のロジックが追加されており、カスタム要素と同一の挙動が保証されています。

設計判断

テーマレベルのデザイントークンとして管理する方式 が採用されました。

PR内のコメントには、--wa-button-transform-hover のようなボタン専用トークンと、--transform-hover のようなコンポーネントスコープのプロパティとの間で検討があったことが示されています。トランスフォーム挙動がテーマごとに異なること(default はスケール、awesome はトランスレート、shoelace は無効)を踏まえ、テーマ側で一元定義するデザイントークン方式が選ばれています。

--wa-color-mix-active--wa-color-surface-default を採用したことも注目点です。ボタンの色を暗くするのではなく、背景色を混ぜ込むことで「ページ面に押し込まれる」ような感覚を演出するこのアプローチは、単なる輝度操作とは異なる奥行き感を生み出します。

また、全ての transform 適用箇所で @media (prefers-reduced-motion: reduce) が適切に実装されており、アクセシビリティへの配慮がコンポーネントレベルで一貫して組み込まれています。

まとめ

color-mix() の混合色を oklch() 相対色構文に移行したことで、テーマの彩度を維持しながら視覚フィードバックの明瞭さを改善しています。--wa-button-transform-hover / --wa-button-transform-active のトークン化により、これまでテーマ内に散在していたトランスフォームロジックが統一され、サードパーティテーマが一貫したインターフェースでボタンの物理的フィードバックをカスタマイズできるようになりました。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
24b76906

この記事は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:path/to/file.css```)やGitHubのPR/Issueリンク記法([#123](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

「oklch()」「相対色構文」「デザイントークン」といった専門用語を適切に使用しており、対象読者であるエンジニアに適した技術レベルで記述されています。

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

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

各セクションが要旨を述べるパラグラフから始まり、各パラグラフもトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、可読性が非常に高いです。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロックは、提供されたDiff情報と完全に一致しており、ファイルパスも正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「oklch()相対色構文」「デザイントークン」「prefers-reduced-motion」など、使用されている技術用語はすべて正確で、文脈に適しています。

説明の技術的正確性 ✓ PASS

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

「oklch()」による彩度維持の仕組みや、「--wa-color-surface-default」を混合する効果の説明など、技術的な解説が正確かつ論理的です。

事実の突合 ✓ PASS

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

記事内のすべての主張は、PRのDescription、Diff、コメントで裏付けられています。特に「設計判断」セクションはPR内の議論を正確に反映しており、ハルシネーションは見られません。

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

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

PR番号(#2360)、Issue番号(#2255)、CSSのプロパティ値(`scale(0.9875)`など)といった数値や固有名詞はすべて正確です。

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

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

記事のタイトルはPRのタイトル「Improve hover and active feedback」の内容をより具体的に(`oklch()`とトランスフォームトークン)説明しており、PRの主題と完全に一致しています。

外部知識の正確性 ✓ PASS

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

記事の内容は提供されたPR情報に限定されており、サポート状況やリリース予定といったPR外の知識の追記はありません。

時間表現の正確性 ✓ PASS

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

「awesome テーマが以前から独自に持っていた」といった時間的な前後関係を含む表現が、Diffの内容と一致しており正確です。