模写修行メディア

初心者向けCSS設計手法を紹介!カオスなコードを卒業しよう!

CSS設計は中級者向けの内容なので、難しいと感じる方も多いでしょう。

確かにいきなり有名な手法を使おうと思っても、『この場合はどうするんだ...?』と迷うことも多いはずです。

僕らが運営している、コーディングの練習が出来るサービス模写修行を制作する際、どんなCSS設計を採用するか悩みました。

模写修行は、ある程度基礎を学び終えたくらいの方が対象なので、有名なCSS設計やそもそもCSS設計自体を知らなくても使えるようにしなければいけません。

OOCSS / SMACSS / BEM / … などの考え方を採用しつつ、webサイトからwebサービスまで、なるべく幅広く対応出来るように考えました。

どんなCSS設計を採用するかに正解は無いので、『模写修行流』ということで、1つのサンプルとして参考になれば幸いです。

【CSS設計入門】class名の決め方(命名規則)から具体的な書き方まで詳しく解説

CSS設計についてまだ全く勉強したことがない方は、↑こちらの記事を読んでからだと内容が入りやすいかもしれません。

【レビュー記事】CSS設計に関するおすすめの本はこの3冊で決まり!

書籍でしっかり勉強したい方は、↑こちらの記事で紹介している3冊がおすすめです。

👇 メンターやってます 👇

模写修行やこのメディアを作ったエンジニア中心に、メンタリングサービスHello Mentorを運営しています。

0からweb制作やプログラミングの勉強を始める方はもちろん、12ヶ月以上独学している方や既にお仕事をしている方にもご利用いただいています!

模写武者くんのアイコン
  • 独学に限界を感じている...
  • 何をどこまで勉強すれば良いかわからない...
  • 自分の書き方が正しいかわからない...
  • 検索しても解決しない問題が多い...
  • 転職や副業のアドバイスが欲しい...

このような方は、ぜひ下記のリンクからサービス詳細をご覧ください。無料相談もお気軽にお申し込みください。

※ 少人数運営のため人数制限あり ※

詳しいサービス内容を見る

入会金/解約料/契約期間の縛りなし

👆 メンターやってます 👆

この記事の目次

雛形コードのダウンロード

雛形コードを見ながらこの記事を読んだ方が、より理解できると思うので、ダウンロードできるようにしました。どこに何を書くか、イメージがつくようにコメントを入れただけの雛形です。

  • scssディレクトリ内全て
  • css > style.css.map

SCSSを使わない方は、上のコードを消してしまって大丈夫です。

SCSSはDart Sassを使うことを前提にしています。

Dart SassをVSCodeの拡張機能(プラグイン)で利用する方法を紹介

VSCodeの拡張機能(プラグイン)を使って、Dart Sassをコンパイルする方法は、上の記事を参考にしてみてください。

コーディングする際の基本ルール

ガチガチのルールを設けるわけではなく、最低限にとどめています。重要な点だけ抑えて、それ以外の細かい点は好みもあるので自分の中でルール化すれば良いです。

classの命名

.service-title{...}
.service-list{...}
.service-item{...}

.c-button {...}
.c-button--primary {...}
.c-button--secondary {...}
.c-button--accent {...}

class名は全てハイフン区切りにしています。


✋ BEMを勉強していない方はここ読み飛ばしてください

BEMのようにブロックとエレメントで分ける命名は、一目でそれがブロックなのかエレメントなのかがわかる点がメリットです。しかし、最初のうちはclassをつける際にこれはブロックか?エレメントか?で悩むことにもなります。全てハイフン区切りであれば、classをつける際のその悩みがなくなります。ブロックなのかエレメントなのかは曖昧でも、迷わず先に進める方を優先しました。


カラーやサイズなどのバリエーションを実装するための、バリエーションクラス(Modifier)はハイフンを2つつけるルールにします。

<!-- 背景赤色のボタンになる -->
<button class="c-button c-button--accent">ボタン</button>
.c-button { /* 全ボタンに適応させたいスタイルを書く */ }

.c-button--primary {
    background-color: #000;
    color: #fff;
}

...

.c-button--accent {
    background-color: red;
    color: #fff;
}

バリエーションクラス(Modifier)はこのように使います。

  • class名の省略はなるべくしない
  • 連番が必要な場合は、1番目は番号は付けずに、2番目から付ける

上の点にも気をつけます。

シングルクラスの採用

