CSS設計 MOCSSの紹介と解説


GitHubはこちら

概要

MOCSS(モックス)は、CSS Modules、BEM、SMACSSに影響を受けて作られた、コンポーネント志向のCSS設計です。コンポーネントが持つ保護と再利用という2つの性質を分けることで、強固さと柔軟さを兼ね備えました。

背景

BEMやSMACSSなどでCSSに秩序がもたらされたものの、ウマく対応しきれない部分もあって結構不満も感じてました。BEMは命名が冗長だったり、SMACSSはあまり強固でなかったりなどなど。それに対し、CSS Modulesは結構よかったです。CSSの問題をよく分かってる感があります。ただWebpackとJSXを前提としているので、自分の範囲だと採用できるプロジェクトが限られてしまっています。

以前自分が設計した方法だと、CSS Modulesのようなコンポーネント志向でCSSを書くことができ、それは結構うまくいっていて、しかも他では見ない設計だったので今回共有しておこうと思いました。今回、改めて設計を見直すと共にMOCSSという名もつけて公開します。

※ 追記
MOCSSの思想となっている内容の記事を書きました。
より詳細な背景が知りたい場合は以下をご覧ください。

特徴

Componentと聞いて私たちが期待するのは以下の2点かと思います。

  • スタイルが閉じていて安全であること
  • 再利用可能な一つの塊であること

しかしCSS Modulesを使っていて思ったのは「少し変えれば再利用可能である」というパターンが実際には多く、再利用可能にしようとすると閉じたスタイルの一部を外に解放しないといけません。つまり何が言いたいかというと、この2つは相反する性質だということです。

この課題を解決するためのアイデアとして、上記の性質を別々にしてしまい、コンポーネントに差異が出ることを前提とした設計を行うことで生まれたのがMOCSSです。したがって、MOCSSはコンポーネントの差異に強いという特徴を持っています。

必要なもの

SASS(またはnested, import, mixin, include)を使用することが前提の設計です。またSASSに限らず、CSSプリプロセッサが使える環境であれば適用可能です。

基本原則

MOCSSでは、以下の5つの基本原則を押さえる必要があります。

  • Common
  • Model
  • Package
  • Component
  • Page

Common

Commonサイト全体に影響を与えるスタイルの集まりです。とにかく全体に影響のあるものは必ずcommonにぶち込み、common外に漏らさないことが原則です。

Model

Model は共通部品で、不変なスタイルの塊です。とにかく再利用するスタイルは全てModelとして抽象化していきます。再利用を妨げる微妙なスタイル差異や、他に影響を与えるmargin等は排除することが原則です。このModelが強固さと柔軟さを与えているので、ModelのMoをとってMOCSSと名付けています。

Package

Package は画面グループの名前空間で、class名の影響範囲を閉じ込めます。MOCSSでは、ページ毎にPackageを指定し、Package単位でスタイルを書いていくのが原則です。

Component

Component はmixinとして定義したModelの実体化を担います。また文脈によって生じるModelとの差異や、Package固有の調整などによるバリエーション増加を引き受けます。Packageクラス直下の要素は、必ず何かのComponentに属するようにするのが原則です。

Page

Page は画面固有の名前空間で、classの詳細度を強めます。それによって、PackageやComponentでは対応しきれない画面固有のスタイル調整を引き受けます。画面固有の名前空間なので、サイト全体で一意になるようにclass名を付けるのが原則です。

コード例

Common

// 共通の変数やスタイルはcommon配下に配置
$fontSizeS: 12px;
$fontSizeL: 16px;
*{ margin: 0; }

Model

// 共通のパーツをmixinとして定義

@mixin articleCardModel(){
  .title{
    font-size: $fontSizeL;
  }
  .summary{
    font-size: $fontSizeS;
  }
}

@mixin articleCardsModel(){
  .card-S{
    @include articleCardModel;
    width: (100%/3);
    margin-top: 8px;
  }
  .card-L{
    @include articleCardModel;
    width: (100%/2);
    margin-top: 8px;
  }
}

PackageとComponentとPage

// xxxPackageという名前で囲い、中にComponentを定義
.topPackage{

  // Componentでは、文脈によって生じる調整やPackage固有のスタイルなどをあてる
  .articleCardsComponent{
    @include articleCardsModel;
    margin-top: 8px;
    padding: 0 8px; 
  }

  // 画面固有のスタイル調整は、xxxPageで定義してComponentのスタイルを上書きする
  &.articlesPage{
    .articleCardsComponent{
      margin-top: 16px;
    }
  }
}

Html

<!-- bodyなど、rootに相当する要素に使いたいpackageを指定 -->
<body class="topPackage topPage">
  <div class="articleCardsComponent">
    <ul>
    <li class="card-S"></li>
    <li class="card-S"></li>
    <li class="card-S"></li>
    <li class="card-L"></li>
    <li class="card-L"></li>
    </ul>
  </div>
</body>
<!-- 個別にスタイル調整するには、xxxPageを指定してpackageのスタイルを上書き -->
<body class="topPackage articlesPage">
  <div class="articleCardsComponent">...</div>
</body>

サンプルについて

近日中に、簡単なサンプルコードをGitHub に用意したいと思います。
GitHubにもpushしました。
続きも書きました →次の記事