LoginSignup
14
12

More than 5 years have passed since last update.

うわあああああってならない為のCSS設計

Last updated at Posted at 2018-12-08

こんにちは、マークアップを担当している@fork_kawaseです。

普段はSMACSSをベースにコーディングしていますが、運営を前提としているサイト制作においてどんなルールすれば
保守しやすいのか、拡張しやすいかを考え、自分なりのコーディングルールを改めてまとめることにしました。
過去経験した、('ω')······· うわあああああああああって思う 悪い例も合わせて紹介します。

記事内のSMACSSやBEMなどの有名な設計理論の詳細に関してはこの記事では
説明を省略させていただいています。
詳しくはこちらのリンク先で確認していただければ幸いです。
- SMACSS
- BEM

ルールの目標は

  • [ コードが再利用できる ]
  • [ 破綻しない(後から困らない)]

ベースルール

  • HTMLタグ単体へのstyle指定は禁止。
  • おおよそ役割が予測できる命名をする。
  • 相対的なネーミングは避ける。

HTMLタグ単体へのstyle指定は禁止

スタイル汚染が進行するのでHTMLタグ単体にスタイルを定義しない。
クラスへスタイルを定義する。

 .tableCell{color: blue;}
 .tableCell tr{color: blue;}
 .menuList a{display: block;}
('ω')······· うわあああああああああってなるやつ。
× tr{color: blue;}
× <p style="color: blue;"> 

おおよそ役割が予想できる命名をする。

命名規則はMindBEMdingを基準とします。
block__element--modifier
ただし、BEM記法そのままではネストを重ねるごとに名前が長くなります。
例) .block__elementsA__elementsB__elementsC--modifier
これだと長すぎる。目が疲れるし読む時スライドしてしまいます。

('ω')······· うわあああああああああってなるやつ。
/*css*/
.headerNav{}
.headerNav .headerNav__listText{}
.headerNav .headerNav__listText .headerNav__listText__iconImg,
.headerNav .headerNav__listText .headerNav__listText__iconImg--nowLink{}
.headerNav .headerNav__listText .headerNav__listText__iconImg--nowLink{}

このような命名を避けるために補足ルールで文字数を節約します。
補足ルールについては「命名規則」にて後述。

相対的なネーミングは避ける。

原則位置など相対的な要素による命名は避ける。意味や役割から命名を考える。
基準がどこにあるのかはっきりしていれば、相対的なクラス名でも命名して良いものする。

//例えば、
.primaryTagBox{
  display: inline-block;
  padding: 5px 20px;
  border: 1px solid black;
}
.primaryTagBox--long{
  @extend .primaryTagBox;
  padding: 5px 45px;
}

('ω')······· うわあああああああああってなるやつ。
.rowBoxLeftImg , .rowBoxRightText{}
.firstChild__middleBlock:last-child{} //('ω')うせやろ

CSSの読み込み順

SMACSSを基準にする

ページ全体に影響する横幅やコンテンツごとに共通するスタイルなど
クラスを分けて管理する。レイアウト・形・色など1つのクラスの中に情報を詰め込んではいけない。

Base / Layout / Module(Element) / Staite / Theme の順にクラスを分類する。

Base

リセットCSSなど初めに定義したい内容。
SASSを使う場合、このエリアで別シートをimportしたりやカラーなどの変数、WEBフォント、mixinなどを定義。

// Base Style
// ==========================
@import 'base'; //reset.cssをインポート
@import 'font'; //webfontをインポート

$main-black: #111;//変数を定義

$z-index: (
  nav  : 100
, logo :  50
, thumb:  30
);//配列を定義

@function zi($name){ //関数を定義
  $z-index : map-get($z-index, $name);
  @return $z-index;
}
// ==========================
//  など

Layout

ページ全体に対して 位置が左右どちらにあるのか または 均等割付なのか
ページ全体に幅は伸びているのか、最大幅をきめるかきめないか などなど
ページ中のレイアウト・ブロック組みを定義。

プレフィックス
名前の先頭にl-をつける。(「僕はLayoutのクラスだよ!」という目印)

// Layout Style
// ==========================
.l-wrapper{ //sectionごとのレイアウト
  max-width: 960px;
  margin: 0 auto;
  padding: 20px;
  box-sizing: border-box;
}
.l-wrapper--full{
  width: 100%;
  padding: 20px;
  box-sizing: border-box;
}
.l-row{
  display: flex;
  box-sizing: border-box;
  flex-wrap: wrap;
}
@for $num from 1 through 12{
  .l-pc-#{$num}{
    width: percentage($num / 12);
  }
}
// ==========================
//  など

Module

コンテンツ・最小パーツを定義。


//Module
.btnBlock-module{
  padding: 5px 15px;
  border: 1px solid #000;
  &:hover{
    color: white;
    background: gray;
  }
}

ボタン・カードコンテンツ・ナビゲーション・タブなど
ページで使われるほとんどのクラスをここで管理します。
モジュールには[module]を名前に入れる。
モジュールをまとめる親要素には[container]を名前入れる。

プレフィックス  ( ...プレフィックス? プレフィックス!!)
[container]のルールはSMACSSにはないルールのため、
layoutのl-やStateのis-のルールと区別したいです。
私の場合は名前の先頭に[container]を入れるのではなく、名前の末尾に[container]を入れています。
[module]も同様に名前末尾に追加。
(プレフィックスは[接頭辞]という意味なので末尾に追加という表現は、矛盾していますがご勘弁ください。。ここでは目印という役割です。)

