💅

CSSを書くときに気をつけたい基本的なtips

2020/12/24に公開

CSSをレビューしていたりすると、お?これは、、、みたいなことがちょいちょいあります👀
例えばfigma等からコピペしてもってきたコードなんかもあると思いますが、よく見る気をつけたほうが良さそうなtipsをいかに列挙していきたいと思います💪

■ line-height

line-heightというのはなかなかの曲者です。こいつのせいで意図しない動きをしたりする場面があります👀

値に単位を含めるな問題

MDNを見ても書かれていますが、line-heightの値においては単位をつけないことが推奨されています👀

.hoge {
  line-height: 1.5; /* OK */
  line-height: 150%; /* NG */
}

理由としては、line-heightに単位をつけると、px等の場合はその値に、%等の場合はそのコンテキストの一意の相対値に決まってしまうからです。
単位を付与しないことで、例えば文中に16px24pxの指定が混在していたとしてもよしなにline-heightを算出してくれるようになります🙆🏻♂️ ‍

https://developer.mozilla.org/ja/docs/Web/CSS/line-height

imgとかに変な隙間生まれるなー問題

line-heightが全体にあたっているときなどに、inline-block要素にも影響が出ます。よくあるのはimgの下部に変な隙間が出る問題です。
imgdisplay: blockを当てると解決します。

全体にline-height: 1;を当てちゃう

僕はよくこれをやっています。

body {
  line-height: 1;
}

なんとなく今まで制作をしてきて、line-heightが全体にあたっていてメリットを感じたことがないからです。
なにか隙間が出たりとか、そもそも、各コンポーネントでline-heightが異なってしまうとか、、、
であれば、必要なところで個別で当ててやったほうが早いよね、みたいな動機です🚂

■ width

widthなんにでも書いちゃう問題

これはちょいちょい見ることもあるかと思うんですが、何にでもwidth: 100%があたっている場合があります👀
実際、それで問題になることはそこまで多くないんですが、display: block;の場合すでにwidth: 100%;です。
display: flex;等の場合もそうです。

逆に指定したほうがいい場合もあります。
思いつく限り以下に列挙してみました

  • 要素の幅が決まっている場合
  • display: tableの場合
    • 内部のコンテンツによってしまうので、もし100%にするならwidthを指定する必要があります
  • display: table-cell;の場合
    • どのくらいの割合なのかを%で指定するなどしてあげたほうが崩れなくてすみます
  • flexの子要素の場合
    • 中のサイズに依ってしまうので、指定してあげる必要がある場合の方が多いイメージです
  • positionabsoluteまたはfixedの場合
    • 中のサイズに依ってしまうので、必要であれば指定しましょう

■ display

display: blockなんにでも書いちゃう問題

いろんなものにdisplay: block;が与えられているのを見たことがあるかもしれません。
多くの場合ブロックレベル要素はdisplay: block;が初期値です(tableなんかは違いますが)。
なので、再度当てる必要はありません。
https://developer.mozilla.org/ja/docs/Web/HTML/Block-level_elements

また、position: absoluteまたはposition: fixedが指定された要素は、display: blockになります。
これは出典が見当たらなかったですが、例えばchromeでspanposition: absoluteをかけてデベロッパーツールでcomputed欄を確認すると、displayblockになることが確認できると思います。
なので、これらのpositionが指定されている場合には、別途display: blockを指定する必要はありません。

■ z-index

なんか当たらない問題

z-indexで大きい値を当てているにも関わらず何故か思ったように動かない🤔
みたいなことは多くの人が経験があるかと思います🍙
結果、よくわからないけど、大きい数字を当てたりすることになるのですが、z-indexの仕組みを知るとおそらくそこまで大きな数字が必要でないことがわかると思います🙆🏻♂️ ‍

■ 重ね合わせコンテキストを理解する

z-indexはそもそも重ね合わせコンテキストがない状態では効きません。

https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context

以上のリンクを読むとわかると思いますが、例えばpositionが初期値staticの状態ではz-indexをいくら大きな値にしたとしても効くことはありません。
z-indexが効くようにするには重ね合わせコンテキストが生成されるスタイルがあたっている必要があります(例えばposition: relativeなんかがお手軽です)。

そして、z-indexの比較はコンテキスト内での比較となります。

<style>
  div {
    position: relative;  
  }    
</style>

<div id="hoge" style="z-index: 1">
    <div id="hoge-child" style="z-index: 100"></div>
</div>

<div id="fuga" style="z-index: 2">
    <div id="fuga-child" style="z-index: 50"></div>
</div>

これだと、#fuga > hogeなので、その子要素のz-indexがどうであろうと、#fuga-child > #hoge-childになります。
なお、デフォルトではあとに記述された要素のほうが、重ね順は上になります。
また、何も設定しないときの値は0で負の数を指定することで重ね順をマイナスにすることも可能です。

https://developer.mozilla.org/ja/docs/Web/CSS/z-index

■ 最初以外、などのいい感じの指定の仕方

最初だけ、最後だけはmarginを当てたくない、みたいな需要はよくあると思います🍣
そういうわけでそういうときによく使うやつを書いておきます。

li + li

<style>
  li + li {
    margin-top: 8px;
  }    
</style>

<ul>
    <li>当たらない</li>
    <li>hoge</li>
    <li>hoge</li>
    <li>hoge</li>
</ul>

例えばliを複数持つulがあったとした場合、これは最初のli以外にスタイルが当たることになります。
+は隣接兄弟結合子というらしいですが、hoge + fugaだと、前にhogeがあるfugaという指定になります。
この例で行くと、最初のliは前にliがないためこの指定からは外れ、最初以外となるわけです。

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