種類説明
シングルクラス1つのタグに1つのclassがつく
マルチクラス1つのタグに複数のclassがつく

シングルクラスとマルチクラスの定義はこのようにしています。基本的にはシングルクラスで書きますが、例外もあります。

例外(マルチクラスになる例)

<!-- 🙆‍♂️ OK -->
<h2 class="c-title c-title—-center">
  Modifierと一緒に使うケースはOK
</h2>

<!-- 🙆‍♂️ OK -->
<div class="service u-ptb-m">
  utilityと一緒に使うケースはOK
</div>

<!-- 🙆‍♂️ OK -->
<section class="service l-section">
  layoutと一緒に使うケースはOK
</section>

<!-- 🙅‍♂️ NG -->
<section class="service-title c-title">
  componentと一緒に使うケースはNG
</section>

<!-- 🙆‍♂️ OK -->
<h2 class="c-title c-title—-center js-page-title has-animation-title-fade-in is-title-fade-in-active">
  JavaScriptで操作する際にこのようになるケースもOK
</h2>

まだ紹介していない用語は後半で紹介します。意味が分からない場合は読み進めてから、最後に再度読むと理解できると思います。

  • Modifierを使用する
  • utilityを使用する
  • layoutを使用する
  • JavaScriptのフックを使用する
  • アニメーションを使用する
  • 状態によってJavaScriptで追加削除される

これらのケースではマルチクラスになっても良いことにします。

使用例(シングルクラスでの書き方)

<!-- 🙅‍♂️ NG -->
<button class="c-button contact-submit-button">…</button>

<!-- 🙆‍♂️ OK -->
<div class="contact-submit-button">
  <button class=“c-button>…</button>
</div>
.contact-submit-button{
  margin-top: 40px;
}

例えばボタンの上に40pxの余白を開けたい場合は、このように書きます。.c-buttonは色々なところで使い回すパーツ(コンポーネント)です。

NGの例は悪い書き方というわけではありません。今回はそのような書き方をしないルールにしただけです。どのclassに書いたスタイルが適応されているか分かりやすいように基本的にはシングルクラスにしました。

サイトによってはマルチクラスでの運用の方がスッキリ書けて、ページや要素の追加も楽かもしれません。しかし、マルチクラスは複雑なデザインの場合、破綻を招きやすい特徴があります。

ディレクトリ構成

ディレクトリ構成は雛形をダウンロードしてもらえると分かりやすいと思います。

小中規模なサイトを制作する想定なので、CSSは1ファイルにコメントで区切りながら書きます。SCSSの場合は、後述する役割ごとにディレクトリを切って、その中でブロックごとに分割しています。

ブロックとはそのサイトやページの中でのパーツや塊だと思ってもらえればOKです。

その他

基本的にスタイリングする際はclassを付与して、単体のclassセレクタに書きます。

/* 🙅‍♂️ NG */
.hoge h2{
  margin-top: 40px;
}

/* 🙆‍♂️ OK */
.hoge-title{
  margin-top: 40px;
}

/* 🙆‍♂️ 例外でOK */
.c-posts--col3 > .c-posts-item{
    ...
}

例外に関しては.m-posts-item.m-posts-item--col3のようにしても良いのですが、HTMLが見にくくなるので、例外的にOKにしています。

<!-- 少し見にくい... -->
<div class="c-posts c-posts--col3">
    <article class="c-posts-item c-posts-item--col3"></article>
    <article class="c-posts-item c-posts-item--col3"></article>
    ...
</div>

<!-- 見やすい -->
<div class="c-posts c-posts--col3">
    <article class="c-posts-item"></article>
    <article class="c-posts-item"></article>
    ...
</div>

あまり変わらないと感じる方もいるかもしれませんが、下の方がスッキリします。

【初学者・中級者の方】独学に限界を感じてませんか?

プログラミングやデザインは独学可能ですが、ほとんんどの方が苦戦します。

↓このように感じていませんか?

  • 何をどこまで勉強すれば良いかわからない...
  • 自分の書き方が正しいかわからない...
  • 検索しても解決しない問題が多い...
  • 転職や副業するまでの道が見えない...

そんな問題を解決するために、模写修行やこのメディアを作ったエンジニア/デザイナー中心に、メンタリングサービスHello Mentorを始めました。

初級者から中級者まで対応できる、数少ないサービスです。

スクールのような大金は必要ありません。高額な費用は払いたくないけど、プロのサポートが欲しい方は、ぜひ下記のリンクからサービス詳細をご覧ください。

※ 少人数運営のため人数制限あり ※

詳しいサービス内容を見る

入会金/解約料/契約期間の縛りなし

👆 メンターは全員現役エンジニア 👆

classのカテゴリ分類とその詳細

classのカテゴリ役割
globalサイト内共通の設定
foundationリセットCSSとサイト内共通で効かせたいスタイル
utilityちょっとした使いわますスタイル
componentサイト内で使い回すパーツのスタイル
layoutレイアウトを作るスタイル
page各ページ固有のスタイル
animationアニメーションに関するスタイル

スタイルをこれらの7つのカテゴリに分類します。これ以降、どんな役割のスタイルか詳しく紹介します。

このような分け方をしたのはDart Sassとの兼ね合いもあります。構成を考える際に参考になった記事を載せておきます。

globalカテゴリ

globalカテゴリ
サイト内共通の設定が入る
  • カラー
  • コンテナの幅
  • フォント
  • z-index

このような値をカスタムプロパティで定義しておきます。SCSSを使う場合は、それぞれ別ファイルに書くので4ファイルできることになります。

  • ブレークポイントの設定
  • mixin

SCSSを使う場合は、上の2つも必ずglobalカテゴリに存在します。個人的にあまりmixinを使わないので、mixinは1ファイルにまとめて書きますが、よく使う場合はmixinディレクトリを作っても良いかもしれません。

CSSの場合(SCSSを使わない場合)

/*!
global > color
------------------------------
*/
:root {
  --color-font-base: #333;
}

/*!
global > content-width
------------------------------
*/
:root {
  --width-content-s: 920px;
  --width-content: 1080px;
}

/*!
global > font
------------------------------
*/
:root {
  --font-family-base: "Helvetica Neue", Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
}

/*!
global > z-index
------------------------------
*/
:root {
  --z-index-modal: 100;
  --z-index-header: 30;
  --z-index-menu: 10;
  --z-index-default: 1;
}

このようにコメントで区切って入れています。

SCSSの場合

/*!
global > color
------------------------------
*/
:root {
    --color-font-base: #333;
}

_color.scssのコードです。他のファイルも同じように書いています。

@use "sass:map";

// global > breakpoint
// ------------------------------
$breakpoints: (
    "sm": 500px,
    "md": 768px,
    "lg": 1080px,
    "xl": 1200px,
);

@mixin mq($breakpoint: md) {
    @media screen and (min-width: #{map-get($breakpoints, $breakpoint)}) {
        @content;
    }
}

_breakpoints.scssのコードです。

// 矢印
// ex)
// <li class="side-item">
//     <a class="side-link">サイドメニュー</a>
// </li>
// .side-link{
//     @include arrow(45deg, #000, 8px, 8px, 0, 8px, 0, auto);
// }
@mixin arrow($color: var(--baseFontColor), $rotate: 45deg, $w: 6px, $h: 6px, $left: 0, $right: auto) {
    position: relative;

    &::before {
        content: "";
        position: absolute;
        top: 0;
        bottom: 0;
        margin: auto;
        left: $left;
        right: $right;
        width: $w;
        height: $h;
        border-top: 2px solid $color;
        border-right: 2px solid $color;
        transform: rotate($rotate);
    }
}

...

_mixin.scssのコードです。分かりにくいものはコメントで使用例も載せています。

foundationカテゴリ

foundationカテゴリ
リセットCSSとサイト内共通で効かせたいスタイルが入る
分類役割
foundation > resetリセットCSS
foundation > baseプロジェクト固有のサイト内共通で効かせたいスタイル

foundationカテゴリ内はresetとbaseに分類します。分類の基準は表の通りです。

/*!
foundation > reset
------------------------------
*/
html {
  color: #000;
  background: #fff;
}

body,
div,
dl,
dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6,
pre,
code,
form,
fieldset,
legend,
input,
textarea,
p,
blockquote,
th,
td {
  margin: 0;
  padding: 0;
}

table {
  border-collapse: collapse;
  border-spacing: 0;
  width: 100%;
}

fieldset,
img {
  border: 0;
}

address,
caption,
cite,
code,
dfn,
em,
strong,
th,
var {
  font-style: normal;
  font-weight: normal;
}

ol,
ul {
  list-style: none;
}

caption,
th {
  text-align: left;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-size: 100%;
  font-weight: normal;
}

q:before,
q:after {
  content: "";
}

