Help us understand the problem. What is going on with this article?

ぼくがかんがえたさいきょうのCSS設計

More than 3 years have passed since last update.

Misocaという会社でなんかしてる @merotan ともうします。
最近仕事でとある機能のUIリニューアルをおこなうことがありまして、僕はどうしてもPostCSSを使いたかったのと、現行のクソみたいになっているCSSからなんとしてでも脱却したかったので、どうすればクソなことにならずいい感じにCSSを書くことができるか考えて実践してみた結果を書いていこうと思います。

影響を受けているもの

参考・影響を受けているものとしてFLOCSSBEMAtomicDesignなどがあります。
意識はしてないものの何かでみてあーこれ良いなと思ったものもあるかもです。

命名規則

命名規則はBEMです。(BEMの説明はここではしないので、上のリンクから確認していただければと)
ですが、Modifierはカスタムデータ属性を使用します。

またModifierはカスタムデータ属性を活用します。
こうすることで無理にクラス名で状態等を表現することがなくなり、HTMLでのクラス名が短くなり非常に読みやすくなります。

詳しくは
CSS設計の類はもっとカスタムデータ属性を活用するべきでは

BEMのModifierはclassで書くのやめませんか?
で。

使用ツール

PostCSSを使用します。
将来的に実装されるであろう機能がPostCSSのプラグインとして提供されているので保守性が高いと思います。

主に使うプラグインがこれらになります。
詳細はここでは説明しませんので、Githubで確認していただければと思います。

基本ルール

基本的にHTMLタグに対して複数のクラスは付けないようにします。
1タグ1クラスにします。

ファイル構成

ファイル構成は以下のようにします。

src/stylesheets/
  |-- main.css
  |
  |-- foundation/
  |     |-- index.css
  |     |-- reset.css
  |     `-- base.css
  |
  |-- variables/
  |     |-- index.css
  |     |-- layout.css
  |     `-- color.css
  |
  |-- units/
  |     |-- index.css
  |     |-- text.css
  |     |-- color-palette.css
  :     : 
  |
  |-- parts/
  |     |-- index.css
  |     |-- button.css
  |     |-- message-ballon.css
  :     : 
  |
  |-- components/
  |     |-- index.css
  |     |-- header.css
  |     |-- member-list.css
  |     |-- modal
  |     |    |-- index.css
  |     |    |-- confirm-window.css
  |     |    |-- alert-window.css
  :     : 
  |
  |-- layouts/
  |     |-- index.css
  |     |-- index-layout.css
  |     |-- edit-layout.css
  :     : 
  |
  |-- utilities/
  |     |-- index.css
  |     |-- margin.css
  |     |-- padding.css
  :     : 

main.cssはエントリーポイントです。
基本的には

@import 'foundation';
@import 'variables';
@import 'units';
@import 'parts';
@import 'components';
@import 'layouts';
@import 'utilities';

このように定義するだけになります。

また各ディレクトリのindex.cssでは基本的には各ディレクトリのCSSファイルをインポートするようにします。

foundation

foundationでは主にReset.cssNormalize.css等のブラウザのデフォルトスタイルの初期化、アプリケーションのベースとなるスタイルを定義します。
アプリケーション全体の背景、フォントの設定等を行います。

variables

ここではアプリケーションで使われるグローバルな変数を定義します。
CSS Custom Propertiesを使い定義していきます。
プラグインとしてはpostcss-custom-propertiesです。

  • layout.cssにはアプリケーションのレイアウトのwidth等の変数を定義します。
  • color.cssにはアプリケーションで使われる色を定義します。
variables/layout.css
:root {
  --nav-width: 200px;
  --content-width: 700px;
}
variables/color.css
:root {
  --text-default-color: #333;
  --text-warning-color: red;
}

こんな感じ。

units

ここではアプリケーションの各 parts / component を構成するのに必要なものを定義します。
例をあげると、文字の大きさ(font-size)と文字の太さ(font-weight)の組み合わせや、背景色(background-color)と文字色(color)の組み合わせ、
のようなアプリケーションの構成要素として基本的なモノを定義していきます。
CSS @apply Ruleを使用します。
プラグインとしてはpostcss-applyです。

units/text.css
:root {
  --default-text: {
    font-size: 14px;
    font-weight: normal;
    line-height: normal;
  }
}
units/color-palette.css
:root {
  --main-color-set: {
    color: white;
    background-color: var(--main-color);
  }
  --warning-color-set: {
    color: white;
    background-color: var(--warning-color);
  }
}
units/rectangle.css
:root {
  --rounded-rectangle: {
    border-radius: 4px;
  }
}

以上のように書いていきます。

