CSSの継承〜親から子へ受け継がれる意志〜


アドベントカレンダー初挑戦！21日目（プラコレ的には10日目）
こんにちは！横田です。
不適切なところがありましたらご指摘いただけると幸いです。
よろしくお願いします！

# 今一度見直したい「継承」
フロントをやっていて、そこまで日は浅くないのですが、これまであまりcssプロパティの「継承」について深く考えたことがありませんでした。
親要素からプロパティを指定していって、子要素で効いていなかったらまた指定しよう、ぐらいでした。
しかしたびたび思うのです。「またcolor: #555;って書くのか...」と。
そこで、継承についておさらいしてみました。
Sassなどでスマートに書ける今、変数や関数をつかってかっこよく書けるようになりましょう！

# 継承とは
親要素のプロパティの値が、子要素に引き継がれることです。
プロパティによって継承されるものとされないものに分かれます。

# 継承されるプロパティ
使う機会が比較的多いプロパティをまとめました。

- border-collapse
- color
- cursor
- font
- letter-spacing
- line-height
- list-style
- quotes
- text-align
- text-shadow
- visibility
- word-spacing

# 継承されないプロパティ
継承されそうでされないプロパティもあります。

- background
- content
- margin
- table-layout
- text-decoration
- vertical-align
- width

ここで挙げたプロパティは一部です。
ざっと見ると、継承されるプロパティはテキストに関するものが多いです。
裏を返せばテキスト関連以外は要素ごとにちゃんと指定する必要がありますね！

# colorが継承されないのはなぜ？
しかし上記で挙げた継承されるプロパティでも、子要素に継承されないケースがあります。

```html:親要素のp要素と子要素のa要素
<p>あいうえお<a href="#">かきくけこ</a></p>
```
<dl>
  <dt>表示</dt>
  <dd> あいうえお<font color="blue">かきくけこ</font></dd>
</dl>
実際は「かきくけこ」にアンダーラインも引かれていて、よく見るテキストリンクの表示です。
a要素に指定していないプロパティが設定されている。そう、これはユーザーエージェントの仕業でした。

