レスポンシブのときfont-sizeを可変対応させるCSS/SCSS

# レスポンシブサイトでするっと可変するfont-size
最近はハードが増えたので、メディアクエリで区切るとクライアントさんに怒られる。
うっかり1行でも増えようものなら、デザイナーさんが「デザインと違う」と嘆く。
特にタブレットカンプがないときの「いい感じになるよう頼みます」は厄介だ。いい感じとは？

CSSでcalcが使えるようになったおかげでvwの使い勝手は大変良いが、可変域が広いと面倒だ。
算数はできない。数学なんてもってのほかだ。
とはいえ人並みの国語力はある。字は読める。数字も多分読めているはずだ。
もっと直感的に処理したい。せめてSPとPCのカンプデータがあればあとは機械にお任せにしたい。

と思ってgoogle先生に聞いてみたら、頭の良い人親切な人がたくさんいた。すごい。


## 連立方程式と一時関数
下記のグラフがとてもわかりやすかった。

[CSSだけでレスポンシブ対応 font-sizeを画面に合わせて自動で変える](https://web-design-textbook.com/recipe/text-responsive.html)

参考サイトによると、PC時の値とSP時の値で連立方程式とつくって、xとyの値を一次関数の式にはめこめばよいとのこと。
なるほど...？SPのwidthとfont-sizeを「最小値」、PCのwidthとfont-sizeを「最大値」に設定して、両者をむすぶ直線上の値を求める。これをcalcに計算してもらえばいいわけだ。
計算式はこちらを参考にして、あとは計算できれば...！

# JavaScript
SCSSもなんとかしたいが、使わない案件もある。とりあえずJSで適当な計算機をつくってみることに。
SCSSでfunctionつくるとき役立つかもしれないし。（「プロパティ名が見やすい」というお馬鹿で単純な理由からfunctionが好きだ。この時はまだ穴にはまったことに気が付いていない。）

[デモ](https://sasadesignworks.com/calc-font-size/)

```JavaScript:Calc.js
// all
const values = document.querySelectorAll('input');
// sp
const spWidth = document.getElementById('js-spWidth');
const spFontSize = document.getElementById('js-spFontSize');
// pc
const pcWidth = document.getElementById('js-pcWidth');
const pcFontSize = document.getElementById('js-pcFontSize');
const sendBtn = document.getElementById('send');

// result
const viewElm = document.getElementById('result');

// 四捨五入用
function round(number, base) {
  const pow = Math.pow(10, base);
  return Math.round(number * pow) / pow;
}
sendBtn.onclick = () => {
  // リセット
  viewElm.textContent = '';

  // 空判定
  let nullValues = 0;
  for (let i = 0; i < values.length; i++) {
    const value = values[i].value;
    if (!value) {
      nullValues++;
    }
  }
  if (nullValues != 0) {
    window.alert('未入力項目があります。');
    return;
  }

  // 計算
  const spWidthNum = Number(spWidth.value);
  const spFontSizeNum = Number(spFontSize.value);
  const pcWidthNum = Number(pcWidth.value);
  const pcFontSizeNum = Number(pcFontSize.value);
  const x = pcFontSizeNum - spFontSizeNum;
  let y;
  let result;
  if ( x != 0 ) {
    y = (pcWidthNum - spWidthNum) / x;
    // 小数点以下4桁で四捨五入
    y = round(y, 4);
    result = 'font-size: calc(((100vw - ' + spWidthNum + 'px) /　' + y + ') +　' + spFontSizeNum + 'px);';
  } else {
    result = spFontSizeNum + 'px';
  }

  // 表示
  const p = document.createElement('p');
  const viewTxt= p.appendChild(document.createTextNode(result));
  viewElm.appendChild(viewTxt);
};
```

```HTML:sample.html
  <form action="post">
    <h2>SP</h2>
    <p>SP表示時の画面幅とfont-sizeを入力してください。</p>
    <p><label for="js-spWidth">SP:width</label>
    <input type="number" id="js-spWidth" name="spWidth" min="0" max="10000"> px</p>
    <p><label for="js-spFontSize">SP:font-size</label>
    <input type="number" id="js-spFontSize" name="spFontSize" min="0" max="1000"> px</p>
    <h2>PC</h2>
    <p>PC表示時の画面幅とfont-sizeを入力してください。</p>
    <p><label for="js-pcWidth">PC:width</label>
    <input type="number" id="js-pcWidth" name="pcWidth" min="0" max="10000"> px</p>
    <p><label for="js-pcFontSize">SP:font-size</label>
    <input type="number" id="js-pcFontSize" name="pcFontSize" min="0" max="1000"> px</p>
    <button type="button" id="send">送信</button>
  </form>
  <div class="section__result" id="result"></div>
```

## 四捨五入
あまり小数点以下が長いと疲れる。4桁でカットする。だいたいInt型しか使わないので四捨五入の方法に困った。
CSSコピペるとき0埋めがなんとなくいやという理由でtoFixed()を使っていない。
処理速度考えたら使った方がいいのかな？いやローカルでしか使わんしな。好きにする。

## 条件分岐
font-sizeが0のときは計算ができない。0の除算はできないわけで、JSではInfinityがでる。
そもそも意味がないのでif文で0以外のときに限定する。SPとPCのfont-sizeが同じ数値の時と同様elseにつっこむ。
SPとPCのfont-sizeが同じということはつまり可変じゃなくて固定値な訳だから、SPのvalueをそのまま出力する。

# SCSS
とりあえずなんとなくわかってきたような気がする。
SCSSで変数指定して使えるようにしよう。

しかしfunctionでエラーになる。なぜだ。基本的に単位が異なる数値は計算できないからだ...。完全に忘れている。
CSSで書けるということは出力できるはず。mixinにする。

px計算どうするかなとおもったけど、普通に0pxを加算した変数を定義するとなんとかなりそう。
初期値は全部数値だけ入れておいて、mixin内の変数定義でpxを追加する。

## mixin.scss

```function.scss
@function decimal($number) {
  $value: $number;
  @return round($number * 10000) / 10000;
}
```

```mixin.scss
@function decimal($number) {
  $value: $number;
  @return round($number * 10000) / 10000;
}
@mixin font-size($spFont, $spBase: 320, $pcFont: 16, $pcBase: 1400) {
  $value: $spFont;

  $spPx: $spFont + 0px;
  $spBasePx: $spBase + 0px;
  $fontSub: $pcFont - $spFont;
  $y: ($pcBase - $spBase) / $fontSub;
  $minWidth: $pcBase + 1px;
  @if ( $fontSub != 0 ) {
    font-size: calc(((100vw - #{$spBasePx}) / #{decimal($y)}) + #{$spPx} );
    @media screen and (min-width: #{$minWidth}) {
      font-size: #{$pcFont}px;
    }
  } @else {
    font-size: #{$value}px;
  }
}
```

PCのベースサイズを超えたらpx固定にするメディアクエリをつけておく。これつけておくと楽。PCベースサイズがmax-widthのときはこれでよかろ。場合によっちゃremでも。
小数点以下の処理はfunctionに丸投げ。
calc内の` #{$var}` はインターポレーションというらしい。関数も入れ込めるので重宝しそう。

[SCSSの変数と、CSS3のcalc()を併用するには](https://qiita.com/mtmtkzm/items/2e3aef1b504ebcde5311)
[インターポレーション, ShanaBrian](https://shanabrian.com/web/sass/interpolation.php)

## style.scss

```style.scss
html{
  // ルートを62.5％にしておくとremが使いやすいような気がする
  font-size: 62.5%;
}
body {
  // width320pxのとき14px, width1400pxのとき16pxを基準とする
  // width375pxのとき14px, width1000pxのとき18pxならfont-size(14,375,18,1000)と指定する
  @include font-size(14);
  }
}
```
responsive()はブレークポイント用のやつ。念のため。なくてもいい。

# PC時の最大幅以降 / 修正作業
SP幅はこれで問題なさそうなので、PCの基準値以降は別途設定する。
コンテンツの最大幅に合わせて62.5%で丸め込んでしまうことにする。
修正時はmixin名で検索すれば、大体なんとかなるかもしれない。

# 参考サイト
## 計算式関係

- [CSSだけでレスポンシブ対応 font-sizeを画面に合わせて自動で変える, WEB DESIGN TEXTBOOK](https://web-design-textbook.com/recipe/text-responsive.html)
- [あまり知られていないcalc()とvwを使ったお手軽レスポンシブ制御, Qiita](https://qiita.com/takamoso/items/c44956b7dfc9155cbd86)
- [【CSS】結局レスポンシブでのフォントサイズはどう書くべき？, ペコプラ](https://pecopla.net/web-column/fontsize-css)

## SCSS関係

- [SCSSの変数と、CSS3のcalc()を併用するには, Qiita](https://qiita.com/mtmtkzm/items/2e3aef1b504ebcde5311)
- [インターポレーション, ShanaBrian](https://shanabrian.com/web/sass/interpolation.php)
- [知っておくと便利！！SASSで用意されている８つの演算関数をまとめてみた, Webクリエイターのアトリエ](https://kanoto.info/201909/563/)

## CSS関係
- [calc(), MDN Web Docs](https://developer.mozilla.org/ja/docs/Web/CSS/calc())

## 計算機
- [一次関数の電卓, 算数の電卓](https://k3su.xyz/calc/linear-function.php)