:not(:first-child), :not(:last-child)

:not()は直感的な名前でいいですね。
よく使うのは :not(:first-child):not(:last-child)あたりです。
今はgridがあるので、そういう機会はないかもしれませんが、例えば、display: flex;で3こずつで折り返す要素を作ったときに、

  • 最後の3個以外はmargin-bottomを当てたい
  • 3n個目の要素以外にはmargin-rightを当てたい

みたいなことがあったとします。その場合は、

<style>
  .list {
    list-style: none;
    display: flex;
    flex-flow: row wrap;
  }
  
  .list > li {
    width: 30%;
    height: 100px;
    background-color: red;
  }
  
  .list > li:not(:nth-child(3n)) {
    margin-right: 5%;
  }
  
  .list > li:not(:nth-last-child(-n + 3)) {
    margin-bottom: 16px;
  }
</style>
<ul class="list">
  <li>テスト</li>
  <li>テスト</li>
  <li>margin-right当たらない</li>
  <li>テスト</li>
  <li>テスト</li>
  <li>margin-right当たらない</li>
  <li>marign-bottom当たらない</li>
  <li>marign-bottom当たらない</li>
  <li>margin-right, marign-bottom当たらない</li>
</ul>

のようにすることができます🙆🏻♂️ ‍

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

■ > でコンテキストを絞る

例えばul.list > liみたいなDOMがあったとき、liにいちいちクラスをつけるのがめんどくさい、みたいなことはよくある話です💀
そんなとき、愚直に

.list li {
  padding: 8px;
}

みたいにすると、例えば以下のようなときに問題になります。

<ul class="list">
  <li></li>
  <li>
    <ul>
      <li></li>
    </ul>
  </li>
</ul>

まぁ当たり前な話ではあるんですが、web制作の文脈ではなく、アプリケーションのコンポーネントを考えたときに、このlichildrenを受け取る要な場合、childrenは何が入ってくるか知ることができません。
そういうわけで>をつけることで直接の子のみに絞ってしまいましょう。

.list > li {
  padding: 8px;
}

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

■ クラスつけるのだるいよね、みたいなとき

例えば、ある程度深いところの要素にスタイルをつけるとき、クラス名をつけるのがだるい、みたいなこともよくあります。
例えばBEMなんかだと、.block__element__elementくらいまで来て、あー次のelementを書くのはちょっとやだなーみたいなことがあります。

そういうときは子結合子で要素を取得してしまうのもありだと思いますが、あるいは僕は_付きのクラス名を付与して使うみたいなことをやっています。

<div class="profile">
  <div class="profile__content">
    <div class="profile__content_detail">
      <div class="_age">60歳</div>
      <p class="_text">HELLO</p>
    </div>
  </div>
</div>

このとき、_付きのclassを使う場合は以下のようなルールを設けています

  • 必ず、親要素の子孫(または子)結合子とともに使用される
  • 一階層以上のネストを持たない
  • 必ず子を把握できる(childrenなどを入れない)

これは単純にルールの問題なので、単純にこんな風にしているという紹介でした🙏

■ メディアクエリでいい感じの書き方

スマホとPCでそれぞれ違う値を設定するみたいなことがあると思います。
SCSS、またはemotion等を使っている場合、そんなときにこのような書き方をすると見通しが良くなる場合があります。
例はemotionです。

const Hoge = styled.div`
  background-color: red;

  @media (max-width: 767px) {
     padding: 8px 16px;
  }
  @media (min-width: 768px) {
     padding: 16px 24px;
  }
`;

max-width: 767pxはスマホ用、min-width: 768pxはPC用です。
本当は変数にこのあたりを入れてわかりやすくしてやるとより良いと思います。
このようにして書くことで、各要素のスマホとのときの値とPCのときの値がすぐに分かるようになります。

また、この書き方のポイントは共通のものと各メディアクエリのものを分けて書くことです。
メディアクエリ間で打ち消しを行わないように書くことで、各デバイスごとのスタイルを変更する場合への心理的な障壁が下がります。

// 良くない例
// これくらいの量だと把握は可能だが、数が増えてきた場合、
// どのプロパティが関連しているかわからなくなってくるので明示的に分けてあげたほうが理解しやすい

const Hoge = styled.div`
  background-color: red;
  padding: 16px 24px;
  
  @media (max-width: 767px) {
     padding: 8px 16px;
  }
`;

ただ、ヘッダーのグローバルナビなどでそもそもPCとSPで全くデザインが違うような場合があります。
こういう場合はまるっきり分けて書いてしまったほうが見通しが良い場合もあります。

const spNavStyle = css`
  @media (max-width: 767px) {
     & > .nav {
       padding: 8px 16px;
       ...
     }
  }
`;

const pcNavStyle = css`
  @media (min-width: 768px) {
     & > ..nav {
       padding: 16px 24px;
       ...
     }
  }
`;
SCSSの例

SCSSだと、こんなmixinを作ると

// media queries
@mixin max-screen($break-point) {
  @media (max-width: $break-point) {
    @content;
  }
}

@mixin min-screen($break-point) {
  @media (min-width: $break-point) {
    @content;
  }
}

@mixin screen($break-point-min, $break-point-max) {
  @media (min-width: $break-point-min) and (max-width: $break-point-max) {
    @content;
  }
}

このように使えます

.hoge {
  @include max-screen(767px) {
    padding: 8px 16px;
  }
  @include min-screen(768px) {
    padding: 16px 24px;
  }
}

そういうわけで

基本的なtipsが多かったですが、CSSは破綻しやすいものでもあるので、少しずつ気をつけられたらと思います🧠
また思いついたら追記します🛹

Discussion