ボタンのホバー・アクティブフィードバックを `oklch()` とトランスフォームトークンで改善
デフォルトテーマのニュートラルボタンで発生していた視覚フィードバックの弱さを解消するため、color-mix() の混合色算出を oklch() ベースに刷新し、--wa-button-transform-hover / --wa-button-transform-active のデザイントークンを新設しました。
背景
#2255 で報告されたように、デフォルトテーマのライトモードにおけるニュートラルボタンのホバー・クリック時のフィードバックが極めて弱く、インタラクションが視覚的に判別しにくい状態でした。原因は --wa-color-mix-hover / --wa-color-mix-active が black 固定の混合色を使用していた点にあります。
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.ts と native.css への transform 適用
トークンを実際に機能させるため、button.styles.ts と native.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 のトークン化により、これまでテーマ内に散在していたトランスフォームロジックが統一され、サードパーティテーマが一貫したインターフェースでボタンの物理的フィードバックをカスタマイズできるようになりました。