お勧めのメディアクエリのmixin:hover-media-query
この記事では、私がいつも使用しているメディアクエリのhoverのmixinである
hover-media-queryについて紹介します。
hover-media-queryの概要
このパッケージの作者は、ブルガリアのバルナのデベロッパーAtanas Atanasovになります。
2021年4月にver1.0.0が公開され、2022年1月現在はver1.1.0となっており、比較的新しいパッケージになります。
:hover 擬似クラスの問題点
擬似クラスの:hoverのみだけで要素をスタイリングした場合、プライマリーインプットがマウスなどのホバー機能を持ったデスクトップの場合は問題ありませんが、スマホやタブレットなどのタッチスクリーンデバイスでは問題が発生します。下のMezo Istvanのブログにも紹介されているように、タッチスクリーンデバイスで要素をタップした際、タップ後もホバースタイルが適用され続けてしまいます。
MDNでも同様の内容が記載されており、スマホやタブレットなどのタッチスクリーンデバイスで、ホバースタイルが当てられた要素をタップすると、フラッシュ(ちらつき)の原因になったります。
この問題を解決するのが、Media Queries Level 4 (25 December 2021)のワーキングドラフトで定義されている、Interactin Media Featureになります。
Interaction Media Featureには、3つのクエリがあります。
-
Pointing Device Quality: the pointer feature(精度のクエリ) -
Hover Capability: the hover feature(プライマリーインプットのホバー機能のクエリ) -
All Available Interaction Capabilities: the any-pointer and any-hover features(すべてのポインティングデバイスとホバー機能に対するクエリ)
今回の:hover擬似クラスの問題を回避する最適なクエリは、Hover Capabilityになります。上記のMezo Istvanのブログにも紹介されていますが、Surfaceなどのタッチスクリーンを搭載したラップトップにおいて、タッチパッド/キーボードを取り外すとタブレットモードになり、メディアクエリはそれを正しく処理すると書かれています。
使い方
これまで説明してきたHover Capabilityを楽に導入する事ができるパッケージが、hover-media-queryになります。
ソースコードはとてもシンプルです。hover CSS Media Featureをサポートしていない、IE10/IE11/Firefox before 64も対象としています。
@mixin hover {
@media (hover: none) {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
&:active {
@content;
}
}
@media (-ms-high-contrast: none), (-ms-high-contrast: active), (-moz-touch-enabled: 0), (hover: hover) {
&:hover {
@content;
}
}
}
あとは、ホバーしたい要素に@includeでmixinを呼び出してhoverスタイルを適用します。下のコードはbuttonコンポーネントにhoverのメディアクエリを使用して、:hover擬似クラスを適用した例になります。(hover-medea-queryのコードは、abstractsディレクトリ内で管理しています)
@use '../../abstracts/' as *;
/* base style */
.c-button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
margin: 0 auto;
border-radius: 100vh; // rounded corners
// active style
&:active {
transform: translateY(fs-rem(1px));
}
// diabled style
&[disabled] {
opacity: 0.65;
pointer-events: none;
}
}
/* primary modifier */
.c-button--primary {
background-color: var(--color-neutral-lightest);
border: fs-rem(2px) solid var(--color-primary-mid);
transition: background-color 0.3s var(--ease-in-out-cubic);
// hover style
@include hover {
background-color: var(--color-primary-mid-alpha-high);
}
コンパイルした結果がこちらになります。
.c-button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
margin: 0 auto;
border-radius: 100vh;
border-radius: calc(var(--vh, 1vh) * 100);
}
.c-button:active {
transform: translateY(0.0625rem);
}
.c-button[disabled] {
opacity: 0.65;
pointer-events: none;
}
.c-button--primary {
background-color: var(--color-neutral-lightest);
border: 0.125rem solid var(--color-primary-mid);
transition: background-color 0.3s var(--ease-in-out-cubic);
}
@media (hover: none) {
.c-button--primary {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.c-button--primary:active {
background-color: var(--color-primary-mid-alpha-high);
}
}
@media (-ms-high-contrast: none), (-ms-high-contrast: active), (-moz-touch-enabled: 0), (hover: hover) {
.c-button--primary:hover {
background-color: var(--color-primary-mid-alpha-high);
}
}
hover-media-queryのmixinを使用することで、記述量を減らすことができるので助かっています。
ターゲットデバイスに適用されるメディアクエリの確認
ターゲットのデバイスがどのメディアクエリを適用するのかをテストするサイトがあります。下の画像は、私が使用しているMac Miniのテスト結果になります。主要な入力メカニズムがポインティングデバイスであり、ホバー機能を持っている事がわかります。

手元にある、Mac Mini/iPad/iPhone/androidでテストしてみた結果が以下になります(iPad/iPhone/androidはタッチスクリーンでテストしています)。このようにしてターゲットデバイスに適用される、メディアクエリを確認する事もできます。
| - | Mac | iPad | iPhone | android |
|---|---|---|---|---|
| @media(hover: none) | False | True | True | True |
| @media(hover: hover) | True | False | False | False |
| @media(any-hover: none) | False | True | True | True |
| @media(any-hover: hover) | True | False | False | False |
| @media(pointer: none) | False | False | False | False |
| @media(pointer: fine) | True | False | False | False |
| @media(pointer: coarse) | False | True | True | True |
| @media(any-pointer: none) | False | False | False | False |
| @media(any-pointer: fine) | True | False | False | False |
| @media(any-pointer: coarse) | False | True | True | True |
参考記事
本記事を書くにあたり、以下の記事を参考にしました。
- Finally, a CSS only solution to :hover on touchscreens
- Interaction Media Features and Their Potential (for Incorrect Assumptions)
- Touch Devices Should Not Be Judged By Their Size
- MDN Web Docs @media hover
- MDN Web Docs @media any-hover
- MDN Web Docs @media any-pointer
最後に
ユーザーに混乱を与えるインタラクションをしないよう、適切にhoverのメディアクエリを適用する必要があります。hover-media-queryを活用することにより、数行のコードで:hover擬似クラスの問題点を回避することができます。皆さんも試してみてください。
そして、hover-media-queryのmixinを用意してくださった、Atanasさんに感謝いたします。
Discussion