parts

ここではアプリケーションに必要最小限の構成物を定義します。
ボタンや、メッセージバルーン、タブ、テキストフィールドといった汎用的なモノを定義します。
基本的には子要素(BEMでいうエレメントですね)を作らないようにします。
必要最小限の構成物なので、複雑なものは作らないようにします。

parts/button.css
.button {
  @apply --main-color-set;
  @apply --default-text;
  @apply --rounded-rectangle;

  &[data-size="large"] {
    width: 200px;
    height: 50px;
  }
  &[data-size="small"] {
    width: 20px;
    height: 20px;
  }
}

このように基本的にはunitを組み合わせて作っていきます。
ですが基本的にはなので、ここにスタイルを書いてはだめということではありません。

こうすることで、パット見でどういうスタイルなのかがわかりやすくなります。
上の例だと、「色はアプリのメインの色で、文字はデフォルトのもので、角丸な四角い、ボタン」ということが、詳細をみなくてもわかるようになります。

components

ここではアプリケーション内で使う大きい構成物を定義します。
componentsは他のcomponentspartsを子要素として持つ場合があります。
components内でcomponentspartsを子要素として持つ場合は、
hogeArea(このhoge基本的にはcomponents名やparts名にしますが、大まかに左と右で分けたい等ある場合は、leftArearightAreaとすることもあります。)をつくってその中に、componentspartsを格納します。

components/header.css
.header {
  @apply --clearfix;
  height: var(--app-header-height);

  &_logoArea {
    float: left;
  }
  &_loginButtonArea {
    float: right;
  }
}
header.html
<header class="header">
  <div class="header_logoArea">
    <img class="logo" src="logo.png" />
  </div>
  <div class="header_loginButtonArea">
    <button class="button">ログイン</button>
  </div>
</header>

このような感じです。
こうすることで、他のcomponentspartsに対してスタイルの上書きを行わないようにします。

また、似たようなcomponents(例えば確認ダイアログとアラートダイアログは似たような見た目かと思います)はディレクトリを切るようにします。
そのディレクトリのindex.cssにそのコンポーネント共通のunitsvariables、共通の親components等を切り出します。
こうすることで、似たようなコンポーネントだからといってむやみにunitsとして切り出してしまうことを抑制でき、かつ似たcomponentsをまとめることができます。

layouts

ここでは各componentをどこに配置するかを定義します。
大枠のワイヤーフレームを書くものです。
基本的にはfloatflexboxpositionを利用してレイアウトを行っていきます。

layouts/application-layout.css
.applicationLayout {
  display: flex;
  flex-direction: row;
  &_navArea {
    order: 1;
    width: var(--application-nav-width);
  }
  &_contentArea {
    order: 2;
    width: var(--application-content-width);
    min-width: var(--application-content-min-width);
  }
}

命名規則としてhogeLayoutのように後ろにLayoutをつけます。
また、子要素はすべてhogeArea(componentsのと同様に)のように後ろにAreaをつけます。

utilities

ここではある特定の一箇所でのみスタイルを当てたいという場合が発生した時に使うスタイルを定義します。
例えば説明文中にそこでしか使わない、特別なmarginが必要な場合等に使います。
命名規則は特殊なものとわかりやすくするために、
u-hogeのように最初にu-つけるようにします。
またそのスタイルを優先(強制)させるために!importantを必ずつけるようにします。

margin.css
.u-description-margin-bottom {
  margin-bottom: 10px !important;
}

使ってみた感想

自分で考えたこともあるのですが、非常にやりやすかったです。
特にhogeAreaのお陰でスタイルの上書きをすることがなくレイアウトできたのがよかったなぁとおもいました。
またunitsは良かったです。partsのところにも書いたのですが、人が書いたCSSでも詳細を見ることなく、@applyのところを読むだけで「こういう見た目になるんだろうな」というのがすぐひと目でわかるのがとても良かったです。

とはいえ、どの程度のものをunitsに切り出すか、partsとするかというのが難しくて悩むことが多かったです。
でも多分そのへんはどの設計でも難しいのかなとも思ったりしたり…。
また、Layoutsの恩恵があまりうけれずこれいるのかなぁ…とおもったりしたりしました…。

まだまだ改善点がたくさんあるだろうなとつよく思い、今後も改善していこうと思いました。

世の中には色々CSSの設計思想があってどれも良いものだと思いますが、
皆さんのやりやすいやり方を見つけたり考えられたりできるといいなと思います。
これが皆さんのなにか参考になるといいなぁと思います。

merotan
ふろんとえんどとかるびぃとかをやってる CSSは悪い文明! 粉砕する!
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした