scss・css探検隊#2 ~値指定編~

# 導入
cssやscss初心者あるあるなのが、不適切な値指定です。
全部絶対値のpxで指定したり、何でもかんでも相対値にしちゃって、結局どれくらいの大きさなのかコード見ただけじゃわからなくなってしまったり。

**css・scssって飾りだから、うまく表示できていればそれでいい**という方が多くいるかと存じますが、現代のweb制作においては、レスポンシブデザイン(画面サイズが変わってもデザインが崩れない)ものが主流となっており、これに準拠した値指定を行わなければ食っていけません。

ということで、復習もかねて値指定についてまとめなおすことにしました。

# 絶対値指定と相対値指定
値指定の種類は大まかに2種類、**絶対値指定と相対値指定**
これらは従来は使い分けといった感じでしたが、最近のトレンド的にはほぼ相対値で書くのが主流になりつつあります。

## 絶対値指定
```css
px, pt
```
これだけです。

:::note warn
ほかにもcmとか実世界単位もありますが、誰も使っている人がいないのでやめておいた方がいいでしょう。
:::
なんだったら、```px```しか使いません。

この絶対値指定は、何があっても変わらない大きさの指定です。
ウィンドウサイズを変えようと、デバイスを変えようと同一の大きさで表示されます。
初めの1歩としては、直感的に大きさを指定できるこの絶対値指定は使いやすいですが、やはりレスポンシブデザインを考える上では邪魔になってきます。
例えば、ウィンドウサイズがフォントサイズを下回った場合、ウィンドウをはみ出したりします。

## 相対値指定
相対値指定とは、他の何かしらの絶対値を持つ要素に対して、依存する形で定義されます。
例えばウィンドウサイズの〇%だとか、親の△%だとか、そういった感じです。
```css
%,em,rem,vw,vh,vmin/vmax,ch
```
とかなりの種類があります。
これらについて1つずつ解説していきます。

<br>

### フォントサイズ基準の単位
#### em
**現在の要素の親要素(1つ上の要素についている大きさ指定)を基準としてどれくらいか**で指定します。
入れ子構造になっていた場合、〇emとすると、一つ上の要素の〇倍の大きさになります。
<br>
使用例
```css
.parent {
    font-size: 16px;
}
.child {
    font-size: 1.5em;    /* 24px (16px × 1.5) */
    padding: 1em;        /* 24px (自身のfont-sizeを基準) */
    margin: 1em;         /* 24px */
}
.grandchild {
    font-size: 1.5em;    /* 36px (24px × 1.5) */
}
```
このようになります。
よくemは**文字サイズの〇倍で指定する単位**と言われがちですが(部分的にはそうなんですけど)、それだけではないです。
サイズ指定は次のような流れで決まります。
1. ``` font-size```で指定された場合、親要素を参照して〇倍した値を設定する。この際、親要素がroot(ない)または親要素で```font-size```が指定されていなかった場合、デフォルトの16pxが設定されます。
2. ```font-size```以外(```padding```とか)で指定された場合は、同要素内の```
font-size```から〇倍した値を設定します。

というように、結構複雑な手順で決まるんですよね...

:::note warn
フォントに関しては親要素にサイズが指定されていない場合、16pxがデフォルトの値として入ります。つまり、〇emと指定すると、16×〇pxとしてサイズ指定されます。
```css
.content {
  padding: 2em; /* 16×2=32px */
}
```
:::


これらの手順で決定されることを踏まえて、私は```font-size```だけは絶対値指定のpxで指定する派です。
最近だと```font-size```も相対値にするべき、という考えを持つ方が増えつつありますが、個人的には後からサイズを追っていくうえで複雑なプロジェクトになるとネストをいくつも追っていくことになり、これが非常にストレスに感じます。
なのでscssだと、```font-size```のレスポンシブは

