🍳

CSS は「ええ加減」に書けばええんですわ

2021/06/15に公開

こんにちは。ひらやま(@rhirayamaaan)です。

先日「料理と利他」という書籍を読ませていただきまして、土井善晴先生の料理の考え方にふれました。
「ええ加減にしなさい」と言われれば「やったらだめだ」という印象を受けますし、「ええ加減にやってるなぁ…」と言われれば「雑に」とか「手を抜く」というような印象があったりと、あまりポジティブな印象は受けません。
しかし、土井先生はそういうことではないとおっしゃっています。

その「ええ加減」というのが、自分で考えろということで。やったらあかんということじゃない。[中略] 今は「ええ加減」が悪い事のように言われていますが、それは自分で判断することだと思います。

ええ加減というのは、たとえば豚の生姜焼きでも、ちょっとした味をつけた豚肉にパッパと粉をまぶして、ついてないやないかというところがあってもええんですよ。でも、粉がついているところとついていないところでバランスがとれる。

引用:料理と利他 P84, P136

「ええ加減でええ」というのは、手数を減らして作っていく中で、料理をよりおいしいものへと導きつつ導かれていく過程を受け入れる姿勢のように思えました。
作っていくものと対話をしながら共創していくための第一歩は、手数を減らしていくことなのかもしれません。

そんな中、フロントエンドエンジニアとして働いている私ですが、CSS という言語は「ええ加減」の姿勢をかなり取りづらい言語なのではないかと感じました。
やりたいことを実現するためにどんどんと書き加えていってしまい、何が起こっているのかすらわかりづらくなってしまうのではないでしょうか。
そこで、どのようにしたら「ええ加減」に CSS を書けるのか、事例を踏まえつつ考えてみました。

カードを横並びにしたい

記事リストや商品リストなど、カードUIを以下のように横並びにしたいケースは多々あります。

これを実現するためにもちろん display: flex; を使うのですが、ちょっと工夫して使うと余白をきれいに整えることができます。

<div class="cardList">
  <ul class="cardList__items">
    <li class="cardList__item">
      <a href="#" class="cardList__itemLink">
        <div class="card">
          <div class="card__image">
            <img
              src="https://dummyimage.com/300x300"
              class="card__imageContent"
            />
          </div>
          <div class="card__description">
            <p class="card__title">Card 1</p>
          </div>
        </div>
      </a>
    </li>
    <li class="cardList__item">...</li>
    ...
  </ul>
</div>
.cardList {
  .cardList__items {
    display: flex;
    flex-wrap: wrap;
    margin: -16px 0 0 -16px;
  }

  .cardList__item {
    width: 50%;
  }

  .cardList__itemLink {
    display: block;
    margin: 16px 0 0 16px;
  }
}

※カード部分の UI は .card という block として切り分けてスタイリングしていますが、上記では載せていません。

ポイントは、.cardList__itemwidth: 50% という記述しかなく、要素間の余白は .cardList__itemLink に委ねられている点です。
こうすることで、.cardList__item は「カラム数をいくつにするか」という責務だけを与えられることになります。width: 50%; なら2カラム、width: 33.33%; なら3カラムとなり、コードとしてもリーダブルになります。
もし仮に.cardList__itemmargin をつけてしまっている場合、余白の制御を nth-child 等を使って打ち消したり付与したり、width の幅を calc を使って計算したりと、どんどん CSS をコントロールしないといけなくなります。
なので .cardList__itemLink に margin を一律つけてしまえばいいのです。その分全体が右下方向にずれてしまいますが、 cardList__itemLink につけた margin の「ネガティブ値」をそのまま .cardList__items につけてあげれば正しい位置に戻ってくれます。

ただ横並びにするだけですが、これだけ手数を少なくして実現できるのです。
手数が少ない理由として display: flex; を使っているから、というのももちろんありますが、float: left; でも display: inline-block; で横並びにする場合も、同じ手は使えます。

