CSS 設計: FLOCSS と BEM について - 破綻しにくい CSS 命名規則
良い CSS 設計とは? (Philip Waltson)
-
予測しやすい: クラス名を見るだけでどんなクラスであるかを大体理解できる.
-
再利用しやすい: プロジェクト中の複数箇所で必要になった際に使い回すことができる.
-
保守しやすい: スタイルが重複したり、意図せずにスタイルが当たったりしない.
-
拡張しやすい: 複数人での作業を全体に、誰が作業したとしても同じような品質のコードを得るために共通のルールを定める.
FLOCSS: Foundation Layout Object CSS (フロックス)
-
CSS を 3 つのレイヤーと 3 つの子レイヤーに分けて管理する.
-
分類ごとにファイルを分けて管理する.
-
|
|- Foundation : 基本的なスタイル (リセット CSS、基本指定、変数定義、ファンクション etc.)
|- Layout : サイト共通で使用するブロック (ヘッダー、メイン、フッター、サイドバー etc.)
`- Object : 再利用可能なモジュールを 3 つの子レイヤーに分けて管理.
|- Component : 最小単位のモジュール (ボタン、タイトル、フォーム、アイコン etc.)
|- Project : 複数の Component やそれに該当しない要素 (プロフィール、カード、モーダル etc.)
`- Utility : スタイルの調整のための便利クラス
/sass
|
|- /foundation
| |- _base.scss
| |- _variables.scss (サイト全体で使える変数)
| |- _mixin (サイト全体で使える mixin を管理)
| `- _reset.scss
|- /layout
| |- _header.scss
| |- _main.scss
| |- _footer.scss
| `- _sidebar.scss
`- /object
|- /component
| |- _button.scss
| `- _tab.scss
|- /project
| |- _profile.scss
| `- _articles.scss
`- /utility
|- _margin.scss
`- _position.scss
Foundation
-
基本的なスタイルを定義する. リセット CSS やサイト内で使用する色の定義、ミックスインなど.
-
リセット CSS
-
基本の指定
-
変数の定義
-
ファンクション
-
etc.
-
// リセット CSS
* { margin: 0; }
// 基本の指定
body, a
// 変数の定義
:root {}
$primary : #0cf;
// ファンクション
@function, @mixin
Layout
-
サイト共通で使用するブロック (単位で唯一の存在である要素) を定義する.
-
ヘッダー
-
メイン
-
フッター
-
サイドバー
-
etc.
-
// ヘッダー
l-header
// メイン
l-main
// フッター
l-footer
// サイドバー
l-sidebar
Object
-
再利用可能なモジュールを 3 つの子レイヤーに分けて管理する.
Component
-
最小単位のモジュール (ボタン、タイトル、フォーム、アイコン etc.) を定義.
// ボタン
c-button
// タイトル
c-title
// フォーム
c-form
// アイコン
c-icon
Project
-
複数の Component やそれに該当しない要素 (プロフィール、カード、モーダル etc.) を定義.
-
複数の Component を組み合わせることで作ったパーツを Project と呼ぶ.
-
// プロフィール
p-profile
// カード
p-card
// モーダル
p-modal
Utility
-
スタイルの調整のための便利クラスを定義. マージン、ポジション、揃え位置、サイズ etc.
// マージン
u-margin
// ポジシヨン
u-position
// 揃え位置
u-align
// サイズ
u-size
-
この他、 Object の子レイヤーとして
Themeが含まれる場合もある.-
テーマによる色の切り替えや、ページ単位の色違いなどの設定.
-
プリフィックスとしては
t-を用いてt-blue.scssのようにファイルを命名する.
-
FLOCSS の命名規則
-
各レイヤーのアルファベットの頭文字とハイフンをクラス名の先頭に付与する.
-
ただし、Foundation は例外であり、命名規則のルールからは除外される.
-
したがって、Foundation は
f-のような接頭辞を付与せずに記述する.
-
// Layout レイヤーに配置されるクラスは l- を接頭辞として付与する.
.l-header {
background: #fff;
}
.l-main {
flex: auto;
}
// Component レイヤーに配置されるクラスは c- を接頭辞として付与する.
.c-button {
font-size: 1rem;
}
.c-title {
font-size: 1.5rem;
}
// Project レイヤーに配置されるクラスは p- を接頭辞として付与する.
.p-profile {
background: #ccc;
}
.p-article {
border: 1px solid;
}
// Utility レイヤーに配置されるクラスは u- を接頭辞として付与する.
.u-mt10 {
margin-top: 10px;
}
.u-df {
display: flex;
}
-
このような命名規則により、クラス名を見るだけでどのファイルに定義されている、どんな用途のクラスなのかがだいたい分かるようになる.
-
なお、JavaScript で操作して状態が変更されることを表すためには、FLOCSS では
is-をプレフィクスとするクラスを使用する.-
例えばクリックされた時の状態を表すクラスとして
is-clickといったクラスを使用する.-
この
is-clickクラス自体にはスタイル情報は全く入っておらず、単なるマーカー目的で使用する.
-
-
他のオブジェクトの状態変化に対して誤ってスタイルを当ててしまうことが無いように、
is-click自体に対してはスタイルを定義しない. -
例えば Component であるボタンに対してクリック時のスタイルを指定したければ、
.c-button.is-clickのようにスタイルを定義する.-
.c-buttonと.is-clickの 2 つのクラスを両方持つ要素に対してだけスタイルが設定される.
-
-
BEM: Block Element Modifier (ベム)
-
Block 、 Element 、 Modifier という 3 つの要素を組み合わせて使用する命名規則.
- Block : 大きな括り. 大枠.
- Element : Block 中の要素.
- Modifier : Block、Element のパターン.
<div class="block">
<div class="block__element">
コンテンツ
</div>
<div class="block__element">
コンテンツ
</div>
<div class="block__element--modifier">
コンテンツ
</div>
</div>
-
Block は大枠を表しており、Block 中の要素は Element として扱われる.
-
Block と Element はアンダースコア 2 つ
__で繋げられる.-
Element 中にさらに Element が現れる場合もアンダースコア 2 つ
__で繋げられる.
-
-
-
Block や Element についてスタイルを変えたい場合 (パターンを増やしたい場合) には Modifier を使用する.
-
Modifier はハイフン 2 つ
--の後ろに記述する. -
なお、Modifier の後ろに
__で Element を付与することは禁止.-
よって、
cardtext—inverseは OK だが、card—inversetextは禁止.
-
-
<!-- Block は大枠 -->
<article class="card">
テキスト
</article>
<!-- Block 中の要素は Element として扱われる -->
<article class="card">
<div class="card__text">
<h3 class="card__text__title">
タイトル
</h3>
</div>
<!-- Block や Element のスタイルを変えたい場合には Modifier を使用する -->
<article class="card">
<div class="card__text">
<h3 class="card_text__title">
タイトル
</h3>
</div>
<div class="card__image--round">
<img src="">
</div>
</article>
<article class="card--inverse">
<div class="card__text">
<h3 class="card__text__title">
...
</h3>
</div>
</article>
-
なお、 Block や Element 名が 2 単語以上となる場合には、
単語-単語のように区切り文字としてハイフンを使用する. -
BEM を使った場合と使わない場合の比較
-
BEM を使うとぱっと見たときに構成がわかりやすくなる.
-
<header class="header">
<a class="logo">
<img src="logo.svg">
</a>
<nav class="nav">
<a class="item">会社概要</a>
<a class="item">サービス</a>
<a class="item accent">お知らせ</a>
</nav>
</header>
<header class="header">
<a class="header__logo">
<img src="logo.svg">
</a>
<nav class="header__nav">
<a class="header__nav__item">会社概要</a>
<a class="header__nav__item">サービス</a>
<a class="header__nav__item--accent">お知らせ</a>
</nav>
</header>
-
サイトの規模が大きくなればなるほど BEM の恩恵は大きくなる.
-
クラス名の重複を防ぎ、どこにあるどんな要素か想像できるようになる.
-
ただし、クラス名が長くなってしまう.
-
-
BEM も FLOCSS と同様に Sass との相性が良いため、上記のスタイルは Sass では非常にわかりやすく記述することができる.
-
HTML の構造とスタイルシートの構造がほぼ対応するようになり、読みやすくなる.
-
.header {
&__logo {
img {
height: 24px;
}
}
&__nav {
&__item {
color: #000;
&--accent {
color: #ff0000;
}
}
}
}
<header class="header">
<a class="header__logo">
<img src="logo.svg">
</a>
<nav class="header__nav">
<a class="header__nav__item">会社概要</a>
<a class="header__nav__item">サービス</a>
<a class="header__nav__item--accent">お知らせ</a>
</nav>
</header>
Sass では & で 親要素セレクタ自体 を参照することができ、 { } の中で更に { } を記述する際にセレクタ名の一部として使用することができる.
(https://prograshi.com/design/css/ampersand-in-selector/)
.test {
&--red {
color: red;
}
}
.test-red {
color: red;
}
FLOCSS と BEM の併用
<header>
<a>
<img src="logo.svg">
</a>
<nav>
<a>会社概要</a>
<a>サービス</a>
<a>お知らせ</a>
</nav>
<div>
<a>お問い合わせ</a>
</div>
</header>
-
まず FLOCSS ではヘッダーは Layout の要素に分類される.
-
FLOCSS のルールに従い、
l-headerというクラス名が付与される.
-
-
ヘッダー内に配置される要素は BEM のルールに従ってクラス名を付与する.
-
l-headerが BEM における Block に相当するため、その後ろに__を使って Element を付与する. -
特定の要素だけデザインが異なる場合には
--を使って Modifier を付与する.
-
-
他の場所で使い回せるようなスタイルは FLOCSS の Component として定義するとよい.
<header class="l-header">
<a class="l-header__logo">
<img src="logo.svg">
</a>
<nav class="l-header__nav">
<a class="l-header__nav__item">会社概要</a>
<a class="l-header__nav__item">サービス</a>
<a class="l-header__nav__item--accent">お知らせ</a>
</nav>
<div class="l-header__action">
<a class="c-button">お問い合わせ</a>
</div>
</header>
sass
|- foundation
|- layout
| `- _header.scss あるいは _l-header.scss
`- object
|- component
`- _button.scss あるいは _c-button.scss 、 _btn.scss など.
|- project
`- utility
.l-header {
&__logo {
img {
height: 1.5rem;
}
}
&__nav {
display: flex;
&__item {
color: #000;
&--accent {
color: #f00;
}
}
}
&__action {
.c-button {
font-size: 14px;
}
}
}
.c-button {
color: #fff;
font-size: 16px;
display: block;
padding: 1rem;
background: #000;
}
-
作業の流れとしては、 HTML を BEM の構成で記述し、その後 FLOCSS の構成で Sass ファイルを作成するという順序となる.
サンプルプロジェクト
-
gulpを使って Sass 開発環境を作り、FLOCSS + BEM の Sass 環境を作ってみる.
$ npm init -y
$ npm install --save-dev gulp gulp-sass sass
$ vim gulpfile.js
$ vim package.json
$ mkdir sass
$ mkdir -p sass/foundation
$ mkdir -p sass/layout
$ mkdir -p sass/object/component
$ mkdir -p sass/object/project
$ mkdir -p sass/object/utility
const gulp = require('gulp');
const sass = require('gulp-sass')(require('sass'));
exports.sass = () => {
return gulp.src('./sass/**/*.scss')
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(gulp.dest('./css'));
};
exports.watch = () => {
return gulp.watch('./sass/**/*.scss', ['sass']);
};
{
"name": "hello-flocss-bem",
"version": "1.0.0",
"description": "= CSS 設計: FLOCSS と BEM について - 破綻しにくい CSS 命名規則",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "gulp sass",
"watch": "gulp watch"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"gulp": "^4.0.2",
"gulp-sass": "^5.1.0"
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>FLOCSS & BEM テスト</title>
<link rel="stylesheet" href="./css/style.css">
</head>
<body>
<header class="l-header">
<a class="l-header__logo">
<img src="logo.svg">
</a>
<nav class="l-header__nav">
<a class="l-header__nav__item">会社概要</a>
<a class="l-header__nav__item">サービス</a>
<a class="l-header__nav__item--accent">お知らせ</a>
</nav>
<div class="l-header__action">
<a class="c-button">お問い合わせ</a>
</div>
</header>
</body>
</html>
// https://github.com/necolas/normalize.css/blob/master/normalize.css
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
// https://webdesign-trends.net/entry/8137#Normalizecss
*,
*::before,
*::after {
box-sizing: border-box;
}
.l-header {
&__logo {
img {
height: 1.5rem;
}
}
&__nav {
display: flex;
&__item {
color: #000;
&--accent {
color: #f00;
}
}
}
&__action {
.c-button {
font-size: 14px;
}
}
}
.c-button {
color: #fff;
font-size: 16px;
display: block;
padding: 1rem;
background: #000;
}
// Foundation
@use './foundation/normalize';
// Layout
@use './layout/header';
// Object - Component
@use './object/component/button';
// Object - Project
//@use './object/project/xxx';
// Object - Utility
//@use './object/utility/xxx';
-
ビルド
$ npm run build