```scss
   // ブレイクポイントの定義
$breakpoints: (
    'xs': 375px,
    'sm': 576px,
    'md': 768px,
    'lg': 992px,
    'xl': 1200px
);

// フォントサイズの定義
$font-sizes: (
    'heading': (
        'min': 24,
        'max': 48
    ),
    'body': (
        'min': 16,
        'max': 20
    ),
    'small': (
        'min': 12,
        'max': 14
    )
);

@function get-fluid-value($min-size, $max-size, $min-vw: map-get($breakpoints, 'xs'), $max-vw: map-get($breakpoints, 'xl')) {
    @return clamp(
        #{$min-size}px,
        calc(#{$min-size}px + (#{$max-size} - #{$min-size}) * ((100vw - #{$min-vw}) / (#{$max-vw} - #{$min-vw}))),
        #{$max-size}px
    );
}

@mixin fluid-type($type) {
    $config: map-get($font-sizes, $type);
    font-size: get-fluid-value(
        map-get($config, 'min'),
        map-get($config, 'max')
    );
}

// 使用例
h1 {
    @include fluid-type('heading');
}

p {
    @include fluid-type('body');
}

.small-text {
    @include fluid-type('small');
}
```
のようにしてブレイクポイントである程度決めちゃって、これをウィンドウサイズにごとに分けて使い分けしていくのが好みです。
こうすれば、同要素内でサイズのある程度の見積もりは立ちますし、親のフォントサイズをいじったばっかりに小や孫にまで影響が出て微調整する羽目になる、ということもなくなります。

なので、よほど小さいプロダクトでもない限り```font-size```は絶対値指定の方がいいなぁと思っています。
<br>

#### rem
みなさんそもそも```rem```と```em```の違いって説明できますか？

私は「なんか毎回同じになるし、相対値だからremの方が使いやすいなぁ」くらいの認識でした。特にボタンの中身の文字や、フレーム内の要素などの子要素で重宝していましたが、今思うと普通に頭悪いです。

正しい```rem```の表現は
#### 親要素を基準にするのではなく、HTMLタグに指定されたサイズが基準の単位
です
<br>
つまり、**htmlタグに何もサイズ指定されてなければ、毎回同じサイズ(emと同じくデフォルトの16px)なのです。**

実は```rem```は ```root em```略で、root(htmlタグ)に対する```em```ということだったんです。

私の思っていた```rem```の「毎回同じサイズになる」というのは実際間違ってなくて、いつもはhtmlタグにサイズの指定なんてしないので、それは同じサイズになるよね、ということなんですね

そう考えると、手あたり次第```rem```にしてたのは、割と正解だったのかもしれません。ただ、これを知らずに使うのと知って使うのとは雲泥の差で、もし社会人になってからhtmlタグなんて一瞥もせずに乱用しまくってたら、原因不明のサイズミスが量産されてしまいます。
まぁ細かいことといえば細かいことなんですけど、よく理解しないまま使うって、怖いんだなぁ...
<br>
また、小ネタではありますがブラウザの文字サイズの変更にも対応しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786217/936e869f-1421-a373-bcd0-971241859d29.png)
GoogleChromeでいうココです。

```px```だとこれに対応していないので文字サイズは固定になるのですが、```rem```だと勝手に変更してくれます。
開発者はそんなこと考えてないとは思いますが、思わぬところでユーザビリティの欠落したプロダクトを作ることになってしまうよりかは、知っておいた方が得でしょう。
<br>
#### ch
使ったことがある人はなかなかいないと思いますが、結構便利な単位です。
この単位は、**フォントの「0」文字の幅を基準とする単位**です。
例えば、```width```に```10ch```と指定するとその要素で指定しているフォントの「0」の10文字分の横幅をサイズとします。
...キモイ単位ですよね
文字帯とかに使えるんじゃないかなと思います
<br>

### ビューポート基準の単位
そもそもビューポートってなんぞやといいますと、簡単に言うと```window-size```ですね。画面サイズをビューポートって言います。

