MERYの記事作成ツールをNuxt.js × Atomic Designで作り直した話(CSS設計編)

前回は、記事作成ツールの具体的な機能や記事内容を構成するアイテムコンポーネントの中身を紹介しました。
今回は、記事作成ツールで採用したCSS設計について紹介していきます。

新しい記事作成ツールでは、CSSプリプロセッサとしてSCSSを使用しており、共通のカラーコードやフォントサイズなどはグローバルCSSとしてnuxt.config.jsファイルに記述し、全体で使えるようにしています。また、コンポーネントの中では全てScoped CSSを使っています。

Scoped CSSを使用すれば、コンポーネントごとにユニークな属性が割り当てられカプセル化されるので、厳格な命名規則があるようなCSS設計を導入するメリットは低くなったように感じます。しかし、まったくルールを設けずに開発を行っていくと、担当者によって記述にバラツキがでたりして大規模な開発になるにつれて効率は下がってしまうでしょう。

そこで新しい記事作成ツールの開発では、設計手法としてRSCSSを採用しました。
実はMERYではこれまでにもRSCSSを他の内部サービスでも使用していましたが、今回はじめてNuxt.jsでRSCSSを使ってみたところ非常に相性がよいと感じたので紹介させてください。

RSCSSは公式のドキュメントで「フレームワークではなくアイディア集である」と謳っているだけあって、非常にシンプルでルールが比較的少ないです。
基本的な考え方はBEMと似ているので、BEMを使用したことがある方は理解しやすいかと思います。BEMと違う点として、命名規則がシンプルでclass名が冗長になることがなく、スッキリとした見やすいCSSを書くことができます。
ComponentsとElementsの2つで構成されるので、それぞれについて公式のドキュメントの例を見ながら簡単に説明していきます。

Components

1つのパーツをComponentsとして、ハイフンで繋げた少なくとも2単語以上で名前付けを行います。

  • A like button (.like-button)
  • A search form (.search-form)
  • A news article card (.article-card)

Elements

ElementsはComponentを構成する要素で、子セレクタを使用するのが推奨されており、基本的には1単語で名前付けを行います。

search-form {
  > .field { /* ... */ }
  > .action { /* ... */ }
}

もし名前が2単語以上になったら、そのまま連結させます。

.profile-box {
  > .firstname { /* ... */ }
  > .lastname { /* ... */ }
  > .avatar { /* ... */ }
}

公式ドキュメントではそのまま連結をさせていますが、今回の開発では視認性の観点から.firstNameのようにキャメルケースにするというルールにしました。
あくまでもRSCSSはアイデア集なので、破綻しない程度で好みに合わせて変更・拡張していくのがいいと思います。

この他に、状態や見た目を変えるVariantや、汎用クラスを定義するHelperなどもありますが、ここでは割愛いたしますので、気になった方は公式ドキュメントで確認してみてください。

実際の例

それでは実際に記事作成ツールのコンポーネントを例に、CSSの記述方法を見ていきましょう。

こちらは記事のタイトルと説明文を設定するArticleHeader.vueコンポーネントになります。


<template>
   <div class="article-header>
    <input class="title" placeholder="タイトル" v-model="title" @input="updateTitle">
    <p class="titleCount">{{ titleCount }}文字 / {{ maxTitleCount }}文字</p>
    <textarea class="lead" placeholder="説明文" v-model="lead" @input="updateLead"></textarea>
    <p class="leadCount">{{ leadCount }}文字 / {{ maxLeadCount }}文字</p>
  </div>
</template>
<style scoped lang="scss">
  .article-header {
    max-width: 720px;
    padding: 30px 20px 0;
    > .title {
      height: 40px;
      border: none;
      font-size: $-font-size-xlarge;
      border-bottom: $-border-normal;
    }
    > .titleCount {
      font-size: $-font-size-xsmall;
      text-align: right;
      right: 0;
      margin: 0;
    }
    > .lead {
      height: 90px;
      margin-top: 30px;
      font-size: $-font-size-normal;
      border-bottom: $-border-normal;
    }
    > .leadCount {
      font-size: $-font-size-xsmall;
      text-align: right;
    }
  }
</style>

CSSの記述をみてもらうと非常にシンプルで見やすいと思います。

このコンポーネント内のtitleとleadは他の場所で使われることがないため1つのコンポーネントとしていますが、titleとleadをコンポーネントとして分ける場合はそれぞれにRSCSSのComponentsにあたるwrapperのタグを追加して構成していくということになります。

コンポーネント指向のフレームワークとRSCSSの相性のよさ

新しい記事作成ツールでは、コンポーネント指向のフレームワークであるNuxt.jsを採用し、なおかつAtomic Designを採用したため、1つ1つのコンポーネントが細分化されていました。
コンポーネントが小さいため、他のメジャーな設計手法に比べて記述量も少なくシンプルに設計できるRSCSSは、今回の構成に非常にマッチしていました。

RSCSSの懸念点として、Componentsの2単語が思いつかないという声を耳にすることがあります。

しかし、Vue.jsを使っての開発では、将来定義されるHTML要素との衝突を防止するため、コンポーネント名は複数単語が推奨されているので、
そもそもファイル名(コンポーネント名)を複数単語で作っている方が多いのではないでしょうか。
そのため、ファイル名(コンポーネント名)をそのままハイフンで繋げればRSCSSのComponentsの命名規則に当てはまるため、特に深く考えずに使えると思います。

今回RSCSSを使用して開発してみて、CSSに関してストレスをほとんど感じなかったので、コンポーネント指向のフレームワーク等の開発でぜひ検討してみてください。

おわりに

MERYの記事作成ツールをNuxt.js × Atomic Designで作り直した話を、コンポーネント設計編機能編、今回のCSS設計編と全3回に渡って紹介しました。

Nuxt.js・Atomic Design共にMERYのアプリケーションでは初めての採用だったため、最初はコンポーネントの分割や機能の実装に苦労したりしましたが、なんとか社内のライターに使ってもらえる状態にもっていくことができました。

ただ最低限記事を作成できる状態にはなりましたが、これがゴールではありません。
開発前にライターなど社内メンバーからもらった要望がたくさんあり、現状ではそれがすべて反映されたわけではないからです。
短い時間で記事が書けるようになったり、記事をもっと書きやすくして、コストを下げることができてはじめて成果といえるでしょう。
まだこれからも開発は続くので、新しい機能を追加していく中で技術的に共有できることがあれば、またどこかで紹介できればと思います。

comy

comy

    フロントエンドエンジニアです

    関連記事
    MERYの記事作成ツールをNuxt.js × Atomic Designで作り直した話(機能編)
    トップへ戻る