脱入門!わかりやすさ、パフォーマンスにこだわった CSS の書き方 研修コースに参加してみた
今回参加したコースは 脱入門 わかりやすい、パフォーマンスを落とさない CSS の書き方 です。
Web サイトは、主に HTML でコンテンツを書き、 CSS でスタイルを指定して作ります。
CSS そのものは Web ブラウザで手軽に始められるため敷居は低いのですが、入門レベルを脱しようとすると、どんな順番でレイアウトが決まって表示されるのか、どうすると !important
地獄にならないのか、どう名前や構造をつくるとわかりやすいのか、書き方や設計を知る必要がでてきます。
このコースでは、そもそも Web のパフォーマンスとはなにか、その中でパフォーマンスを落とさない書き方、また、チームでメンテンスするときに、どうルールを作るとわかりやすいのか、 CSS の設計手法のこれまでとこれからを学びました。
では、コースの内容をレポートします!
もくじ
コース情報
前提知識 | 基本的な HTML と CSS が書ける方 |
---|---|
受講目標 | パフォーマンスとメンテナンスを考慮した CSS の書き方や設計がわかる |
講師紹介
このコースで登壇されたのは 米山 学さん です。
Java はもちろん Python / PHP などスクリプト言語、 Vue / React など JS だってなんだってテックが大好き。原点をおさえた実践演習で人気
HTML などのコースでは過去に、「 モダンコーディング(HTML5 / CSS3)入門と WebSocket など新技術」に登壇されています。
余談ですが、↑ のコースで紹介いただいた HTML5 で登場した技術、 WebStorage や WebSocket が最近、役に立ちました。研修に参加してよかったです(役得)。
パフォーマンスを気にした書き方
このコースは、脱初級を狙い、主にパフォーマンスと、わかりやすさ( CSS の設計手法)の、 2 つをテーマにしています。まずは、 CSS のパフォーマンスから始まりました。
Web におけるパフォーマンスとは
まずは Web の世界では切っても切れないパフォーマンスの基礎知識から。
- パフォーマンスで問題になること
- ユーザの離脱
- 特に初回ユーザはロードが遅いと 3 秒で 6 割が離脱(昔は 8 秒)
- 0.8 秒パフォーマンス良くすると、 1 割コンバージョンが上がる(!!)
- SEO にも影響
- 2018 年に Google が検索ランキングの指標としてサイト・スピードを加えた
では、そのパフォーマンスの良し悪しはどのように判断されるのでしょうか。
- パフォーマンスの指標
- W3C が定めている
- Perceived load speed: ページがどれぐらいのスピードでロードされるか
- Load responsiveness: JS などのコンポーネントがどれだけスムーズに動くか
- Runtime responsiveness: そのコンポーネントがどれぐらいの時間で動かせるようになるか
- Visual stability: ユーザの意図に反した動きになっているか。ユーザの行動を邪魔していないか
- Smoothness: 動きのあるアニメが滑らかに動いているかどうか
- メトリクス
- FirstCP: 一部が表示されるまで
- LastCP: 最大のテキストや画像ブロックが表示されるまで
- TTI: コンポーネントが動くまでの時間
- FID: インタラクティブな操作に対して応答している時間
- TBT: FCP + TTI
- 計測ツール
- Lighthouse
- Google 製。Chrome の拡張ツールだけでなく、 Chrome の DevTools にもある
- Google 製。Chrome の拡張ツールだけでなく、 Chrome の DevTools にもある
- W3C による計測用の API ( JS 製) がある
- Lighthouse
- W3C が定めている
- Chrome DevTools
- 色々なものが計測できるようになっている
- 使い方は Get Started With Analyzing Runtime Performance | Chrome DevTools で詳しく
Web のレンダリングの流れ
パフォーマンスについてわかったところで、そもそも web ページの表示の流れ(レンダリング)について見てみます。
クリティカルレンダリングパス
- HTML / CSS / JS / 画像などが最終的に表示されるまでの流れ
- 間違えやすいが レンダリング Rendering とペインティング Painting にわかれる
- Rendering が準備
- Painting が描画
流れ
- loading
- ダウンロードして解析
- HTML は DOM ツリーに、 CSS は CSSOM( CSS Object Model )に
- scripting ( JS の実行が終わる)
- ここが終わらないとレンダリングに入らない
- 並列処理やバックグラウンド処理をしよう
- レンダリング
- レンダリングツリーを構築
- 詳しいことはのちほど
- CSS のオブジェクトモデル( CSSOM )はキャッシュされてない
- CSSOM 構築が終わらないと、レンダリングは始まらない
- レンダリングをブロックするものと、しないものをわける
- 絶対に必要な軽量 CSS 、遅れても構わない巨大 CSS にわける
- レンダリングツリーの構築
- ルールセット ex.
h1 { font-size: 2em; }
- セレクタ(場所: h1)とプロパティ(振る舞い: font-size)
- ルールセットは右から左に解析・マッチングされる
- クラスでレンダリングできるようになるほうがよい
- ルールセット ex.
- レイアウト
- 大きさや場所の計算をする
- ボックスモデル( margin や border, padding )の計算が終わったらレンダリング
- DOM 要素の座標が変わったときに再レンダリング
- eg. ウインドウサイズがかわる
- DOM ツリーの変化
- ボタンのクリック、フォームの入力など
- 再計算させない工夫が必要
- 再計算しても再レンダリングしない条件がある
- 再計算しても、発生する場所を限定するとできる
この中でも気をつけたいところが、
ルールセットは右から左に解析・マッチングされる
です。以下のサンプルで考えてみます。
article > div.logo {
/* style */
}
-
ルールセットの解析・マッチングの流れ
- 一番右の logo
- div かどうか
- article の直下かどうか
つまり article p { /* style */ }
としてしまうと、 p のたびにマッチングしにいってしまいます。。
ついつい場所だけを気にして、、うっかりやらかしがちです。もちろん私はやらかしています。
CSS パフォーマンスチューニング
では、 CSS のパフォーマンスチューニング手法にはどのようなものがあるのでしょうか。
なお、最近ではハードウェアやネットワークの性能向上、 Web ブラウザが高性能され、セレクタの最適化などでパフォーマンスが落ちることは少なくなっています。
-
CSS のファイルサイズの削減
- Minify する
- 未使用/重複のルールセットの削除
- ショートハンドプロパティ( background-color より background )
- いろいろあるが Minify が一番効く
- Minify ツールなどは 10 Best Tools for Formatting and Optimizing CSS Files – Geekflare によくまとまっている
- CSS Lint はいい感じに効く
- DOM 要素 (div h1 のようなタグの数) × ルールセットでマッチング( = 計算)が行われる
- ネストを深くしすぎない
- BEM を活用しよう
- マッチングの速さ
- id (#hoge)
- class (.hoge)
- tag div
- Adjacent sibling (h2 + p)
- Child (li > ul)
- Descendant (ul li)
- Universal (*)
- Attribute ([type=”text”])
- 擬似クラス :hover
- とにかくコンビネータを使わない
セレクタの最適化
/* NG */
/* td は table 要素の下なので、tbody など書く必要がない */
table > tbody > tr > td {
/* style */
}
/* Good */
td {
/* style */
}
-
パフォーマンス劣化のあるプロパティを使わない
- border-radius
- box-shadow
- transform
- filter
- :nth-child
私は、角丸大好き! shadow こだわりたい派です!だったのですが、辞めねば。。
-
スタイルシートの読み込み
- インライン
- 外部リソースの読み込みがないので速い
- ブラウザのキャッシュが効かない
- 外部リソース化
- link タグ以降は DOM 構築に影響しない
- レンダリングがブロックされる
- JS による CSS injection (非同期化)
- レンダリングをブロックしない
- スタイルがあたってないものが表示される可能性がある
- media タグを使って非同期にする
-
<link href="css/style.css?d=5000" rel="stylesheet" media="print" onload="this.media='all'">
- onload を入れて、 print から all に変えている
- 外部リソースが多い場合、逆に遅くなる可能性がある
rel="preload"
にすると非同期にできる- Firefox などがサポートできてない
-
速さは最高の UX の土台とも称された dev.to は <link>
を使わず、 head 内にインラインでスタイルを書いています。
とはいえ、いずれの手法にしても、ボトルネックを確かめて問題を切り分けてからチューニングしましょう。有名な「推測するな計測せよ」ですね。
- Scripting に時間がかかっているようなら CSS チューニングの影響は少ない
- Rendering に時間がかかっているなら効果がある可能性が高い
わかりやすいセレクタの書き方やルール( CSS 設計手法)
CSS を書くとき、一度は必ず悩む「どのようにセレクタを書くと、わかりやすいのか」です。カードっぽいから card ? テーブルを覆うタグだから table-container ?
この書き方には、過去から色々な構造化、設計手法が提案されています。
CSS 設計が必要な理由
- 名前空間がない(= グローバルに適用されてしまう)
- 予期しないスタイルがあたってしまう
- あたるべきスタイルが効いてない
もう、ホント、ページや場所ごとに閉じて欲しい。。です
CSS 設計の原則
- 予測しやすい(名前でわかりやすい)
- 再利用しやすい
- 拡張しやすい
このあたりはプログラミングにおける変数名、関数名などにも通ずるお話ですね。ネーミングセンスつけたい(いい名前をマネするぞ)。
CSS 設計手法
歴史的には 3 つあります。
- OOCSS
- SMACSS
- BEM
それぞれどのような設計手法が見ていきましょう。
- OOCSS
- オブジェクト指向を使う
- 多くのCSS設計の基礎になっている
- 拡張しやすいが、変更による影響範囲が大きい
- OOCSS のやり方
- 構造と見た目を分離
- 適用前
<!DOCTYPE html>
<html>
<head>
<style>
#main .btn-general {
display: inline-block;
width: 300px;
max-width: 100%;
padding: 20px 10px;
background-color: darkslateblue;
box-shadow: 0 3px 6px rgba(0, 0, 0, .16);
color: whitesmoke;
font-size: 18px;
line-height: 1.5;
text-align: center;
}
#main .btn-warning {
display: inline-block;
width: 300px;
max-width: 100%;
padding: 20px 10px;
background-color: red;
box-shadow: 0 3px 6px rgba(0, 0, 0, .16);
color: yellow;
font-size: 18px;
line-height: 1.5;
text-align: center;
}
</style>
</head>
<main id="main">
<button class="btn-general">General Button</button>
<button class="btn-warning">Cancel Button</button>
</main>
</html>
- 適用後
<!DOCTYPE html>
<html>
<head>
<style>
/* Structure */
#main .btn {
display: inline-block;
width: 300px;
max-width: 100%;
padding: 20px 10px;
box-shadow: 0 3px 6px rgba(0, 0, 0, .16);
font-size: 18px;
line-height: 1.5;
text-align: center;
}
/* Skin */
#main .general {
background-color: darkslateblue;
color: whitesmoke;
}
#main .warning {
background-color: red;
color: yellow;
}
</style>
</head>
<main id="main">
<button class="btn general">General Button</button>
<button class="btn warning">Cancel Button</button>
</main>
</html>
- ボタンという構造を変えずに、 Skin を追加しやすい
- main を外すともっとよい
- コンテンツとコンテナを分離
- 特定のエリアだけに留まらない
はい、、、わたしは最近、いちいち書くのがアレだったので、特定ページのボタン要素に background で色を入れました。。
SMACSS
これはだいぶ古いやり方とのこと。。
-
段階的にスタイルを適用
- Base
- Layout
- Module
- State
- Theme
これにより影響範囲がわかりやすいとのことで、確かに見通しが良くなりそうですね。
- Base
- html body form など
- Layout
- header main footer section など
- ページ内で 1 つしか使用されない
- 今はもう Base に入れてもよいだろう
- Module
- Layout 内に配置されるサブ・モジュール
- ボタンやカード、ナビゲーション、カルーセルなど
- State
- 特定の状態になったときのスタイル
- アクティブなど
- 接頭詞には is- を入れると良い
- is-error
- 特定の状態になったときのスタイル
- Thema
- ページごとにスタイルを変えたい場合
BEM
この BEM が現在、多くの企業、 CSS フレームワークで使われているとのことでした。
- Block / Element / Modifier の 3 つで命名規則を分ける
- Block
- パーツ・モジュール
- 何を表しているかでクラスの名前を決める
- NG: red-text
- Good: error
- Element
- ブロックの中の要素
- e.g. menu__item
- メニューの中の項目
- ネストはオススメしない
- NG: menu__item__link
- good: menu__link
- Modifier
- 見た目や振る舞い、状態を表す
- ブロック要素を継承して
_
でつなげる - 単独で使用せず、ブロックと一緒に使う
- NG: button_small
- Good: button button-small
- 単語の組み合わせのときに
-
を使う- button_small color-thema_caution
- 名前から意図しない振る舞いを入れず、責任範囲を保つ
.button_bg-color_red {
width: 200px; /* 名前から width が入っていると思わない */
}
- デメリット: 記述量が増える。。。
最後の BEM はやっぱりわかりやすく、確かに定義することがお多そうですが、マネしやすそうです!
Lint を使おう
あとは脱入門ということで、チームでのコーディングも意識して、 Lint ツールを紹介いただきました。
- 問題箇所やルールから外れるときにアラートなどを出してくれる
- Linter を使うといいよ
- stylelint というツールがいいよ
欲張りですが、 CSS フレームワークごとに Lint ツールがあるといいなぁ(願うのは自由)。
もっとも最新の CSS in JS に触れる
ちょっとお腹いっぱい気味だったのですが、よくインターネットで見かけるやつが出てきたので、もうちょっと頑張ります。
- JS で CSS を記述する( どゆこと? )
- 名前空間を閉じることができる ( !! )
- styled-compornents が代表的なライブラリ
- HTML と 振る舞い ( JS ) にスタイルを入れたコンポーネント集
- 書き方
function tag(array, arg) { return "tag():" + array[0] + arg } const val = 100; console.log(tag`abc${val}`);
今回は React でやってみます。
米山さんに用意いただいたサンプルコードを見てみます。
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/react-is@16.13.1/umd/react-is.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
</head>
<body>
<div id="myapp" />
<script type="text/babel">
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
ReactDOM.render(
<Wrapper>
<Title>
Hello React!
</Title>
</Wrapper>,
document.getElementById('myapp')
);
</script>
</body>
</html>
body 内のコンテンツは JSX という記法で書かれていますが、なんとなく雰囲気がわかりますね。
- 実行結果
ここからコンポーネントを拡張してみます。
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/react-is@16.13.1/umd/react-is.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
</head>
<body>
<div id="myapp" />
<script type="text/babel">
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
const Button = styled.button`
background: ${
props => props.primary ? "palevioletred" : "white"
};
color: ${
props => props.primary ? "white" : "palevioletred"
};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
ReactDOM.render(
<Wrapper>
<Title>
Hello React!
</Title>
<Button>Normal</Button>
<Button primary>Primary</Button>
</Wrapper>,
document.getElementById('myapp')
);
</script>
</body>
</html>
- 実行結果
このレベルなら React も行けるかと思えます。 が、ステート管理が、 Redux が、とか出てくると、、、
と、この最新の CSS に触れたところで、このコースは修了しました。
まとめ
このコースでは、 CSS について脱入門できるよう、 Web ブラウザの仕組みとパフォーマンスやわかりやすさと設計手法を学べました。
CSS はググればなんとか書けてしまう面がありますが、パフォーマンスを考慮した良い書き方や、チームで開発したり、スクラッチで定義したりするようになるには、断片的にググった知識だけでなく、体系的に学ぶ必要があると感じました。
Web コーディングで入門レベルからステップアップしたい方や、本職ではないもののフロントもある程度コーディングできる方には、とてもオススメです!
SEプラスにしかないコンテンツや、研修サービスの運営情報を発信しています。