カードの列数を画面幅に合わせて変えたい

レスポンシブデザインという言葉が流行って久しいですが、画面幅が広くなるにつれてカラム数を増やす仕組みを作ることに四苦八苦している方はまだまだ多いのではないでしょうか。
ただ、もし前節のように CSS を書けているなら、以下のようにすれば簡単にカラム数を増やすことができます。

  .cardList__item {
    width: 50%;

    @media (min-width: 480px) {
      width: 33.33%;
    }
  }

Media Query の記述を追加しただけです。
画面幅が小さいときの状態をデフォルトとし、画面幅が大きくになるにつれてカラムを増やす形にしています。

画面幅が大きいときをデフォルトとしてもよいのですが、画面幅が大きいときはも要素を増やしがちになり、画面幅が小さいときに要素を打ち消していくことになるので CSS の手数を増やすことに繋がります。

PC 向けに幅を固定したい

「PC 向け = 画面幅が広いとき」と考え、前節と同じよう Media Query で .cardList に対してスタイリングすることもできます。
ただ、そうしてしまうと、.cardList に対して少し責務を持たせすぎな感じがします。
画面幅が広いときに .cardList の横幅を変えるとしてしまうと、例えば、.cardList が2カラムのページレイアウトの中に入るときに横幅のコントロールが難しそうです。
なので、横幅を決めるときは、その横幅を決めるための責務を持つ要素を新たに作ったほうが良さそうです。

<div class="page">
  <div class="page__item">
    <div class="cardList">...</div>
  </div>
</div>
.page {
  margin: 0 auto;
  padding: 24px 0;
  max-width: 960px;

  .page__item {
    padding: 0 16px;
  }
}

.cardList は横幅可変で作成されています。なので、上記のように max-width を活用することで一定数の幅までいったら横幅が固定されます。
もしある程度画面が広がったときにページの要素を2カラムにしたい場合は、.pagedisplay: flex;display: grid; を足して、.page__item に横幅の設定を入れてあげれば良いです。

縦に並ぶ要素の間を空けたい

.cardList の下に新たな .articleList という要素を追加したいとします。

上記の画像が追加したときの状態です。
この画像のように追加した場合、.cardList.articleList の間に余白を入れたくなると思います。
この余白を入れる場合に重要なのは、どの要素がこの余白を管理すべきかを考えることです。

もし、.cardList で管理する場合は margin-bottom を入れることになります。そうすると一律下に余白が空いてしまい、.cardList の下には別の要素が入ることを示唆してしまいます。
.cardList にそれを示唆する責務があるかと考えると、それはやはり過多だと感じざるを得ません。

また、.articleList に関しても同様です。もしこの要素に margin-top を入れた場合、.cardList が存在しなかったら無駄に余白が空いてしまいます。
ただ「.articleList に入れた場合は .cardList + .articleList のときだけ margin-top を空ける」という指定が可能なので、.cardListmargin-bottom を入れるよりかはかなり良いはずです。
とはいえ、.cardList.articleList の間に別の要素が入ってしまったら、また余白の指定の仕方を考えないといけないため、どんどん手数が多くなりそうです。

そう考えると、この余白は .page に持たせるのが良さそうです。以下のように書いてみるとスッキリします。

<div class="page">
  <div class="page__item">
    <div class="cardList">...</div>
  </div>
  <div class="page__item">
    <div class="articleList">...</div>
  </div>
</div>
.page {
  ...
  
  .page__item + .page__item {
    margin-top: 24px;
  }
}

このようにしておけば、「間の余白を空ける」という指定文から .cardList.articleList の文脈を抜くことができます。
別の要素が追加されても .page__item で囲んでしまえば CSS を直さずに要素を追加できます。
もし .page__item 同士の間を一部変えたければ別の要素名にしたり、Modifier を追加したりすれば良いわけです。

