459
458

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

社内でCSSの新しい方針について話したメモ

Last updated at Posted at 2016-08-18
1 / 57

社内で新しいドメインを設立するにあたり、__CSS Modules, PostCSS, cssnext__を試してみました。
このスライドは、その際の説明に使ったものです。せっかくなので公開します。

「プロトタイプ作成で試してみたけど、みなさんどう思いますか?」くらいの温度感。本番採用が確定したわけではありません。何かお役に立つことがアレば幸いです。


以後、説明に使ったスライド。


おしながき

    1. コンポーネント時代のスタイリング
    1. グローバルCSS、BEM、そしてローカルCSS
    1. CSS Modules、そしてJSXへの割り振り
    1. cssnextと、その書き方
    1. 我々のPostCSSスタンダード

新ドメインの
CSS環境(案)


CSS Modules
css next
PostCSS


on webpack


68747470733a2f2f7765627061636b2e6769746875622e696f2f6173736574732f6c6f676f2e706e67.png


何が変わるのか


  • 我々の今までのスタイリング
  • sassで書く
  • スタイルのモジュール化とネスト、そしてbodyクラスを駆使して、スタイルの衝突を防ぐ
  • gulpでconcatした1枚の巨大なCSSをHTMLから読み込む

  • これからのスタイリング
  • (ほぼ)生のCSSを書く
  • クラス名の衝突を気にする必要はない。かつ、BEM等は使わない
  • クラス名をJavaScriptにimportして、JSXにバインドする
  • webpackでconcatした1枚のCSSをHTMLから読み込む

順を追って説明します。


1. コンポーネント時代のスタイリング


ここでのコンポーネントは、CSSを単にカプセル化するという意味ではなく、Reactのコンポーネントに対応してコンポーネント化するという意味。



ボタンコンポーネント

// Button.jsx
const Button = ({ onChangeButton }) => (
  <div>
    <button
      onClick={onChangeButton}
    >
    変更
    </button>
  </div>
)

※例なのでけっこう雑です


対応するCSSコンポーネント

/* Button.css */
.container {
   margin: 0 auto;
}

.container .button {
   background-color: #996;
   color: #fff;
}

.container .button:hover {
   background-color: #ff6;
}

※例なのでけっこう雑です


基本的にはJS(X)と
CSSの範囲が一致。


メリット

  • スタイルが見つけやすい
  • 再利用性が高まる
  • 捨てやすい

参考リンク: コンポーネント時代の CSS 設計


とは言え、今までもHTMLとSCSSはパーシャル化して対応させていた。
我々にとって、コンポーネントCSSは真新しいことではないかもしれない。


2. グローバルCSS
BEM、
そしてローカルCSS


CSSの問題点


Screen Shot 2016-08-18 at 14.15.01.png

CSS Modules - Welcome to the Future- より引用


基本的には全てのCSSはグローバルである


スタイルの影響範囲が分からない


「なぜかスタイルが効きません😫」


かつての対応策


Screen Shot 2016-08-18 at 14.17.52.png

悩まないコーディングをしよう! OOCSS,SMACSSを用いた、読みやすくてメンテナブルなCSS設計(Sass対応)
より引用


命名規則で対応していた

<div class="block">
    <div class="block__element"></div>
    <div class="block__element--modifier"></div>
</div>
<div class="block--modifier">
    <div class="block--modifier__element"></div>
</div>

BEMという命名規則とSass 3.3の新しい記法より引用


我々の対応

命名規則での対応は、我々が通らなかった道。
bodyクラスで対応していた。

// application.scss
body.account {
  @import "page/account.scss";
}

body.setting {
  @import "page/setting.scss";
}

CSSは<body/>単位でローカル化される。


ローカルCSS


css-loader

webpackのcss-loaderをモジュールモードで使えば、以下のCSSが

// common.css
.button {
  // here comes style
}

以下のように変換される。

// application.css
.common-button-1DYla {
  // here comes style
}

モジュールモードをON

CSS Modulesは、css-loaderのモジュールモードをtrueにした状態。

// webpack.config.js
const cssLoaderQueryObj = {
  modules: true, // ← これ
  sourceMap: isDevelopment ? true : false, 
  localIdentName: '[name]-[local]-[hash:base64:5]',
  importLoaders: 1 // https://www.npmjs.com/package/postcss-loader#integration-with-css-modules
}

クラス名

