almost 10 years ago

CSSの保守性と見通しを高めて、プロジェクトに対するモチベーションを維持するためにやってることをまとめてみました。

主にCSSを保守するのがめんどくさいという事に対する愚痴が書いてあります。完全な持論ですので「そんなの絶対おかしいよ!」と思う事、あると思います。つっこみお待ちしております。

簡単なまとめ

  • クラスで切るんじゃなくてメタ言語のMixin(Function)で切る

  • よく使う値は変数で管理する

  • Bootstrapを逃げ道にするのはやめよう

長いけどこれだけです。以下は時間がある人だけ読んでください。


前提

  • Stylusというメタ言語を使用しています。Rubyistは適宜SCSSに読み替えるなどしてください、文法は殆ど同じです。

  • StylusはLESSと違ってサーバサイドでのみコンパイル可能なメタ言語なので、どんな非効率なコードだろうと気にする必要ないと思ってます。

  • ブラウザのCSSレンダリング速度はプロトタイピング中は気にしなくていいかなって思ってます。

  • クラスベースのCSSをdisってるわけじゃないです。

  • HTMLはjadeというメタ言語を使っています


わたしがまずいと思ってること

最近のイケてるサービスやBootstrapなどのCSSフレームワークでは、主にクラスベースでスタイルを切り分けてます。あれはHTML4.*とCSS2.*における完成系です、最初からあそこに辿り着こうと思ったらプロジェクトが崩壊します。

クラスベースのスタイライズを行うと、HTMLとCSSが密結合になります。スタイルの適応や修正にHTMLをいじる必要が出てきます。HTMLを過度に編集するとJSが不審な挙動を起こし始めることもよくあります。そして、JSのためにHTMLを編集するとそれに伴ってCSSも編集する必要に迫られることすらあります。

つまり、途中でスタイルを見直すと「CSSを編集⇄HTMLを編集⇆JSを編集」というコンボがキマり、結局全てのファイルを弄るハメになる場面が多々あります。スタイルを変更するのにロジックもいじらないといけないなんて変ですね。クソ食らえです。

結局、後に残るのは「全てを捨ててスマートなプロダクトをゼロから再実装したい」という欲求です。

個人や少人数など、足が軽い状態で実装していると「ゼロから再実装したくなる欲求」を抑制することは非常に困難です。加えて、そのような欲求に苛まれた際にもう引き返せないラインに到達していると、実装意欲の急激な低下を招き、結果としてプロジェクト放棄の原因となります。オナ禁っぽいですね。

これらを防ぐためにも、プロトタイピングにおいて「スタイル調整に伴うViewの変更」は不要とされるべきです。HTMLとCSSは疎結合であって欲しいというのが私の願いで、スタイルのことはスタイルの中で完結するべきなんです。

またプロダクション環境であっても、Viewの変更はユーザからキャッシュを奪うことになるので非効率なんじゃないかなって思います。

長い繰り返しコードはFunctionにして外部化する

微妙に値を変えながら繰り返して使用されるテンプレートはFunctionにします。

よくない例

例えば「マージン5pxの合計60x60pxのオブジェクトにviewからbackground-imageを指定して、要素の短辺を満たす最大サイズにfitさせたい」という場合、こんな風に書くと思います。

// html
.picture(style='background-image:url(#{pict})')

// css
.picutre
  margin 5px
  width 50px
  height 50px
  background-color transparent
  background-size contain
  background-repeat no-repeat
  background-position 50% 50%

クラスベース信者はすぐにこうしたがります。

// html
.picture.background-contain(style='background-image:url(#{pict})')

// css
.picture
  margin 5px
  width 50px
  height 50px
.background-contain
  background-color transparent
  background-size contain
  background-repeat no-repeat
  background-position 50% 50%

後から他のスタイルでbackground-positonmarginを弄る必要が出るとこうします。

// html
.picture.background-contain(style='background-image:url(#{pict})')
.image.background-contain.background-position-topleft(style='background-image:url(#{img})')

// css
.picture
  margin 5px
  width 50px
  height 50px
.image
  margin 10px
  width 20px
  height 20px
.background-contain
  background-color transparent
  background-size contain
  background-repeat no-repeat
  background-position 50% 50%
.background-position-topleft
  background-position 0 0 !important

何れもViewを弄る必要性が出てきます。もしmarginwidthheightもクラス化したい気分になっていたらそれはもう終わりの始まりです。

これではダメです。

よい例

// html
.picture(style='background-image:url(#{pict})')
.image(style='background-image:url(#{img})')

// css
background_fit(bgsize, leftposition = 50%, rightposition = 50%)
  background-color transparent
  background-size bgsize
  background-repeat no-repeat
  background-position leftposition rightposition

.picture
  margin 5px
  width 60px
  height 60px
  background_fit contain
.image
  margin 10px
  width 20px
  height 20px
  background_fit contain 0 0

background_fitの定義部が長くて全体の見通しを悪化させるので外部化します。