この話から分かることは、要素間の余白というのはそれらの要素と並列であり、囲んでいる要素で管理すべきということです。
Figma にある Auto layout の Spacing between items がうまく機能しているのもそのためです。
上記の CSS の指定の仕方は、Spacing between items と同様の指定の仕方なので、Figma の Auto Layout でデザインされたものを実装するときにとても相性が良いはずです。

非活性リンクのデザインを変えたい

以下のようにリンクやボタンを非活性にしたいことはよくあると思います。


(記事のリンクを非活性にするというのはあまりないかもしれませんが、あくまでも例として見ていただければ幸いです。)

上記のときの、ベースとなるリンクのコーディングは以下の通りです。

<div class="articleList">
  ...
  <a href="#" class="articleList__link">
    <p class="articleList__name">Article 1</p>
    <p class="articleList__description">Article Description</p>
  </a>
  ...
</div>
.articleList {
  ...

 .articleList__link {
    display: block;
    padding: 16px;
    border-top: 1px solid #eaeaea;
    border-bottom: 1px solid #eaeaea;
    text-decoration: none;
  }

  .articleList__name {
    font-size: 14px;
    font-weight: bold;
  }

  .articleList__description {
    margin-top: 5px;
    color: #808080;
  }
}

このときに非活性のときの見た目を作っていくわけですが、この場合は「非活性」という「状態」を作り出すため、BEM でコーディングしている場合は Modifier を追加します。
BEM を使っていない場合でもやることは同じです。ベースとなる CSS に対して必要最低限だけ上書きしていくことを心がけてスタイルを追加していきます。

.articleList {
  ...

  .articleList__link.articleList__link--disabled {
    background-color: #ccc;
    color: #fff;

    .articleList__description {
      color: inherit;
    }
  }
}

いくつかポイントがあります。
まず、articleList__link.articleList__link--disabled と指定しているところです。もし .articleList__link--disabled と指定してしまうと、.articleList 内であればどこでもこのスタイルを使うことができてしまいます。
あくまでも、articleList__link に対しての Modifier としてしか機能しないようにすることが重要です。

CSS の指定だと background ではなく backgrond-color としているところも重要です。
background という指定の仕方は background にまつわる様々な指定をまとめて行うために用意されています。それを使わずに background-color を使うことで「必要最低限」を実現しています。
Modifier を指定する場合は限定的に指定することで意図を明確にすることも重要です。

また、.articleList__descriptioncolor: inherit としています。inherit は、親要素と同じ指定にするためのものです。
今回は、非活性を表現するために、リンク内の文字色を一律 #fff にするようにしています。ただ、articleList__description には #808080 が指定されているため、親要素で #fff を指定しても色が上書きされません。
そのため、親の #fff を継承して上書きするように指定してあげることで「非活性のときは一律文字色を #fff にする」という文脈をソースコードで表現することができます。
もし、ただ単に .articleList__description#fff にするのであれば無理に inherit を使わなくていいと思いますが、デザインの文脈に合わせて inherit を使うと、これもまた指定の手数を減らすことにつながるはずです。

さいごに

様々な観点で手数を減らしながら「ええ加減」を探ってみましたがいかがだったでしょうか。
今回は BEM で書かせていただきましたが、どのような形態で CSS を書いていても使える知識だと思っています。
(CSS in JS で要素指定を禁止している場合は、隣接セレクタを活用するのはもしかしたら難しいかもしれません。)

「ええ加減」に書くことで CSS に対しての接し方はもちろんのこと、ものづくりに対しての接し方自体も見つめ直していけると、より良いものづくりをさらに追求できるのかもしれません。

今回この記事のために作ったソースコードを GitHub にあげておりますので、全体像を見たい方はぜひご覧になってください。

Code: https://github.com/rhirayamaaan/eekagen-css/
Page: https://rhirayamaaan.github.io/eekagen-css/

Discussion