SMACSSを使用したCSS設計 | サンプルコードで解説

SMACSSは、CSSを定義する上での規則を、下記の5種類に分類しています。

  • ベース
  • レイアウト
  • モジュール
  • 状態(state)
  • テーマ

それぞれの使用方法について、あまり整理ができていない方は一定数いるのではないでしょうか。

この記事を読むことで、SMACSSの5つの概念を、図解を元に構造的に理解することができます。
また、サンプルコードを用いて、実践的な解説も交えていますので、実務にも役立ちます。
それでは早速5つの概念を解説していきましょう。

ベース

ベースルールは、サイト全体で、要素そのもののデフォルトのスタイルを定義します。

【ベース定義の記述例】

下記は、私のブログのCSSですが、bodyやaタグなどの要素そのものにデフォルトでスタイルが当たっています。

body {
  background: #f9f9f9;
  color: #383838;
}

a {
  text-decoration: none;
  color: #2581c4;
}
a:hover {
  text-decoration: underline;
}

デザインが完成した段階で、サイトの文字色やフォント、背景色などは決まっています。
各ページの個別の要素に対して1つ1つスタイルを当てるのは、骨が折れる作業ですし、CSSの破綻にも繋がります。
このように、サイト全体で共通する要素のスタイルを定義する場所として、ベースの概念を使用します。

【ベース定義の注意点】

  • IDやclassのセレクタは定義しない
  • 具体的なスタイルは書かない(ul liのlist-styleなど)

あくまで要素そのもののデフォルトスタイルを定義するので、body, a, inputなどの要素そのものに限定します。
また、具体的なスタイルも定義せず、あくまで最低限の共通のスタイルのみ記述するようにします。
具体的なスタイルを記述することで、CSSの上書きが各所で発生し、破綻につながる恐れがあります。

レイアウト

ページ内をレイアウトに分割し、各レイアウトに対してスタイルを当てていきます。
レイアウトの分割は下記の画像のように、ヘッダー・フッター・サイドナビ・メインコンテンツに分割します。

【レイアウト定義の記述例】

CSSの命名については、プレフィックスをlayout-〇〇、l-〇〇とし、
レイアウトに関する記述であるということを一眼でわかるようにします。

/* ヘッダー */
.layout-header {

}

/* フッター */
.layout-footer {

}

/* サイドナビ */
.layout-side {

}

/* メインコンテンツ */
.layout-main {

}

【レイアウト定義の注意点】

  • グローバルメニューなどの、レイアウト内に含まれるモジュールのスタイルは記述しない
  • IDセレクタは使用しない

SMACSSの考え方として、レイアウトとモジュールは分けて定義するので、
「ヘッダーの次は即座にメニューのスタイルを!」となる気持ちはわかりますが、
ここでは我慢する必要があります。

IDセレクタの使用については、CSSの詳細度を高める性質があるので、設計の段階では使用をしません。
classでCSSを定義し、詳細度を低い状態で一定に保つことで、後でCSSを変更する際にも強烈な上書きが発生することはありません。

モジュール

モジュールでは、レイアウトの内部にある再利用可能なパーツの見た目を定義します。
視覚的にわかりやすくするために画像を用意しました。

画像を見るだけで、大体のイメージはつかんでいただけるかと思います。
赤枠で囲んでいる部分が、SMACSSではモジュールとして定義されます。
下記が、再利用可能なパーツ、つまり、モジュールになります。

  • サイトのロゴ
  • メニュー
  • 検索フォーム
  • インフォメーション

【モジュール HTML 記述例】

ロゴのスタイルを定義する際には下記の構造になります。

<div class="logo">
	<div class="logo-image">
		<img src="./logo.jpg" alt="ロゴ画像">
	</div>
</div>

【モジュール HTML 解説】

まず、モジュールの最上部(親モジュール)に汎用的なclassを命名します。
子のモジュールの命名は親モジュールのclassをプレフィックスとし、ハイフンでつないで命名します。

異なるレイアウトにモジュールを設置する場合

※ヘッダーとフッターにロゴを設置する想定

<!-- ヘッダー -->
<div class="layout-header">
	<div class="logo">
		<div class="logo-image">
			<img src="./logo.jpg" alt="ロゴ画像">
		</div>
</div>
</div>

<!-- フッター -->
<div class="layout-footer">
	<div class="logo">
		<div class="logo-image">
			<img src="./logo.jpg" alt="ロゴ画像">
		</div>