//例: 
.newsBlock-container
.newsBlock-module
moduleとcontainer

[module]は単体でも機能するスタイルを組む。
ブロックごとの並び方や余白は[container]でまとめて管理する。

例えばニュース記事のコンテンツを作る場合は以下のようにHTMLを構成します。
スクリーンショット 2018-12-03 12.45.43.png

<div class="titleBlock-module">
  <div class="titleBlock__titleText">News</div>
  <div class="titleBlock__subTitleText">ニュース</div>
</div>

<div class="newsBlock-container">
  <div class="newsBlock-module">
    <div class="newsBlock__time">2018.12.09</div>
    <div class="newsBlock__title">Qiita記事リリース!ここにニュース記事の説明文が入ります。</div>
  </div>
  <div class="newsBlock-module"> ↔︎ </div>
  <div class="newsBlock-module"> ↔︎ </div>
</div>

<div class="btnBox-module">
  <a  class="btnBox__text" href="#">もっと見る</a>
</div>

繰り返されないmoduleにはcontainerは不要。
上の例では.titleBlock-module.btnBox-module
moduleが増えた時に後から[container]を足します。

おや、このままでは・・
「並び方を管理するのがcontainerってそれlayoutでは?」
とツッコミが飛び込みそうだ!('ω')

containerはlayoutでいいのでは?問題

ぶっちゃけcontainerのルールはmodulが複数ある想定の時にセットでいて欲しいものだからlayoutに居ると都合が悪いのです。

~ここから言い訳('ω')~
- containerはmoduleをまとめて管理するものと同時にmoduleの延長線にあるもの。
- containerは各moduleに依存するためlayoutと比べると用途が狭い。なので別物の扱い。
- 一緒に唱えましょう。containerはmoduleとセット..containerはmoduleとセット..containerはmodul....ほらcontainerがlayoutにいなくても違和感がない!

<!--補足-->
<section class="l-wrapper">
  <div><p>l-wrapperはセクションごとに使えるが</p></div>
</section>

<section class="l-wrapper">
  <div class="primaryText-container">
    <p class="primaryText-module">containerは</p>
    <p class="primaryText-module">moduleに依存する</p>
  </div>
</section>

State

状態を表すクラスを定義。主にJavaScriptなどで制御するクラス。
表示/非表示の切り替えなどJavaScriptの動きでスタイルを上書きする際に使うクラス。

プレフィックス
Layoutの時と同様に目印として[is-]を名前の頭に入れる。

.is-hide{
  visibility: hidden;
  pointer-events: none;
}
.is-mark{
  color: red;
}

Theme

色に関わるスタイルを定義。
background,colorなど

プレフィックス
[theme-]を名前の頭に入れる。
プレフィックスは他の人が見たときに何の要素を表すものなのか判断材料になるので問答無用に必要
プレフィックス不要
補足:
SMACSSではthemeを名前の先頭につけるルールですが
過去に私がSMACSSで運営していて背景色と文字色の変更以外にthemeを使用したことがないため不要で問題ないと判断。

.theme-bgColorMainRed{
  background: $main-red;
  color: white;
}
.theme-bgColorBlack{
  background: black;
  color: white;
}

命名規則について

キャメル/スネーク/ケバブの使い分け

BEMではBlockとelementはアンダースコア2つ__で区切り、
Blockとmodifierはハイフン2つ--で区切ります。

  • キャメル
    BEM記法のblockの用途に使用。
    例: newsBlock , tableCell など
  • スネーク
    BEM記法のelementの用途に使用。
    elementはblockの中にいる要素。
    アンダースコア2つの後はblockと同じようにキャメルで
    例: newsBlock__imgBlock , tableCell__headText など

  • ケバブ
    BEM記法のmodifierの用途に使用。
    modifierはblockやelementの状態の変化を表す要素。
    例: newsBlock--bgColorRed
    [l-][is-][theme-][-module][-container]はmodifierはないのでハイフン1つ

BEM記法の命名について

BEMそのままの運用ではネストが増えた時module名が長くなり読みづらくなります。
module内で管理するクラスはネストする度に名前をブロック切り。
moduleやcontainerにmodifierがつく場合は、後ろに追加。

//例
.primaryCard-module{
  .primaryCard__textBlock{
    .textBlock__titleBlock{・・・}
    .textBlock__leadBlock--bgColorRed{・・・}
  }
  .primaryCard__imgBlock{・・・}
  .primaryCard__btnBlock--bgColorRed{・・・}
}

//module や container に modifier がつく場合。
.primaryCard-module--bgColorRed{
  .primaryCard__textBlock{・・・}
}
.primaryCard-console--bgColorBlack{
  .primaryCard-module{・・・}
}

あとがき

SMACSSとBEMを取り上げて、まとめてみましたが他の有名な設計思想にもそれぞれ一長一短があります。
ルールはあくまでも基準であり、プロジェクトに合わせて変化を加えていくのがベストだと思っています。

読みにくい文章になってしまいましたが最後まで読んでいただきありがとうございます。
使っていたルールの曖昧な部分を洗い出せるいい機会になったと思います。
この記事をまとめていて、自分の考えを文章にする難しさを改めて思い知りました。

参考文献


この記事は FORK Advent Calendar 2018 の9日目です。
前回の記事は => @lilysweet
次回の記事は => @karaage7


14
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
12