これらを参照して、相対的な大きさを決めるのが```vw, vh, vmin, vmax```です。

#### vw,vh
```vw,vh```それぞれ```viewport width, viewport hight```の略で、ビューポート(ウィンドウサイズ)に対して、100分率でどれくらいかを示します。
```vw```ならwidthなのでウィンドウの幅、```vh```ならウィンドウの高さを参照し、例えば```width:1000px```のウィンドウに対して、子要素に```50vw```を指定すると500pxになります。
50vh ならウィンドウサイズの50%、みたいなノリです。

:::note info
あくまで値の参照なので、```height```に```vh```じゃなくて```vw```を指定しても動きはします
:::
<br>
相対値の指定を100分率でやるなら、%でいいじゃん！と思われる方もいるかもしれませんが、少しばかり違いがあります。(というか根本から違います)

%は **親要素の〇%** です。対してvw・vhとかは、**親要素関係なくwindowサイズの〇%** です。
親要素とルート(windowサイズ)という感覚的には、文字要素におけるemとremに似ているかもしれません。

#### vmax vmin
使いそうで使わなそうで割と使う、そんな単位だと思います。
これら2つは、先ほどのvh,vwの亜種みたいなもので、これら2つの両方を参照します。
そして、```vmax```ならそのうちの __大きい方__、```vmin```なら __小さいほう__ の値の〇%を指定します。

例えば、
```html 
<div class="responsive-square">
  動的に変化する正方形
</div>

<div class="responsive-text">
  画面サイズが変わっても読みやすいテキスト
</div>

<style>
/* 正方形を作る例（vmin）
   画面が縦長でも横長でも、小さい方に合わせて正方形を維持 */
.responsive-square {
  width: 50vmin;
  height: 50vmin;
  background: #3498db;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 20px;
}

/* フォントサイズの例（vmax）
   画面の大きい方の寸法に合わせてテキストサイズを調整 */
.responsive-text {
  font-size: 5vmax;
  color: #2c3e50;
  margin: 20px;
}
</style>
```
といった感じで、常に正方形を維持したい時などに使ったりします。

### 親要素基準の単位
#### %
cssを触ったことがある人は誰しもが触ったことがある単位だと思います。
この単位は皆さんご存じの通り、**親要素の〇%** を値として指定します。ただ、脳死で使っている一方で、よくよく考えてみれば親要素の何を参照しているのかわかりにくい単位でもあります。
例えば```<button>```の子要素に```<button_text>```があるとしたときに、```.button_text```の```width```に30%と指定したとき、何の30%なのでしょうか。
この場合は親要素の```width```を参照してるのでは...?と感覚的にはわかりますが、それなら```padding```ならどうでしょう？
実際にやってみると思ったようにいかないはずです。
これは%が完全に動的な参照先決定をしているわけではないからなんです。

実は次のような規則で%の参照先は決められています。
```
width, margin（左右）, padding（左右）
親要素の width が基準

height, margin（上下）, padding（上下）
親要素の height が基準
ただし、親要素の高さが指定されていない場合は機能しない

transform: translate()
translateX: 要素自身の width が基準
translateY: 要素自身の height が基準

font-size
親要素の font-size が基準
```

といったようになっています。
これは知らないとひっかかりますね。

なので、よほど末の要素出ない限りはできれば%は安易に使うのは避けた方がよいと考えています。前述でもいろいろ述べた通り、デザイン変更の際、単位を追っていったときに追いにくいと非常にストレスです。emなりvh,vwなりに置き換えるのが無難かなと思います。


### 最後に
個人的な結論としては、基本は相対値で指定するのがいいんじゃないかなと思います。その中でもem・remがおおすすめですね。
場合によってはvh,vwも全然ありだと思いますが、プロジェクト内でできるだけ1つに統一しつつ、場合によって単位を使い分けるべきではあります。