[ファイル名]-[クラス名]-[ハッシュ値]


  • BEMのように人力で行っていたローカル化を、webpackが自動で行ってくれる。いわば、ローカルCSSのオートメーション化。

  • クラス名の衝突を開発者が気にする必要がなくなった。

  • ちなみに、セレクタにIDやHTMLのタグ名を使うことはできるけど、ローカル化はされない。

参考リンク:[意訳]グローバルCSSの終焉


ただし、css-loaderの仕組みは、<style />タグの動的生成。
→レンダリング時にスタイリングのラグが発生しうる。

そこで、ExtractTextPluginを使う。具体的には、コンパイル時にローカル化済みのCSSファイルを生成する。


ExtractTextPluginコンフィグ

// webpack.config.js
module: {
  loaders: [
    {
      test: /.css$/,
      loader: ExtractTextPlugin.extract([`css?${cssLoaderQuery}`, 'postcss'])
    }
  ]
},
plugins: [
  new ExtractTextPlugin("[name].css") // output CSS
]

これで<style/>、というか新しいCSSファイルが静的に生成される。これをHTMLから読み込む。


これでローカルCSSを静的に読み込むことができる


JS(X)との整合性


静的に生成されるCSSファイルでは、クラス名は既にローカル化されている

.common-button-1DYla {
  // here comes style
}

なので、JSX側で

<button className={button} />

としても、マッチせず、スタイルがあたらない。なので、JSにはローカル化されたクラス名を読み込む必要がある。


とは言えカンタン。CSS Modulesのもう1つの機能として、__ローカル化したクラス名のオブジェクト__を渡してくれる。

なので、まずはcssをimportして、そのプロパティであるクラス名をclassNameに割り当てる。

import buttonStyles from '../stylesheets/Button.css'

<button className={buttonStyles.button} />
// buttonStylesオブジェクトにクラス名がぶらさがっている。

複数クラス名の同時指定については、もう1工夫が必要。
cf. classnames-loader


cssnext


Screen Shot 2016-08-18 at 15.13.27.png


cssnextとは

未来のCSSを先取りしたもの。策定予定の仕様が盛り込まれている。

Screen Shot 2016-08-18 at 15.15.32.png

CSSファイルでネストが使える(嬉しい)


Sassとは若干シンタックスが異なる

.container {
  & .button {
    color: #fff; 
  }
}

ネストするには、 が必要。


脱Sassしてみる


脱Sass

  • Sassはオールインワン的。便利だけど、機能ありすぎ感。
  • Ruby製のツールなので、フロント的になんだか落ち着かない感。
  • node-sassとか、lib-sassとか、よく分からない…。

というわけで、脱Sassして、cssnextを使ってみることに。


PostCSS


cssnextはそのままでは使えない。ブラウザが読める形式に変換する必要がある。

そのためにPostCSSを使う。
より具体的に言うと、PostCSSのpostcss-cssnextを使う。

逆に言うと、それ以外のPostCSSプラグインはとりあえず使わない方向で。(オレオレCSS化するのを防ぐ)

参考リンク: PostCSSとcssnextで最新CSS仕様を先取り!


cssnextとPostCSSはES201*とbabelの関係


ここまでのwebpackコンフィグを抜き出すとこんな感じ。

module: {
  loaders: [
    {
      test: /.js?$/,
      loader: 'babel',
      exclude: /node_modules/
    },
    {
      test: /.css$/,
      loader: ExtractTextPlugin.extract([`css?${cssLoaderQuery}`, 'postcss'])
    }
  ]
},
postcss: () => {
  return [
    postcssCssnext()
  ]
},
plugins: [
  new ExtractTextPlugin("[name].css") 
]

※ cssローダーのクエリは外出ししている。 ※ ExtractTextPluginの引数の[name]はentry依存。


reset.css

いわゆるリセットCSS的なものも使っている。
ただし、これらはグローバルであってほしいものなので、ローカル化のプロセスをパスしてwebpackのバンドルに直に混ぜている。。

entry: {
  application: [
    './app/frontend/stylesheets/reset.css', // ← リセットCSS
    './app/frontend/javascripts/application'
  ]
},

生成後のCSSファイルの先頭に定義されるように、./app/frontend/javascripts/applicationよりも前に指定している。


おしまい


(存外読まれているので追記)


話の方向性としては、以下のようになりました。

  • Sassとのメリット・デメリットの比較をもっと詳しく行おう。
  • そもそもネストって必要なのか?ネストが不要ならばcssnextは不要化する。
  • デザインの流動性にコンポーネントの世界は耐えられるのか。

そんなわけで、そのうちまた続きを書くかもしれません。


459
458
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
459
458

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?