## デベロッパーツールのuser agent stylesheet
デベロッパーツールの「Elements」を見てみると、親要素のpよりも上にa要素に対して「user agent stylesheet」がきていて、プロパティの値が上書きされています。これはブラウザごとに定義されたデフォルトのCSS設定です。
先程の例は、ユーザーエージェントによってすでに子要素に指定されているため親要素の値は継承されない状態です。
継承は、優先度が低いことが分かります。
各ユーザーエージェントのプロパティと値については[こちら](https://coliss.com/articles/build-websites/operation/css/user-agent-stylesheets.html)。

# 親要素を継承したい
ユーザーエージェントなんていうクライアントの環境に左右されるのはこりごりだ！親からもらったこの形質を大事にしたいんだ！と思うこともあると思います。
その場合はデベロッパーツールにもよく出てくる「inherit」が活躍します。
他にもプロパティ値として度々目にするinitial、unsetについてもおさらいしましょう。

## inherit
親要素からプロパティの値を継承します。継承しないプロパティでも指定できます。
先程のa要素にも`color: inherit;`で親のp要素のcolorの値が有効になります！

注意するところは、ショートハンドプロパティ絡みの場合です。ショートハンドプロパティの中で、例えば`border: 2px dotted inhefit;`としても無効です（部分的に効くとかもないです）。

```html:html
<p>親要素<span>子要素</span></p>
```

```css:css
p {border: 1px solid blue;}
span {border: inhefit;} /* OK */
span {border: 2px dotted inhefit;} /* 無効 */

/* 継承したいプロパティを個別に書くとOK */
span {boder-color: inherit;}
```
また、`font`など継承するプロパティでも、子要素でショートハンドプロパティ内のプロパティの値を省略すると、初期値になってしまいます。

```css:css
p {
  font: normal bold 16px/1.2 Arial, sans-serif;
}
span {
  font: italic 16px/1.2 Arial, sans-serif;
  /* 省略したfont-weightは、boldが継承されず初期値のnormalになる */
}
span {
  font: italic inherit 16px/1.2 Arial, sans-serif;
  /* 無効 */
}

/* 継承したいプロパティを個別に書くとOK */
span {font-weight: inherit;}
```

## initial
要素にプロパティの初期値を指定します。
https://developer.mozilla.org/ja/docs/Web/CSS/initial

###主なプロパティの初期値（initialの値）

|プロパティ|初期値|
|:--|:--|
|color|ユーザーエージェントに依存|
|display|inline|
|font-size|medium（ユーザーの既定のフォントサイズ）|
|line-height|normal（ユーザーエージェントに依存）|
|vertical-align|baseline|

ここに挙げたものはブラウザ依存のプロパティばかりですが、初期値はユーザーエージェントの値とは別のものです。
例えば`margin`の初期値は0ですが、ユーザーエージェントの値は何かしらの値が設定されていたりします。

## unset
その要素の任意のプロパティをリセットします。
親から継承できるプロパティの場合は`inherit`、継承できないプロパティは`initial`の振る舞いになります。
https://developer.mozilla.org/ja/docs/Web/CSS/unset

## allプロパティを使ってまとめて指定
その要素のすべてのプロパティ（例外を除く）を指す`all`プロパティがあります。
指定できる値は`initial`、`inherit`、`unset`、`revert`の4つです。
`all: inherit;`とすれば親要素で指定したプロパティすべてを継承できるので、一部分だけ子要素独自のプロパティの値を指定したい時などは、その下に追記する、といった書き方ができます。
https://developer.mozilla.org/ja/docs/Web/CSS/inherit

# inheritの使いどころ
※あくまで個人的見解です！

以上をまとめると、cssの値の優先度（正しい言葉だと詳細度）では

**↑高い**

- IDセレクター
- classセレクター、擬似クラス
- 要素型セレクター、疑似要素
- ユーザーエージェント
- 継承

**↓低い**

となるので、cssの指定の流れは

1. クロスブラウザにするべくリセットcssを読み込む
1. 1.の設定を要素で指定して微調整する
1. class指定する

がシンプルかなと考えました。
1.は、user agent stylesheetの設定を上書きします。「リセットcss」などで検索すると、毎年ブラウザに対応したcssが出ているので、サイトデザインの仕様に適した、お手頃なものを探して入れます。
2.は、この要素の設定だけ直したい、など少し手を加える程度です。common.cssやbase.cssなどを作って書きます。
3.は、階層も深くせず、idは極力使わないようにします（詳細度の計算がややこしくなるため）
ここで、継承させたいプロパティに`inherit`！

まとめてみると思った以上に普通でした。
ユーザーエージェントとリセットcssにより、すでに要素に指定されている値があるため、自然の要素のプロパティ継承は、これまで通りあまり意識することはなさそう...です。
デベロッパーツールを見ながら賢くinheritしましょう！

# 継承よりもユーザーエージェントが気になってきた
これまで見て見ぬ振りをしてきたユーザーエージェントのスタイルシートをのぞいてみると、この書き方は何なんだ、というものがたくさんありました。
調べても不明なプロパティ値がありましたが、これ以上深入りは禁物なので、覚えておくと良さそうな範囲で調べたものを少しご紹介します。
###【セレクター】　a:-webkit-any-link
`:`から始まるので擬似クラス。そして`-webkit-`と続くのでChrome、Safariのベンダープレフィックス。
`:any-link`は :link または :visited に一致する要素（この場合はa要素）を指定します。

###【セレクター】　:matches(article, aside, nav, section)
`:matches()`は擬似クラス関数といい、`:is()`（新しい書き方）や`:any()`（接頭辞が必要）と同じ動作をします。
引数はセレクターとなる要素で、上記は
`article, aside, nav, section`と書くのと同じです。
`:matches(article, aside, nav, section) p:hover`
などと書くと便利さがわかります。

https://developer.mozilla.org/ja/docs/Web/CSS/:is

###【プロパティ】　margin-block-start
`margin-block-start`は新しい書き方！インライン軸とブロック軸を考えた時、これは`margin-top`に相当します。

★英語の場合
margin-block-start = margin-top
margin-block-end = margin-bottom
margin-inline-start = margin-left
margin-inline-end = margin-right

日本語の縦書きや多言語に考慮した考え方が主流になるときは近いのかもしれませんね。
くわしくは[こちら](https://coliss.com/articles/build-websites/operation/css/new-css-logical-properties.html)。

# まとめ
後半散らかってしまいましたが、いろいろな過程を経てその形質が表れていると思うと、感慨深いです。
プロパティの継承が親や祖先から子へ引き継ぐことならば、ユーザーエージェントとは国みたいなものだと思ってしまうのは私だけでしょうか。国（ブラウザ）ごとにルールがあり、差が生まれて...願わくば世界平和です。
未来の子どもたちにとって、来年も希望に満ちた平和な年でありますように！

明日は人物の描写力はピカイチ☆webデザインもさることながら漫画も大好評のあずあずこと田辺さんにバトンタッチです！
私のアイコンの似顔絵も描いていただきました☆
よろしくお願いしますー！

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

**・・・**

〜自由な結婚式をつくる人を増やす〜
プラコレではエンジニア、デザイナーも幅広く募集しています。
 [冒険法人プラコレ](https://pla-cole.co/)は、[プラコレWedding](https://pla-cole.wedding/)を中心に全国展開。
花嫁メディア[Dressy（ドレシー）](https://dressy.pla-cole.wedding/)や[farny（ファーニー）](https://www.farny.jp/)なども運営しています。
ぜひご覧いただけますと嬉しいです！

# 参考文献
CSS初学者の最初の壁「スタイルの継承」をしっかり理解する
https://www.webprofessional.jp/css-inheritance-introduction/

各ブラウザごとのデフォルトのスタイルシート、user agent stylesheetのまとめ -Chrome, Safari, Firefox, Edge
https://coliss.com/articles/build-websites/operation/css/user-agent-stylesheets.html

[CSS]知っておくと便利な論理プロパティ、ボックスモデルにおける古い方法とこれからの方法
https://coliss.com/articles/build-websites/operation/css/new-css-logical-properties.html