abbr,
acronym {
  border: 0;
  font-variant: normal;
}

sup {
  vertical-align: text-top;
}

sub {
  vertical-align: text-bottom;
}

input,
textarea,
select,
button {
  color: inherit;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
  *font-size: 100%;
  border-radius: 0;
  border: none;
  appearance: none;
  -webkit-appearance: none;
  background-color: inherit;
}

input,
textarea,
select {
  font-size: 16px;
}

textarea {
  resize: vertical;
  display: block;
}

button {
  padding: 0;
  cursor: pointer;
}

legend {
  color: #000;
}

main {
  display: block;
}

a {
  text-decoration: none;
  color: inherit;
}

img {
  width: 100%;
  height: auto;
  vertical-align: bottom;
}

svg {
  display: block;
}

* {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  box-sizing: border-box;
}

*::before,
*::after {
  box-sizing: border-box;
}

/*!
foundation > base
------------------------------
*/
body {
  line-height: 1.8;
  font-size: 14px;
  color: var(--color-font-base);
  font-family: var(--font-family-base);
}
@media screen and (min-width: 768px) {
  body {
    font-size: 16px;
  }
}

SCSSではfoundationディレクトリの中に、_reset.scss_base.scssが入ることになります。

utilityカテゴリ

utilityカテゴリ
余白や隠しテキストなどのちょっとした使いわますコードが入る
  • 表示切り替え用のクラス
  • 余白関連
  • 隠しテキスト

例えばこれらなど、必要最低限にします。class名にはプレフィックスとしてu-を付けるようにします。

プレフィックスをつけることで、**他の箇所でも使っていることを念頭に置きながらコードを書けます。**これで思わぬところにも影響してしまうようなリスクも減らせます。

/*!
utility > utility
------------------------------
*/
@media screen and (min-width: 768px) {
  .u-sp {
    display: none !important;
  }
}

.u-pc {
  display: none !important;
}
@media screen and (min-width: 768px) {
  .u-pc {
    display: block !important;
  }
}

.u-visually-hidden {
  position: absolute !important;
  white-space: nowrap !important;
  width: 1px !important;
  height: 1px !important;
  overflow: hidden !important;
  border: 0 !important;
  padding: 0 !important;
  clip: rect(0 0 0 0) !important;
  clip-path: inset(50%) !important;
  margin: -1px !important;
}

utilityカテゴリは最優先で効いて欲しいので!importantを付けます。!importantをさらに上書きする必要があるケースは、そもそもutility classを使うべきでない箇所なので、上書きに上書きを重ねることはありません。

utilityカテゴリ内のコードはあまり増やさないようにしているので、SCSSの場合は、_utility.scssの1ファイルに全て書きます。

componentカテゴリ

componentカテゴリ
サイト内で使い回すパーツ(=コンポーネント)が入る

class名にはプレフィックスとしてc-を付けるようにします。

/*!
component > button
------------------------------
*/
.c-button {
    ...
}

.c-button--primary {
    ...
}

/*!
component > title
------------------------------
*/
.c-title {
    ...
}

.c-title--center {
    ...
}

SCSSではコンポーネントごとにファイル分割します。上の例だと、_button.scss_title.scssで2つのファイルに分割します。

デザインのルールがしっかり決まっているサイトでは、componentに属するスタイルが多くなります。

layoutカテゴリ

layoutカテゴリ
レイアウトを作るコードが入る
  • header
  • footer
  • sidebar
  • container

例えばこれらがレイアウトを作るためのスタイルです。

header / footer / sidebarはレイアウトと分りやすいので、プレフィックスは付けませんが、それ以外には基本的にプレフィックスを付けます。

/*!
layout > container
------------------------------
*/
.l-container, .l-container-s {
  width: 90%;
  margin: 0 auto;
}

.l-container-s {
  max-width: var(--width-content-s);
}

.l-container {
  max-width: var(--width-content);
}

/*!
layout > header > header
------------------------------
*/
.header { ... }

/*!
layout > header > header-nav
------------------------------
*/
.header-nav { ... }

/*!
layout > footer > footer
------------------------------
*/
.footer { ... }

/*!
layout > footer > footer-nav
------------------------------
*/
.header-nav { ... }

SCSSではファイル分割します。上の例だと5つのファイルに分割します。シンプルなデザインではheaderやfooterは1ファイルにまとめてしまうこともあります。その辺りは臨機応変に対応します。

pageカテゴリ

pageカテゴリ
各ページ固有のコードが入る

