AbemaTVにおけるCSS is too fragile問題に対する解 / Solution of "CSS is too fragile" by AbemaTV

AbemaTVにおけるCSS is too fragile問題に対する解 / Solution of "CSS is too fragile" by AbemaTV

リファクタリングをしていたらCSS Modulesを使っていたにも関わらずCSSが壊れてしまった私達は、CSS ModulesがCSSの特性・我々のプロダクトの構成や開発方針に合わないと判断し「BEMベースのクラス名設計 + PostCSS」へ移行を進める方針を決めました。 これらの背景や、どうやって移行を進めたのか、また移行したあとの評価について話しました。

Cb0e411d88a1bcd1562890c816970a8e?s=128

Shota Kubota

May 18, 2019
Tweet

Transcript

  1. 7.

    元のCSSの構成: ディレクトリ構造とコード例 import React from 'react'; import classNames from 'classnames';

    import styles from './Button.css'; export const Button = ({ className, disabled, onClick }) => { className = classNames( className, styles.btn ); return ( <button className={className} disabled={disabled} onClick={onClick} > {children} </button> ); }; @import "vars"; .btn { border-radius: var(--radius); font-size: 100%; padding: .75em 3em; text-decoration: none; } .btn:hover { opacity: var(--opacity); } .btn:disabled { opacity: .2; } src/components ├── atoms │ └── Button │ ├── index.js │ ├── Button.jsx │ └── Button.css ├── molecules └── organisms
  2. 8.

    変更前後のディレクトリ構造 Before /src ├── /container/foo/A ├── /actions/foo/A ├── /dispatchers/foo/A ├──

    /stores/foo/A │ ├── /container/bar/B ├── /actions/bar/B ├── /dispatchers/bar/B └── /stores/bar/B After /src ├── /foo/container/A ├── /foo/actions/A ├── /foo/dispatchers/A ├── /foo/stores/A │ ├── /bar/container/B ├── /bar/actions/B ├── /bar/dispatchers/B └── /bar/stores/B • テレビ機能しかなかった 初期構成の負債の返却 • 特定の機能に閉じた変更 をしやすくする
  3. 11.

    スタイルが壊れる例 A B C D E A B C D

    E BCD間に記述順序に依存するスタイルがあると崩れる 1 2 3 4 5 1 4 2 3 5 変更前 変更後
  4. 14.

    スタイル崩れ その2 崩れている例 正常な例 スタイルの定義順が入れ替わっていた /* 入力フォーム側のボタンのスタイル定義 */ .btn {

    padding: 12px 24px; } /* ボタン側のスタイル定義 */ .btn { padding: .75em 3em; } 段差がある 段差がない
  5. 18.

    BEM + PostCSSへ移行 • BEMベースのクラス名の命名規則 ◦ .アプリケーションドメイン -コンポーネント名__コンポーネント内の要素 --修飾子 ▪

    例: .video-PlayerContainer__screen ◦ grepのためにクラス名を JavaScriptで動的に構築するのは避ける • postcss-importを使ってCSSが展開されるようにした ◦ コンポーネントのルートディレクトリ内に root.cssを用意して その中でCSSをimportする
  6. 19.

    BEM + PostCSSへ移行 Before: CSS Modulesベース @import "vars"; .btn {

    border-radius: var(--radius); font-size: 100%; padding: .75em 3em; text-decoration: none; } .btn:hover { opacity: var(--opacity); } .btn:disabled { opacity: .2; } After: BEM + PostCSSベース .com-a-Button { border-radius: var(--radius); font-size: 100%; padding: .75em 3em; text-decoration: none; } .com-a-Button:hover { opacity: var(--opacity); } .com-a-Button:disabled { opacity: .2; } root.css @import './vars.css'; @import './component/atom.css'; @import './component/molecules.css'; @import './component/organisms.css'; /* template */ @import './featA/featA.css'; @import './featB/featB.css'; /* page */ @import './page1/page1.css'; @import './page2/page2.css';
  7. 22.

    CSS Modulesよりは考えることが増えた • BEM + PostCSSではクラス名の規約を作る必要が増えた • クラス名の規約に沿っているかレビューで見る必要が増えた • 規約をチームメンバーに浸透する必要が増えた

    • チームメンバーに規約が浸透すればレビューで指摘することは そんなにない • 生成後のCSSを想像しやすくなった利点のほうが大きい
  8. 23.
  9. 25.

    他の選択肢と選ばなかった理由 • どうにかしてCSS Modulesを使い続けたりCSS in JSを使ったりする ◦ 標準のCSSの書き方を保ちやすい、ビルドも複雑にならないという点で BEM +

    PostCSSのほうがのちのち捨てやすいと判断 ◦ 標準仕様から外れるような PostCSSのプラグインを使わないようにしている • ディレクトリ構造の変更後にモジュールの探索順序を 変えないようにするためimport宣言の順序を変えない ◦ import宣言の並び順に一貫性がなくなる ◦ それはリファクタリングしたと言えるのか?
  10. 26.