ホバーの分岐にはメディアクエリのwidthではなくhoverを使おう


メディアクエリにはhoverプロパティがあり、ユーザーがホバーに対応しているデバイスかどうかの判定が行えます。

https://developer.mozilla.org/ja/docs/Web/CSS/@media/hover

これを使用することによって、画面幅での分岐が必要なくなるためタブレットのlandscape含めた対応も容易になります。

また、メディアクエリはJavaScriptからもmatchMediaという関数を使う事で使用することが可能なのでCSSとJavaScriptでの利用例を載せていきたいと思います。

https://developer.mozilla.org/ja/docs/Web/API/Window/matchMedia

CSS

CSSでの利用は簡単ですね

.link {
  background: rgba(0, 0, 0, 1);
}

@media (hover: hover) {
  .link:hover {
    background: rgba(0, 0, 0, 0.7);
  }
}

下記のようにホバーができる端末のみ、ホバー前のスタイルを用意したい場合にも分岐が簡単になります。

.link {
  background: rgba(0, 0, 0, 1);
}

@media (hover: hover) {
  .link {
    transform: scale(1.1);
  }
  .link:hover {
    background: rgba(0, 0, 0, 0.7);
    transform: scale(1);
  }
}

SCSSなどを利用している場合はこのままmixinにして使いまわしてもいいと思います。

JavaScript

JavaScriptの利用方法は下記のようにmatchMediaを利用するだけになります。

const hasHover = window.matchMedia("(hover: hover)").matches;

if(hasHover) {
  /* ホバーできる場合の処理 */
}

例えばmouseenterイベントでgsapなどでアニメーションをつけたい場合の分岐も容易になりますね。

ReactでFramerMotionTailwindなどクラス名で分岐したい場合は下記のようにしてもいいと思います。

/* FramerMotionのアニメーション用にstateを変更する場合 */

const Component = () => {
  const hasHover = window.matchMedia("(hover: hover)").matches;
  const [isHover, setHover] = useState(false)

  const onMouseEnter = useCallback(() => {
    if(hasHover) {
      setHover(true)
    }
  }, [])

  const onMouseLeave = useCallback(() => {
    if(hasHover) {
      setHover(false))
    }
  }, [])

  return (
    <a
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
    >
      {/* isHoverでアニメーションの出しわけ */}
      <motion.div />
    </a>
  )
}
/* Tailwindなどでクラス名を分岐させる場合 */

const Component = () => {
  const hasHover = window.matchMedia("(hover: hover)").matches;

  return (
    <a
      className={clsx(
        'text-white',
        hasHover && 'hover:opacity-75'
      )}
    >
      ...
    </a>
  )
}

Reactの場合にはメディアクエリを利用できるライブラリもあったりするのでそちらを利用してみてもいいかもしれません。

https://www.npmjs.com/package/@react-hook/media-query

最後に

直近の仕事でも利用してみましたが、画面幅でホバーを管理していた頃よりコードも書きやすく見通しも良くなった印象でした。

またReactの場合はonMouseEnterのような関数までを返すカスタムhooksを作って運用していましたがそれもおすすめです。

最後まで読んでいただきありがとうございました。