どうも、Webデザイナーですよ。
デザイナーの皆様、CSS、どうしてます?多分悩んでたり、うやむやなままの人が多いかなと思ってちょっと記事を書いてみますね。
フロントエンドエンジニアの方やマークアップエンジニアの方には非推奨な書き方かも。
個人的な部分が結構含まれてます。
前提として
Webデザイナーがコーディングするのってそこまで大規模なものにはならないんですよね。
何故かというと、大規模なものになるとデザイン作業とディレクターとのやり取りで手一杯になり、コーディングする余裕がなくなるから。
そうなると、コーディングを外注や社内コーダーに投げて、デザイナーはデザインに専念するというわけですよ。分業ですね。
しかし、小規模なもの(簡単なブランドサイトやLP等)はデザイナーがコーディングすることが多々あります。この時、CSSの書き方を自由に書いちゃっていいのか考えちゃうかと思います。
というわけで、小規模コーディングのときのCSS設計について。
(注:大手企業などのちゃんとしてる企業ではコーディングガイドラインがあるはずなのでそれに従ってください)
CSSの理想とOOCSS
そもそも、CSSの理想ってなんだろう。
それは、「再利用でき」、「わかりやすく」、「無駄がなく」、「破綻しない」ものだと思うわけですよ。
再利用ができれば、同じスタイルを何度も書くことがなくなり、楽になる。
わかりやすければ、メンテナンスや修正がしやすい。
無駄がなければ、ファイルのサイズが小さくなり、メンテナンスもしやすい。
破綻しなければ、意図しないプロパティがつくことがない。
そんな理想的な書き方あるかー!って話だけど、近づけることはできる。OOCSSの出番です。
OOCSS
簡単に言うと、CSSを塊1つ1つとして扱って、それを組み合わせて作っていく考え方。
わかりやすい話、レゴみたいなもの。
例として・・・
<div class="item-red"></div> <div class="item-blue"></div>
.item-red { position: relative; margin-top: 10px; color: #fff; background-color: red; } .item-blue { position: relative; margin-top: 10px; color: #fff; background-color: blue; }
これって同じこと2回書いてるますよね。これは無駄だし、再利用できてない。
なのでこうしましょう。
<div class="item item-red"></div> <div class="item item-blue"></div>
.item { position: relative; margin-top: 10px; color: #fff; } .item-red { background-color: red; } .item-blue { background-color: blue; }
スッキリ!例えば新しくグリーンを追加したいなら、
<div class="item item-red"></div> <div class="item item-blue"></div> <div class="item item-green"></div>
.item { position: relative; margin-top: 10px; color: #fff; } .item-red { background-color: red; } .item-blue { background-color: blue; } .item-green { background-color: green; }
こうするだけでいい。楽だし、わかりやすいし、再利用してる。
また、Sassを使っているとこのOOCSSの良さがわかる。
Sassって立ち上げに少し手間がかかるのよ。タスクランナー起動したり。
だから、極力Sassファイルを編集せずに反映してあるプロパティを変更できれば楽。
OOCSS、便利!
その他、基礎的なところとして
CSSはIDを参照しない
#header ul li { ... }
こういうのはNG。というのも、OOCSSの考え方として再利用があるけど、id名を参照してしまうと再利用できなくなる。
そのため、CSSは全てクラス名で指定すること。idはjsが参照する箇所やスムーススクロールなどで使う最低限で留める。
小規模なコーディングでは汎用CSSは使わない
ここでいう汎用CSSとは
.mt10 { margin-top: 10px !important; }
こういうやつ。こういった汎用CSSは大規模開発のときには便利。チーム全体でのCSSの組み方が統一されるからね。汎用をまとめたCSSファイルを1つ作成しておいて、importで使用っていうのはあるある。
ただ、小規模となるとデメリットのが多い。この汎用CSSはOOCSSの考え方とミスマッチする場合がある。
例えば
<div class="item item-red mt10"></div> <div class="item item-blue mt10"></div> <div class="item item-green mt10"></div>
.mt10 { margin-top: 10px !important; } .item { position: relative; color: #fff; } .item-red { background-color: red; } .item-blue { background-color: blue; } .item-green { background-color: green; }
こういう状況で、やっぱ上の隙間20pxにしたいなあ、となった時、htmlを3箇所変える必要がある。
それに対して、OOCSSだけで書いている場合は.itemのmargin-topの1箇所を変えればいい。こっちの方が楽。
また、mt10のようなものがたくさんあると、理想のCSSでいう「無駄がない」を満たすことができない。そうした意味でも非推奨。
親のセレクタやクラスに依存する指定はしない
これはSassを使わないで普通にCSSを使っているときに、とくに注意してほしいんだけど、
デザインに凝れば凝るほど、なんだかんだdivが重なっていくことが多い。そうした中で、
.header {...} .header .inner{...} .header .inner .ttl {...} .header .inner .ttl span {...}
こういう指定をすると、もし
「headerが増えたから今の.headerを.header_01に変更したい」
ってなったときに4箇所も直さなきゃいけない。
Sassの場合はネストで組めるから1箇所で済んだりするけど、CSSだと大変!
なので、特に理由のない依存はしないようにしましょう。
余計なセレクタを付属させない
p.header_text-red {...}
これだとpは必要ないので、外しましょう。
書いてあった方が多少わかりやすくなる部分はあるけど、クラス名でも十分わかりやすくできるし、他への流用ができなくなる。
クラス名について
クラス名の書き方は大きく4つに分かれる。
キャメルケース
単語同士の繋ぎを大文字で区別する記法。
例)itemBlock、itemListInnter、sectionBlock
【メリット】後述するスネークやハイフン、BEMと比べると短くでき、かつダブルクリックで全選択できること。
【デメリット】I(iの大文字)とl(lの小文字)の区別がつきにくく、可読性が下がること。また、ダブルクリックで単語1つを選択することができない。
スネークケース
単語の繋ぎをアンダースコアで区別する記法。
例)item_block、item_list_inner、section_block
【メリット】可読性が高い。かつダブルクリックで全選択できること。
【デメリット】クラス名がちょっと長くなる。ダブルクリックで単語1つを選択することができない。
ケバブケース
単語の繋ぎをハイフンで区別する記法。ケバブケースっていう名前なのは今調べて初めて知った・・・。
例)item-block、item-list-inner、section-block
【メリット】可読性がそこそこ高い(スネークよりは劣る)。ダブルクリックで単語1つを選択できる。
【デメリット】ダブルクリックで全選択できない。jsとの連動がスムーズじゃない。
BEM、及びBEMのカスタム記法
block__element–modifier、block–modifier__elementの書き方。
block、element、modifierが何っていうのはこちらの記事が参考になります。
https://qiita.com/usagi-f/items/b4e56e765384c49d5d04
なんのこっちゃって人向けに解説すると、
<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>
いやイマイチこれにする理由がわからんって人向けに個人的な見解をすると、これ、Sass用記法だと思ってる。
Sassには親セレクタを参照する「&」の機能がある。
.block {...} .block__element {...} .block__element--modifier {...} .block--modifier {...} .block--modifier__element {...}
上が生のCSS。長いし、これにする理由がわからない。
ただ、Sassにすると・・・
.block { ... &__element { ... &--modifier { ... } } &--modifier { ... &__element { ... } } }
すごくわかりやすくなる。BEMの説明は終了。
さてさて、キャメルケース・スネークケース・ケバブケース・BEMを紹介してきたけど、個人的な意見としては
「生のCSSで書くならスネーク、Sassを使うならBEMを推奨」です。
個人的にクラス名に望むことって
1 可読性があること
2 javascriptとの連動がしやすいこと
3 短いこと
なので、その3つを考慮するとスネークになっちゃうんだよね。だから、基本はスネーク推奨。
ただし、Sassを使うとなるとSassファイル内の見易さ的にBEMのが優位性が高いと思ってます。
だから「生のCSSで書くならスネーク、Sassを使うならBEMを推奨」としています
自分はSassを使うことが多いからBEMをよく使ってますね。
生のCSSしか学習してない人へは以上です。スネークがいいよ!OOCSS便利だよ!!ってことで。
後述は自分がたどってきたCSS奮闘記。
BEMの記法は長い
.block__element–modifier、.block–modifier__element。
わかりにくいし、長いわっ!!!
ってことで自分なりの解釈でカスタムしましょう。ここに関してはみんなオレオレが入ってると思う。
自分の場合、こんな感じ。
<section class="sec sec-01"> <div class="sec_inner"> <h1 class="sec_ttl sec_ttl-l">...</h1> <h2 class="sec_subttl sec_subttl-s">...</h2> <p class="sec_txt sec_txt-01 sec-01_txt-01"></p> <p class="sec_txt sec_txt-02"></p> </div> </section>
要するに、数字と状態を示す単語はハイフンにしたいんですよ。ダブルクリックで選択したいから。
なので、largeを示す「l」とかsmallを示す「s」、「red」とか数字はハイフンで区切りたい。
それ以外の、ブロックとエレメントの間はダブルクリックで一括選択したいからアンダースコア区切りにしたい。
そうすると、こうなるわけです。
例)sec_txt-01、.sec-01-txt-red
でも、クラス名を書いていくときに、「1つ前が数字だったからハイフン」とか考えるの、めんどくさいじゃないですか。
なので、少し妥協して、今から書こうとしている単語が数字か状態ならハイフン、それ以外ならアンダースコアにしてます。
例)sec_txt-01、.sec-01_txt-red
Sassにするとこんな感じ
.sec { ... &_inner { ... } &_ttl { ... &-l { ... } &-s { ... } } &_subttl { ... &-l { ... } &-s { ... } } &_txt { ... &-01 { ... } &-02 { ... } } } .sec-01 { ... &-txt { ... &-01 { ... } } }
どうでしょうか。うん、わかりやすいし短い。
いやでもさあ・・・
<p class="sec_txt sec_txt-01 sec-01_txt-01"></p>
これ、長くね??クラス多すぎて管理大変じゃね???って思いますよね。そうですよね。ここにさらに加わったりしてくるとメンテナンス大変になってくるわけですよ。プロパティを上書きとかしてるともう地獄。さらに上から新しいクラス付けてとかしたらもう魑魅魍魎です。
でも、短くすると使いまわしができなくなる。どうすればいいのかと。
そこでSassのextendとmixinの出番。
<section class="sec-01"> <div class="sec_inner"> <h1 class="sec_ttl-l">...</h1> <h2 class="sec_subttl-s">...</h2> <p class="sec-01_txt-01"></p> <p class="sec-01_txt-02"></p> </div> </section>
@mixin secTxt01($Fsize:20px, $Fcolor:#000){ ... } @mixin secTxt02($Fsize:16px, $Fcolor:#000){ ... } .sec { ... &_inner { ... } &_ttl { ... &-l { ... } &-s { ... } } &_subttl { ... &-l { ... } &-s { ... } } &_txt { ... &-01 { @include secTxt01(); ... } &-02 { @include secTxt02(); ... } } } .sec-01 { @extend .sec; ... &-txt { ... &-01 { @include secTxt01(28px, #ddd); ... } &-02 { @include secTxt02(20px, #ddd); ... } } }
mixinとextendって、OOCSSの考え方をうまいこと反映してくれるんですよね。
ちなみに、上記だけならmixin使わなくてもextendで
.sec { ... &_inner { ... } &_ttl { ... &-l { ... } &-s { ... } } &_subttl { ... &-l { ... } &-s { ... } } &_txt { ... &-01 { ... } &-02 { ... } } } .sec-01 { @extend .sec; ... &-txt { ... &-01 { @extend .sec_txt-01; ... } &-02 { @extend .sec_txt-02; ... } } }
とすれば同じなんですよ。でもね、ネストでつなげたクラス名をextendで参照するの、辛いよね。
ただ、mixin自体も便利だけどこれはこれで頭使うからどっちが辛いのかってなんともいえないと思うけど。。
これで決まり!と思ったけど
実際、Sass書いてCSSにコンパイルして、実機で確認するわけだけど、まあ、大なり小なり調整が必要なわけですよ。
そうした時に、CSSの場合は検証ツール開いて何行目のスタイルが当たっているのか確認して、CSSファイル開いて、同じ行数のとこいって修正すれば終わる話なんですけど、Sassの場合はそうはいかない。
@mixin secTxt01($Fsize:20px, $Fcolor:#000){ ... } @mixin secTxt02($Fsize:16px, $Fcolor:#000){ ... } .sec { ... &_inner { ... } &_ttl { ... &-l { ... } &-s { ... } } &_subttl { ... &-l { ... } &-s { ... } } &_txt { ... &-01 { @include secTxt01(); ... } &-02 { @include secTxt02(); ... } } } .sec-01 { @extend .sec; ... &-txt { ... &-01 { @include secTxt01(28px, #ddd); ... } &-02 { @include secTxt02(20px, #ddd); ... } } }
こう書いてるから検索できないんですよ。なんてこった。
どうすればいいんや・・・って悩んでいたら救世主発見。ソースマップ様です。
簡単にいうと、検証ツールで「cssのこの行のが反映されてるで」っていうのを「Sassのこの行が反映されてるで」ってしてくれる。超有能。
自分の場合はgulpをもっぱら使用している(デザイナーには多分一番とっつきやすいタスクランナー)から、gulp-sourcemapsをインストール。
インストール方法とかはびゃす様のブログにわかりやすく書いてあるので省略します。
https://yusukegoto.com/2234/
これでほとんど全ての問題がクリアされました。(ソースマップ使ってもextendの使いにくさが少し気になりますが)
というわけで、今の自分は
「クラス名はBEMのカスタムしたものを使用。Sassで書いていく。Sassのコンパイル及びソースマップ制作はgulp。」
となっています。
補足
プロパティについてはEmmetを使用してショートカットして書いてます。
なので厳密には
「クラス名はBEMのカスタムしたものを使用。SassとEmmetで書いていく。Sassのコンパイル及びソースマップ制作はgulp。」
ですね。
今後の自分の課題
Sassっていろいろな機能があって非常に便利なんですけど、for文を未だに1回も使ったことないんですよね。Emmetで済ませちゃうというか。
もっと最適化したい。
では。