スマホのhoverの動きをSassで矯正させるmixin

最近、[スマホで触れている間だけhoverする](https://qiita.com/RinoTsuka/items/8992cc127d42af13cc4f)がチョコチョコいいねをして頂いて嬉しいが、中途半端なコードなので申し訳ないやら。
と、いうことでちゃんとしたのを書いてみました。

まとめたSCSSを[Github](https://github.com/RinoTsuka/smartphone-hover)に置きます。

動作サンプルはめんどくさいので用意していません。あしからず。
古いバージョンだと`&`の扱い方が違うので正しく吐き出されないかもしれません。
Sass 4.0.0以降なら動作確認済みです。

# Scss こんな書き方でスマホに対応できます

```scss:sample.scss
a {
  color: gray;
  @include hover {
    color: red;
  }
  @include active {
    color: green;
  }
}
```


# 準備

まずはじめに[userAgentでブラウザ＆デバイス判別 2017年版 - Qiita](https://qiita.com/gurigurico/items/bd19ed121bfdf77fced6)の[Github](https://github.com/mtskf/UserAgentChecker)からユーザーエージェント判定用のスクリプトを拝借します。

```html:index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Title</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div ontouchstart="">

</div>
<script src="/js/userAgentChecker.min.js"></script>
</body>
</html>
```

`userAgentChecker.min.js`を読み込みます。
`<div>`をラッパーにして(全てを`<div>`で囲む)`ontouchstart=""`を記述します。
※ 何が条件なのか分からないが、`<body ontouchstart="">`では動かないことがあったので`<div ontouchstart="">`としたら動いたのでラッパーに与えた。
参考：[タッチデバイスでCSSの:activeや:hoverを機能させる。 - Qiita](https://qiita.com/junya/items/3ff380878f26ca447f85)

`userAgentChecker.min.js`を読み込んだ時点で`<html>`に`class=""`が付与されています。ここでタッチデバイス`.touch`か、マウス操作`.mouse`か分かる様になるので、これをCSSで利用します。

# SCSS mixin


```scss:mixin/_hover.scss
@mixin hover ($touch: false, $highlight: false, $userSelect: false, $focus: true) {
  // mouse
  @at-root .mouse &:hover {
    @content;
  }
  @if $focus == true {
    @at-root .mouse &:focus {
      @content;
    }
  }
  // touch
  @if $touch == true {
    @at-root .touch &:active {
      @content;
    }
    // $highlight
    @if $highlight == false {
      -webkit-tap-highlight-color: rgba(#000, 0);
      appearance: none;
    }
    // $userSelect
    @if $userSelect == false {
      @at-root .touch & {
        user-select: none;
        input, select {
          user-select: auto;
        }
      }
    }
  } @else if $touch == false {
  } @else {
    @warn 'mixin hover $touchの値が正しくありません';
  }
}
```

```scss:mixin/_active.scss
@mixin active ($touch: true, $highlight: false, $userSelect: false) {
  $e: &;

  // mouse
  @at-root .mouse #{$e}:active {
    @content;
  }
  // touch
  @if $touch == true {
    @at-root .touch #{$e}:active {
      @content;
    }
    // $highlight
    @if $highlight == false {
      -webkit-tap-highlight-color: rgba(#000, 0);
      appearance: none;
    }
    // $userSelect
    @if $userSelect == false {
      @at-root .touch #{$e} {
        user-select: none;
        input, select {
          user-select: auto;
        }
      }
    }
  } @else if $touch == false {
  } @else {
    @warn 'mixin active $touchの値が正しくありません';
  }
}

```

```scss:mixin/_focus.scss
@mixin focus () {
  @at-root .mouse &:focus {
    @content;
  }
  @at-root .touch &:focus {
    @content;
  }
}
```


mixinを書きました。
`:hover`、`:active`を書くような感じで利用したかったので使い方は事項のようになります。

`:focus`を利用したいとき、`:focus {}`と書くとmixinで吐き出す`.mouse a:hover`に優先度で負けてしまうので、頭に`.mouse`、`.touch`をつけるだけの`focus`用のmixinを作成しました。

## mixinを利用

なにも考えずにmixinを利用すると以下のようになる。

```scss:style.scss
a {
  color: gray;
  @include hover {
    color: red;
  }
  @include active {
    color: green;
  }
}

```

```css:コンパイル結果
a {
  color: gray;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
}
.mouse a:hover {
  color: red;
}
.mouse a:focus {
  color: red;
}
.mouse a:active {
  color: green;
}
.touch a:active {
  color: green;
}
.touch a {
  -webkit-user-select: none;
     -moz-user-select: none;
      -ms-user-select: none;
          user-select: none;
}
.touch a input, .touch a select {
  -webkit-user-select: auto;
     -moz-user-select: auto;
      -ms-user-select: auto;
          user-select: auto;
}

```

autoprefixerを利用しているのでベンダープレフィックスがついています。
デフォルトでは`user-select: none`、`-webkit-tap-highlight-color`が透明、`appearance: none`が付与されて出力されます。
上記CSSのon、offに加え、`.touch`のCSSが`hover`と一緒なのか`active`と一緒なのかを引数で選択できるようにしています。

||touch|user-select|-webkit-tap-highlight-color |appearance|focus|
|---|---|---|---|---|---|
|hover|true/**false**|true/**false**|true/**false**|true/**false**|**true**/false|
|active|**true**/false|true/**false**|true/**false**|true/**false**|-|
|focus|-|-|-|-|-|

※ 太字がデフォルト値
※ `user-select`, `-webkit-tap-highlight-color`, `appearance`は`touch`が`true`のとき利用できる。


## 引数を利用したサンプル

```scss:style.scss
a {
  color: gray;
  @include hover (true, false, true) {
    color: red;
  }
  @include active (false) {
    color: green;
  }
  @include focus () {
  }
}

```

```css:コンパイル結果
a {
  color: gray;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
}
.mouse a:hover {
  color: red;
}
.mouse a:focus {
  color: red;
}
.touch a:active {
  color: red;
}
.mouse a:active {
  color: green;
}

```

こんな感じで簡単にスマホがタッチされたときの挙動を`hover`ではなく、感覚的なものに変更できたかと思います。


### 余談

iphoneで`color`の指定がないと、`color`が背景色になるバグ発見！

```css
a:hover {
  background-color: red;
}
```