👌

お勧めのメディアクエリのmixin:hover-media-query

2022/01/17に公開

この記事では、私がいつも使用しているメディアクエリのhovermixinである
hover-media-queryについて紹介します。

hover-media-queryの概要

このパッケージの作者は、ブルガリアのバルナのデベロッパーAtanas Atanasovになります。

2021年4月にver1.0.0が公開され、2022年1月現在はver1.1.0となっており、比較的新しいパッケージになります。

:hover 擬似クラスの問題点

擬似クラスの:hoverのみだけで要素をスタイリングした場合、プライマリーインプットがマウスなどのホバー機能を持ったデスクトップの場合は問題ありませんが、スマホやタブレットなどのタッチスクリーンデバイスでは問題が発生します。下のMezo Istvanのブログにも紹介されているように、タッチスクリーンデバイスで要素をタップした際、タップ後もホバースタイルが適用され続けてしまいます。

https://medium.com/@mezoistvan/finally-a-css-only-solution-to-hover-on-touchscreens-c498af39c31c

MDNでも同様の内容が記載されており、スマホやタブレットなどのタッチスクリーンデバイスで、ホバースタイルが当てられた要素をタップすると、フラッシュ(ちらつき)の原因になったります。

https://developer.mozilla.org/en-US/docs/Web/CSS/:hover

この問題を解決するのが、Media Queries Level 4 (25 December 2021)のワーキングドラフトで定義されている、Interactin Media Featureになります。

Interaction Media Featureには、3つのクエリがあります。

  1. Pointing Device Quality: the pointer feature(精度のクエリ)
  2. Hover Capability: the hover feature(プライマリーインプットのホバー機能のクエリ)
  3. All Available Interaction Capabilities: the any-pointer and any-hover features(すべてのポインティングデバイスとホバー機能に対するクエリ)

今回の:hover擬似クラスの問題を回避する最適なクエリは、Hover Capabilityになります。上記のMezo Istvanのブログにも紹介されていますが、Surfaceなどのタッチスクリーンを搭載したラップトップにおいて、タッチパッド/キーボードを取り外すとタブレットモードになり、メディアクエリはそれを正しく処理すると書かれています。

使い方

これまで説明してきたHover Capabilityを楽に導入する事ができるパッケージが、hover-media-queryになります。

https://github.com/scriptex/hover-media-query

ソースコードはとてもシンプルです。hover CSS Media Featureをサポートしていない、IE10/IE11/Firefox before 64も対象としています。

hover.scss
@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;
		}
	}
}

あとは、ホバーしたい要素に@includemixinを呼び出してhoverスタイルを適用します。下のコードはbuttonコンポーネントにhoverのメディアクエリを使用して、:hover擬似クラスを適用した例になります。(hover-medea-queryのコードは、abstractsディレクトリ内で管理しています)

input
@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);
  }

コンパイルした結果がこちらになります。

output
.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-querymixinを使用することで、記述量を減らすことができるので助かっています。

ターゲットデバイスに適用されるメディアクエリの確認

ターゲットのデバイスがどのメディアクエリを適用するのかをテストするサイトがあります。下の画像は、私が使用しているMac Miniのテスト結果になります。主要な入力メカニズムがポインティングデバイスであり、ホバー機能を持っている事がわかります。

media-query-tester

手元にある、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

参考記事

本記事を書くにあたり、以下の記事を参考にしました。

最後に

ユーザーに混乱を与えるインタラクションをしないよう、適切にhoverのメディアクエリを適用する必要があります。hover-media-queryを活用することにより、数行のコードで:hover擬似クラスの問題点を回避することができます。皆さんも試してみてください。

そして、hover-media-querymixinを用意してくださった、Atanasさんに感謝いたします。

GitHubで編集を提案

Discussion