class名には各ページの名前を必ず入れます。

example.com/service/の場合

service-title{…}
service-text{…}
service-button{…}

example.com/about/の場合

about-title{…}
about-text{…}
about-button{…}

各ページの名前をプレフィックスにしておけば、他のページとclass名が被ることもありません。

コンポーネントが少なく、pageカテゴリのスタイルが1番多くなることも少なくありません。

SCSSで書く場合は、ページ名でディレクトリを切って、その中にページのブロック(塊)ごとにファイル分割することになります。

animationカテゴリ

animationカテゴリ
演出目的のアニメーションに使うためのコードが入る

演出目的とは、例えばスクロールに連動してフェードインさせるようなアニメーションなどのことです。

  • タブメニュー
  • モーダル
  • ハンバーガーメニュー

上のような演出目的ではなく、機能目的のUIを実装する際は、animationカテゴリに入れません。

アニメーションでの使用例

<h2 class="service-title js-page-title has-animation-title-fade-in">
  サービス
</h2>
/* SCSSで書く場合のディレクトリは、 */
/* page > service > service.scss */
/* ----------------------------- */
.service-title{
  ...
}

/* SCSSで書く場合のディレクトリは、 */
/* animation > title-animation.scss */
/* ----------------------------- */
.has-animation-title-fade-in{
    opacity: 0;
    transition: opacity .3s;
}

.is-animation-title-fade-in-active{
    opacity: 1;
}

タイトルが表示領域に入ると、透明度を0から1にアニメーションをするためのサンプルです。見た目のスタイルとアニメーションのためのスタイルは分離した方が分かりやすいので、このようにしています。

.js-hogeはJavaScriptのフックに使うもので、このclassに対してスタイリングしてはいけません。

ハンバーガーメニューでの使用例(animation カテゴリに属さない)

<div class="header-menu js-header-menu">
  ...
</div>
/* SCSSで書く場合のディレクトリは、 */
/* layout > header > header-menu.scss */
/* ----------------------------- */
.header-menu{
  ...
  display: none;
}

/* ハンバーガーメニューactive時に付与するclass */
/* layout > header > header-menu.scss */
.is-header-menu-active{
    display: block;
}

このような場合、animationカテゴリには属さず、.header-menuの近くにコメントをつけて書きます。

CSS設計を意識したプロの書き方を学びたいなら模写修行!

模写修行

CSS設計はコーディングの中でも難しい分野なので、完璧に理解したり腹落ちするのには時間がかかります。

完全に理解するまで書籍を読み込むのではなく、どんどん自分でもコードを書いて、書きながら学ぶのが1番上達します。

模写修行では実際にデザインからコーディングをする練習ができます。

特に、中級の教材はCSS設計の勉強になるように作りました。解説とコードの配布もあるので、ご自身の書いたコードと比較して勉強することができます。

模写修行でCSS設計の練習を✊

さっそく練習する

独学に限界を感じていませんか?

現役エンジニアによるメンタリングサービス作りました!

模写修行やこのメディアを作ったエンジニア中心に、メンタリングサービスHello Mentorを運営しています。

👇 こんな方のためのサービスです。

  • 独学に限界を感じている...
  • 何をどこまで勉強すれば良いかわからない...
  • 自分の書き方が正しいかわからない...
  • 検索しても解決しない問題が多い...
  • 転職や副業のアドバイスが欲しい...

メンターを務めるのは、今も現役でコードを書いているエンジニアのみです。駆け出しの方やメンターだけをやっている方はいません。

高額な料金はかかりません。サブスク / 契約期間の縛りなし / 入会金・解約料なしなので、リスクなく始められます。

少しでも興味がある方は、ぜひ下記のリンクからサービスサイトをご覧ください。無料相談もお気軽にお越しください。(無理な営業等一切ございません!)

※ 少人数運営のため人数制限あり ※

詳しいサービス内容を見る

入会金/解約料/契約期間の縛りなし

この記事を書いた人

Gakuのアイコン

Gaku / @gaku92014091

フリーランス8年を経て法人化(3期目)、業界歴は12年目。コンテンツ制作、ライティング、マーケティング、デザイン、コーディング、プログラミング(フロント)、幅広くやってます!webサービスを作るのが好き!

当メディア運営メンバーでメンターやってます!👉

詳しく見る

\Share/

模写修行のトップページのスクリーンショット
模写修行

実務レベルを体験するためのコーディング練習教材