</div>
</div>

この場合、モジュールは完全に独立した状態でなければならないということを意識してください。
レイアウトの変更の影響を受けないようにする必要があります。
具体的には「レイアウトのclass名が変わってもモジュールのスタイルは保持される」ということです。

つまり下記のような記述はアンチパターンです。
※ヘッダーとフッターでロゴ要素の横幅を分けたい場合

.layout-header .logo-image {
	width: 200px;
}

.layout-footer .logo-image {
	width: 300px;
}

【モジュール 適切な記述例】

<!-- ヘッダー -->
<div class="layout-header">
	<div class="logo logo-head">
		<div class="logo-image">
			<img src="./logo.jpg" alt="ロゴ画像">
		</div>
</div>
</div>

<!-- フッター -->
<div class="layout-footer">
	<div class="logo logo-bottom">
		<div class="logo-image">
			<img src="./logo.jpg" alt="ロゴ画像">
		</div>
</div>
</div>
.logo-head .logo-image {
	width: 200px;
}

.logo-bottom .logo-image {
	width: 300px;
}

違いは、logoクラスにヘッダー用のlogo-head、フッター用にlogo-bottomを追加し、
CSSではlogo-headとlogo-bottomに対してそれぞれ横幅を指定しています。

これでモジュールとして完全に独立した状態になりました。
改めて、モジュールを定義する際には、レイアウトとモジュールを完全に分離することを意識すべきです。

状態(state)

SMACSSの状態管理は、JavaScriptなどを使用して表示・非表示を切り替える特定の状況下で使用します。
プレフィックスはis-を使用します。

使用例としては、JSで特定の要素にclassを付与して、CSSと連携させる下記のような場合です。

  • ハンバーガーメニューの開閉
  • アコーディオンメニュー
  • ダイアログの表示

【状態定義の記述例】

※ハンバーガーメニューをクリックしたら、横からメニューがスライドインしてくる想定

<!-- ハンバーガーメニュー -->
<div class="hamburger-icon">
    <img src="./sp-hamburger-icon.jpg" alt="ハンバーガーメニューの開閉アイコン">
</div>

<!-- サイドメニュー -->
<div class="side-menu" id="sideMenu">
    <ul>
        <li><a href="#">メニュー1</a></li>
        <li><a href="#">メニュー2</a></li>
        <li><a href="#">メニュー3</a></li>
        <li><a href="#">メニュー4</a></li>
    </ul>
</div>
.side-menu {
    position: absolute;
    left: -100%;
    top: 0;
    opacity: 0;
    transition: 0.5s;
}
.side-menu.is-active {
    left: 0;
    opacity: 1;
}
const HamburgerIcon = document.querySelector('.hamburger-icon');
const menu = document.querySelector('#sideMenu');
HamburgerIcon.addEventListener('click', function() {
    menu.classList.add('is-active');
});

初期状態はCSSでメニューの中身を非表示にしておき、
ハンバーガ−メニューがクリックされたタイミングで、is-activeというクラスを付与しています。

このようにJavaScriptを使用して動的にスタイルが変化するCSSの記述を定義する概念として、「状態」があります。

テーマ

主に色に関わる部分がテーマの管理対象になります。プレフィックスはtheme-を使用します。

サイト内で使用されるカラーは、デザインの観点からそれほど多くはありません。
背景色や文字色、枠線の色など、共通化して使いまわせる部分を洗い出します。
それぞれの色に関するスタイルを定義しておけば、毎度border-やbackground-colorのセレクタを当てる必要はありません。

コードを見直した際に、borderやbackgroundを毎回指定している方は、
色に関すスタイルを共通化することで、コード量を削減することができます。

【テーマ定義の記述例】

.theme-border {
	border-color: red;
}

.theme-background {
	background-color: #ddd;
}

【テーマ定義の注意点】

  • 共通化しなくていいものは、記述しない

最後に

SMACSSはメンテナンス性を向上させるCSSの設計手法として、筆者も実務で使用しています。

最初はCSSを書くことに必死でしたが、WEBサイトのフォルダ構造や、ファイル管理を意識するタイミングからCSS設計を意識するようになりました。SMACSSを使用することで、CSSのメンテナンス性が大幅に向上しましたので、読者の方も是非参考にしていただけますと幸いです。

参考 : smacss

関連記事

コメント

この記事へのトラックバックはありません。