// html
.picture(style='background-image:url(#{pict})')
.image(style='background-image:url(#{img})')

// css
@import 'background_fit'
.picture
  margin 5px
  width 60px
  height 60px
  background_fit contain
.image
  margin 10px
  width 20px
  height 20px
  background_fit contain 0 0

もしmarginwidthheightも自動化したかったらこんな定義もあり得ます。

// css
background_fit_box(margin, boxsize, bgsize, leftposition = 50%, rightposition = 50%)
  margin margin
  width w = (boxsize - margin*2)
  height w
  background-color transparent
  background-size bgsize
  background-repeat no-repeat
  background-position leftposition rightposition

.picture
  background_fit_box 5px 60px contain

スマートです。

共通の色など、よく使う値は変数で管理する

stylusなどのメタ言語に乗っかってもせいぜい{}:;の省略とインデント程度しか使わず、MixinやVariableやConditionを活用しないし、むしろ活用しない方が実装速度が早いという人はいると思います。

でも、なんでそれが実装されてるのか?という点から考えれば自明の便利さがあります。

例えば、これをカラーベースにしてサイトを組もうと思い立ちます。

html, body
  color #3D3936
  background #ECE8DF
#header
  color #ECE8DF
  background #3D3936
#article
  background #FCFDFD
  &:hover
    background #F1F1F2

後から見たら「どこに」「どの色が」「どういう方針で」使われたのか全くわからないし、全部でどれだけの色数があるのかもわからない。

Typoがあってもきっと気づかないし、スキーマ変更に全文リプレイスを使用していると、その煩雑さから途中で「これでいいや(・ω・`)」という気持ちになってしまいます。

なので、こうします。

whitecolor = #FCFDFD
whitecolor_highlight = #F1F1F2
basecolor = #ECE8DF
basecolor_highlight = #CEC9C0
maincolor = #3D3936
maincolor_highlight = #1E1A17
accentcolor = #24B9EC
accentcolor_highlight = #089ACE

html, body
  color maincolor
  background basecolor
#header
  color basecolor
  background maincolor
#article
  background whitecolor
  &:hover
    background whitecolor_highlight

headerだけ色が反転している、hoverした時にのみ*_highlightを使う、等の情報がわかりやすくなります。

「コードから色の意味がわかるようにする」というのは有効な手段だと思います。レンダリングされたUIが意図した通りに動作・発色するかを効率的にテストする手法が無い(と思ってるけどあるのかな)ので、実際にhoverして間違った色を使ってないかチェックする手間は最後だけで済みます。

スキーマ変更時も変数を書き換えればよいだけなので、プロトタイピングも捗りますね。

もっと厳密な制約を課したい場合は、関数化するのもありです。

color_scheme(schema)
  if schema is 'regular'
    color maincolor
    background basecolor
  if schema is 'inverse'
    color basecolor
    background maincolor
  if schema is 'article'
    color maincolor
    background whitecolor
    &:hover // これをここに書くべきかどうかは要判断
      background whitecolor_highlight

html, body
  color_schema 'regular'
#header
  color_schema 'inverse'
#article
  color_schema 'article'      

また、*_highlightカラーが元のカラーから一定の法則で生成できるのならば、下記のように色を定義することも可能です。これはstylusの機能なので他にあるかは知りません、LESSではありました。

whitecolor = #FCFDFD
whitecolor_highlight = darken(whitecolor, 10)
basecolor = #ECE8DF
basecolor_highlight = darken(basecolor, 10)
maincolor = #3D3936
maincolor_highlight = darken(maincolor, 10)
accentcolor = #24B9EC
accentcolor_highlight = darken(accentcolor, 10)

共通する色や値の管理はCSSコーダーに取って結構な課題だと思います。何らかの方法を使ってスマートに解決しましょう。

最後に

みんながBootstrapに逃げたがるのって、うまいCSSの書き方メソッドを持ってなくてViewを浸食してぐちゃぐちゃにしちゃった経験に起因してるのかなって思ってます。保守性の高いCSSって語られる機会があまりないし、ぐぐってもSCSS万歳みたいな記事しか出てこなかった。

デザインが画一化するとインターネットがつまらなくなるので、こういう感じでCSSの保守性を高める自分なりのメソッドを見つけて、独自のCSSを楽しく書く人が増えればいいなって思います。

あと、CSSのレンダリング速度がどうしても気になる人は、「プロダクトが完成してから」リファクタリングを行いましょう。

あと、メタ言語にstylusを採用したら光属性ライブラリnibはチェックしましょう。global-reset()とか、border-radiuslinear-gradient()といった先進的実装をベンダープレフィックスで自動展開とか、clearfixの機能なんかを拡張してくれます。

以上です。インターネットの平和を願って。やみのま。

← stylusでCSS吹き出しを簡単に実現するfukidashi.stylつくりました stylusでCSS吹き出しを簡単に実現するfukidashi.stylつくりました →