【初心者向け】ウェブサイトのテーマを作ってみよう!カスタムプロパティの使い方について解説!

CSS

はじめに

CSSにはカスタムプロパティという便利なものがあります。

カスタムプロパティを使用することで、プロパティに設定する値をウェブサイト全体で共通化することができ、コーディングの作業効率を向上させることができます。

この記事では、CSSカスタムプロパティの基本から実用的な活用方法までを解説します。

この記事の対象者

  • ウェブ制作初心者の方
  • 効率的なコーディングに興味のある方
  • カスタムプロパティについて興味のある方

この記事で学べること

  • カスタムプロパティの実践的な使い方
  • カスタムプロパティのメリット
  • カスタムプロパティを使う際の注意点

カスタムプロパティについて

カスタムプロパティはCSS変数と呼ばれることもあります。カスタムプロパティはウェブサイトのコーディングにおいて非常に便利な機能です。

カスタムプロパティを使用することで、CSSスタイルシート内で変数のように値を管理することができ、再利用可能なスタイルコードを書くのに役立ちます。

カスタムプロパティの基本概念

カスタムプロパティは、主に値を変数として使いまわす目的で使用されます。

スタイルシート内でカスタムプロパティを宣言することで、さまざまなプロパティの値を共通化することができます。

カスタムプロパティの宣言
:root {
  --color-main: #3498db;
}
カスタムプロパティの利用
.primary-button {
  background-color: var(--color-main);
}

カスタムプロパティはほとんどのモダンなブラウザでサポートされており、主要なブラウザでの使用が可能です。

古いバージョンのInternet Explorer(IE)では使えませんが、IEは2022年にMicro Softによってサポートを終了しているので、積極的に使用して問題ないでしょう。

カスタムプロパティのメリット

カスタムプロパティを使用することで、以下のようなメリットがあります。

  1. 保守性の向上:
    カスタムプロパティを使用すると、色やフォントなどのスタイル設定を一箇所で管理できます。これにより、デザインの変更や保守が容易になります。
  2. テーマ変更の容易さ:
    カスタムプロパティを活用すれば、テーマの変更がシンプルになります。カスタムプロパティに設定した値を書き換えるだけで、ウェブサイト全体の見た目を変更することができます。
  3. 動的なスタイリングの可能性:
    ウェブ制作初心者向けではありませんが、JavaScriptを使用してカスタムプロパティの値を動的に変更することができます。これにより、ユーザーインタラクションに応じたスタイリングを実現できます。

カスタムプロパティの基本的な使い方

カスタムプロパティを使うには、宣言方法、値の呼び出し方法、スコープについての理解が必要です。これらについて解説します。

カスタムプロパティの宣言方法

カスタムプロパティの宣言は、一般的にルート要素(:rootセレクタ)内で行います。以下は宣言の例です。

/* rootセレクタ */
:root {
  /* --変数名: 出力する値; */
  --color-main: orange;
}

:root」という特殊なセレクタのブロック内に「–」から始まる変数名を宣言します。

「:(コロン)」で区切り、その変数を呼び出した際に出力される値を記述します。

この宣言により、--color-mainというカスタムプロパティにorangeの値が格納されます。この値は、さまざまなルールセットで再利用することができます。

また、宣言は複数行うことも出来ます。以下は、ウェブサイトの配色のシンプルな例です。

:root {
  /* 配色 */
  --color-main: orange;
  --color-base: #333;
  --color-text: #fff;
  --color-accent: skyblue;
}

変数名は半角英数字の大文字や小文字、やハイフン( – )、アンダースコア(_)が使用できます。

カスタムプロパティを宣言する際の注意点として、宣言はメディアクエリやコンテナクエリなどのブロック内で宣言することはできません。以下は無効な例です。

@media screen and (min-width: 768px) {
  :root {
    --text-sm: 14px;
    --text-md: 16px;
    --text-lg: 18px;
  }
}

この例は、メディアクエリを使用して画面幅768px以上の場合にカスタムプロパティの値を書き換える目的で記述しています。しかし、カスタムプロパティはメディアクエリ内に記述しても無視されるため、この値は適用されません。

値の呼び出し方法

カスタムプロパティの値を呼び出すには、「var()」というCSS関数を使用します。以下は値の呼び出しの例です。

.primary-button {
  /* プロパティ名: var(--変数名); */
  background-color: var(--color-main);
}

プロパティの値にvar()関数を使用することで、変数の値を呼び出すことができます。

「var()」内に「–」から始まる変数名を記述します。大文字小文字や、区切りで使用するハイフン、アンダースコアは区別されるので注意しましょう。

CSS関数について聞きなれない方もいるかと思いますが、難しく考える必要はありません。CSS関数とは、CSSの値の一種です。例えば、色の指定で使えるrgb()やrgba()などもCSS関数になります。

CSS関数には多くの種類がありますが、よく使用されるものは限られています。必要になった時や、便利そうなものを調べる程度で問題ありません。

スコープについて

カスタムプロパティにはスコープという概念があります。スコープとは変数を利用できる範囲を示すもので、グローバルスコープローカルスコープに分類されます。

上記の例では「:root」という、ルートセレクタを使用して変数を宣言しました。

:rootブロック内のカスタムプロパティはグローバルスコープとして扱われ、すべてのスタイルシートやルールセットで使用することができます。とても便利な機能なので、一般的にはグローバルスコープが使用されます。

グローバルスコープに対し、ローカルスコープはカスタムプロパティの利用を制限します。宣言を任意のセレクタに行うことで、特定の要素に限定してカスタムプロパティを扱うことができます。

以下がローカルスコープの例です。

:root {
  /* 配色 */
  --color-main: orange;
  --color-base: #333;
  --color-text: #fff;
  --color-accent: skyblue;
}

.button {
  --color-main: skyblue;
  --color-accent: orange;
}

上記の例では、.buttonクラスでは–color-mainと–color-accentを反転するように宣言しています。

ローカルスコープはグローバルスコープよりも優先されるため、.buttonクラスが付加された要素はこれらの変数の値が適用されます。

ローカルスコープの注意点として、ここで宣言された変数の値は子要素にも継承されます。

カスタムプロパティをモジュール単位で管理するなら大きな問題は起きないかも知れませんが、安易にローカルスコープを使用すると、想定していない要素のスタイルが変わる可能性があります。

また、変数の宣言がスタイルシート内に散在することになるため管理が難しく、ローカルスコープでのカスタムプロパティの使用はあまり初心者向きではありません。

具体的な活用方法

カスタムプロパティを適切に使用することでスタイルの再利用性と保守性を向上させ、ウェブデザインをより柔軟にします。具体的な活用方法をご紹介します。

カスタムプロパティでサイトのテーマを設定する

カスタムプロパティが扱える値は色だけではありません。

px値やフォント名などもカスタムプロパティとして定義できるため、テキストサイズや余白の定義、コンテンツ幅の定義など、制作するウェブサイトのテーマを設定することで制作の効率と保守性を向上させることができます。

以下はシンプルなテーマの例です。

/*
================
テーマスタイル
================
*/

:root {
  /* 配色 */
  --color-main: #ff9b21;
  --color-base: #333;
  --color-base-light: #444;
  --color-base-reverse: #fff;
  --color-text: #f0f0f0;
  --color-text-reverse: #060606;

  /* コンテンツ幅 */
  --width-contents-max: 1080px;
  --width-contents-md: 736px;

  /* 余白 */
  --space-lv1: 8px;
  --space-lv2: 16px;
  --space-lv3: 24px;
  --space-lv4: 36px;

  --space-section-lv1: 40px;
  --space-section-lv2: 80px;

  /* 文字サイズ */
  --font-text-sm: 14px;
  --font-text-md: 16px;
  --font-text-lg: 18px;

  --font-heading-sm: 18px;
  --font-heading-md: 24px;
  --font-heading-lg: 32px;
  
  --font-logo-md: 24px;

  /* フォントファミリー */
  --font-famliy-san-serif: "Helvetica Neue", "Helvetica", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Arial", "Yu Gothic", "Meiryo", sans-serif;
  --font-family-serif: "Times New Roman", "YuMincho", "Hiragino Mincho ProN", "Yu Mincho", "MS PMincho", serif;
}

このテーマを使用して簡単なページを作成してみました。

各スタイルの値をカスタムプロパティから呼び出すことで、サイトのテーマを簡単に変更することができます。例として、配色を変更してみます。

/* 配色 */ 
--color-main: #ff3a3a;
--color-text: #333;
--color-text-reverse: #fff;
--color-base: #f0f0f0;
--color-base-light: #ddd;
--color-base-reverse: #060606;

このように、カスタムプロパティを適切に管理することでサイト全体の変更を簡単に行うことができます。

3. インタラクティブな変更

カスタムプロパティはJavaScriptで定義することもできます。

<script>
  // JavaScriptでカスタムプロパティを設定
  document.documentElement.style.setProperty('--color-main', 'orange');
</script>

JavaScriptでカスタムプロパティを動的に変更することで、ユーザーインタラクションに応じたスタイリングを実現できます。

例えば、上記の例で行った色変更をユーザー側で切り替えるようにすることができます。

簡易的ではありますが、例として実装してみます。

<script>
  // テーマ切替用のフラグ。
  let flag = 0;
  
 // ボタンがクリックされたときに実行する処理
  function changeColor(value) {
    switch (value) {
      case 0:
        // カスタムプロパティの値を上書き
        document.documentElement.style.setProperty('--color-main', '#ff3a3a');
        document.documentElement.style.setProperty('--color-base', '#f0f0f0');
        document.documentElement.style.setProperty('--color-base-light', '#ddd');
        document.documentElement.style.setProperty('--color-base-reverse', '#333');
        document.documentElement.style.setProperty('--color-text', '#060606');
        document.documentElement.style.setProperty('--color-text-reverse', '#f0f0f0');

        flag = 1;
        break;

      case 1:
        // 切替前のカスタムプロパティに戻す
        document.documentElement.style.setProperty('--color-main', '#ff9b21');
        document.documentElement.style.setProperty('--color-base', '#333');
        document.documentElement.style.setProperty('--color-base-light', '#444');
        document.documentElement.style.setProperty('--color-base-reverse', '#fff');
        document.documentElement.style.setProperty('--color-text', '#f0f0f0');
        document.documentElement.style.setProperty('--color-text-reverse', '#060606');

        flag = 0;
        break;
    }
  }
</script>

上記のコードをHTML内のhead内あたりに記述すると動作します。

また、この関数を実行するためのボタンをHTML内に作成します。

<button class="button" onclick="changeColor(flag);">色を変更</button>

コードの解説

簡単な処理ですが解説します。

HTMLのhead内に記述しているので、HTMLが読み込まれたタイミングで「let flag = 0;」が 実行され、flag変数が扱えるようになります。

<head>
  …
  <script>
    // テーマ切替用のフラグ。
    let flag = 0;
    …
  </script>
</head>

また、function changeColor(value) {…};でボタンがクリックされたときの処理を記述しています。

function changeColor(value) {
  switch (value) {
    case 0:
      // valueが0の時の処理
      flag = 1;
      break;
    case 1:
      // valueが1の時の処理
      flag = 0;
      break;
}

ユーザーがボタンをクリックすると、button要素のonclick属性によりchangeColor関数が実行されます。

<button class="button" onclick="changeColor(flag);">色を変更</button>

このとき、changeColorの引数に初めに定義したflag変数を渡します。これにより、changeColor関数内でflagの値を動的に使用することができるようになります。

flagの値を受け取ったchangeColor関数は、この値を関数内で「value」という引数名で扱います。

switch文で条件分岐を行い、valueの値に応じて「valueが0の時の処理」と「valueが1の時の処理」をそれぞれ行います。

また、それぞれの処理の最後ではflagの値を再代入してから処理を抜けています。

flagの再代入を行うことで、関数の実行時の表示が「デフォルトの色」なのか「変更後の色」なのかを判別できるようにしています。

変更後の色が表示されているときはflagが1になっており、ボタンをクリックすることで1の値がchangeColor関数に渡ります。

switch文により条件分岐が行われ、case 1:内の処理が実行されて「デフォルトの色」に戻り、flagの値も0に戻ります。

このようにして、インタラクティブにウェブサイトのテーマを変更することができます。

この例はカスタムプロパティの解説用なので出来るだけシンプルな記述にしました。

そのため、複数ページのウェブサイトでは機能しません。複数ページで利用する場合はもう少し工夫が必要になります。

サンプルコード

今回使用したサンプルコードをご紹介します。

コンテンツのボリュームに対して大げさなテーマ設定ですが、初心者の方はコピーしてご自身で触ったりカスタマイズして遊んでみると良いと思います。

HTML
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">

  <script>
    let flag = 0;

    function changeColor(value) {
      switch (value) {
        case 0:
          document.documentElement.style.setProperty('--color-main', '#ff3a3a');
          document.documentElement.style.setProperty('--color-base', '#f0f0f0');
          document.documentElement.style.setProperty('--color-base-light', '#ddd');
          document.documentElement.style.setProperty('--color-base-reverse', '#333');
          document.documentElement.style.setProperty('--color-text', '#060606');
          document.documentElement.style.setProperty('--color-text-reverse', '#f0f0f0');

          flag = 1;
          break;

        case 1:
          document.documentElement.style.setProperty('--color-main', '#ff9b21');
          document.documentElement.style.setProperty('--color-base', '#333');
          document.documentElement.style.setProperty('--color-base-light', '#444');
          document.documentElement.style.setProperty('--color-base-reverse', '#fff');
          document.documentElement.style.setProperty('--color-text', '#f0f0f0');
          document.documentElement.style.setProperty('--color-text-reverse', '#060606');

          flag = 0;
          break;

      }
    }
  </script>

</head>

<body>
  <header class="header">
    <div class="container">
      <h1 class="header-logo">LOGO</h1>
      <button class="button" onclick="changeColor(flag);">色を変更</button>
      <nav>
        <ul class="nav-menu">
          <li class="nav-item"><a href="#" class="nav-item-link">ホーム</a></li>
          <li class="nav-item"><a href="#" class="nav-item-link">サービス</a></li>
          <li class="nav-item"><a href="test.html" class="nav-item-link">お知らせ</a></li>
          <li class="nav-item"><a href="#" class="button button__primary">お問い合わせはこちら</a></li>
        </ul>
      </nav>
    </div>
  </header>

  <main>
    <div class="container">
      <section class="section section__service">
        <h2 class="section-title">サービス</h2>
        <p class="section-lead">当社は以下のサービスを提供しています。</p>

        <div class="service-contents">
          <section class="service">
            <div class="service-img"></div>
            <div class="service-text-wrap">
              <h3 class="service-title">サービス1</h3>
              <p class="service-text">サービス1の説明をします。サービス1の説明をします。サービス1の説明をします。サービス1の説明をします。サービス1の説明をします。</p>
            </div>
          </section>
          <section class="service">
            <div class="service-img"></div>
            <div class="service-text-wrap">
              <h3 class="service-title">サービス2</h3>
              <p class="service-text">サービス1の説明をします。サービス1の説明をします。サービス1の説明をします。サービス1の説明をします。サービス1の説明をします。</p>
            </div>
          </section>
          <section class="service">
            <div class="service-img"></div>
            <div class="service-text-wrap">
              <h3 class="service-title">サービス3</h3>
              <p class="service-text">サービス1の説明をします。サービス1の説明をします。サービス1の説明をします。サービス1の説明をします。サービス1の説明をします。</p>
            </div>
          </section>
        </div>

      </section>

      <section class="section section__news">
        <h2 class="section-title">お知らせ</h2>
        <p class="section-lead">当社からのお知らせを新着順で表示しています。</p>
        <ul class="news-list">
          <li class="news-item">
            <h3 class="news-title">最新のお知らせ</h3>
            <p class="news-text">最新のお知らせについての内容です。</p>
          </li>
          <li class="news-item">
            <h3 class="news-title">2番目のお知らせ</h3>
            <p class="news-text">2番目のお知らせについての内容です。</p>
          </li>
          <li class="news-item">
            <h3 class="news-title">3番目のお知らせ</h3>
            <p class="news-text">3番目のお知らせについての内容です。</p>
          </li>
          <li class="news-item">
            <h3 class="news-title">4番目のお知らせ</h3>
            <p class="news-text">4番目のお知らせについての内容です。</p>
          </li>
        </ul>
      </section>
    </div>
  </main>

  <footer class="footer">
    <div class="container">
      <p>&copy; 2023 サイト名</p>
    </div>
  </footer>
</body>

</html>
CSS
@charset "UTF-8";

/*
================
テーマスタイル
================
*/

:root {
  /* 配色 */
  --color-main: #ff9b21;
  --color-base: #333;
  --color-base-light: #444;
  --color-base-reverse: #fff;
  --color-text: #f0f0f0;
  --color-text-reverse: #060606;

  /* コンテンツ幅 */
  --width-contents-max: 1080px;
  --width-contents-md: 736px;

  /* 余白 */
  --space-lv1: 8px;
  --space-lv2: 16px;
  --space-lv3: 24px;
  --space-lv4: 36px;

  --space-section-lv1: 40px;
  --space-section-lv2: 80px;

  /* 文字サイズ */
  --font-text-sm: 14px;
  --font-text-md: 16px;
  --font-text-lg: 18px;

  --font-heading-sm: 18px;
  --font-heading-md: 24px;
  --font-heading-lg: 32px;
  
  --font-logo-md: 24px;

  /* フォントファミリー */
  --font-famliy-san-serif: "Helvetica Neue", "Helvetica", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Arial", "Yu Gothic", "Meiryo", sans-serif;
  --font-family-serif: "Times New Roman", "YuMincho", "Hiragino Mincho ProN", "Yu Mincho", "MS PMincho", serif;
}

/*
================
基本設定・リセット
================
*/
body {
  font-size: var(--font-text-md);
  font-family: var(--font-family-serif);
  background-color: var(--color-base-light);
  color: var(--color-text);
}

* {
  margin: 0;
  padding: 0;
}

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

ul {
  list-style: none;
}

/*
================
共通
================
*/

/* コンテナ */
.container {
  max-width: var(--width-contents-max);
  margin: 0 auto;
}

/* ヘッダー */
.header {
  background-color: var(--color-base);
  color: var(--color-text);
  text-align: center;
  padding: var(--space-lv2);
}

.header .container {
  display: flex;
  justify-content: space-between;
}

/* ヘッダーロゴ */
.header-logo {
  font-size: var(--font-logo-md);
  line-height: 1;
  font-family: var(--font-famliy-san-serif);
}

.header-logo:first-letter {
  font-size: 1.5em;
  color: var(--color-main);
}

/* ナビゲーションメニュー */
.nav-menu {
  list-style: none;
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
  column-gap: var(--space-lv2);
}

.nav-item-link {
  color: var(--color-text);
  text-decoration: none;
  font-weight: bold;
}

.nav-menu .button__primary {
  margin-left: var(--space-lv3);
}

/* フッター */
.footer {
  background-color: var(--color-base);
  text-align: center;
  padding: var(--space-lv2);
}

/* セクションのベース */
.section {
  padding: var(--space-section-lv2) var(--space-lv2);
}

.section-title {
  font-size: var(--font-heading-lg);
  letter-spacing: 0.1em;
  text-align: center;
  color: var(--color-main);
  margin-bottom: var(--space-lv2);
}

.section-lead {
  font-size: var(--font-text-lg);
  line-height: 1.8;
  letter-spacing: 0.1em;
  text-align: center;
  margin-bottom: var(--space-section-lv1);
}


/* ボタン */
.button {
  padding: var(--space-lv1) var(--space-lv2);
  display: inline-block;
  text-decoration: none;
  font-weight: bold;
  cursor: pointer;
}

.button__primary {
  background-color: var(--color-main);
  color: var(--color-base);
  border: none;
}


/*
================
サービスセクション
================
*/
.service-contents {
  display: flex;
  justify-content: space-between;
  gap: var(--space-lv2);
}

.service {
  flex: 1;
  background-color: var(--color-base-reverse);
}

.service-img {
  aspect-ratio: 3/2;
  background-color: var(--color-base);
}

.service-text-wrap {
  padding: var(--space-lv2) var(--space-lv2) var(--space-lv3);
}

.service-title {
  font-size: var(--font-heading-md);
  text-align: center;
  color: var(--color-main);
  margin-bottom: var(--space-lv2);
}

.service-text {
  color: var(--color-text-reverse);
}

.service-text {
  font-size: var(--font-text-sm);
}

/*
================
お知らせセクション
================
*/
.section__news {
  background-color: var(--color-base-reverse);
  padding-top: var(--space-section-lv1);
  margin-bottom: var(--space-section-lv2);
}

.section__news .section-lead {
  color: var(--color-text-reverse);
}

.news-list {
  background-color: var(--color-base);
  max-width: var(--width-contents-md);
  margin: 0 auto;
  padding: var(--space-lv1) var(--space-lv3) var(--space-lv2);
}

.news-item + .news-item {
  border-top: 1px solid var(--color-base-light);
}

.news-item {
  padding: var(--space-lv1) var(--space-lv1) var(--space-lv2);
}

.news-title {
  font-size: var(--font-heading-sm);
  margin: var(--space-lv1) 0;
}

カスタムプロパティを上手に使うポイント

カスタムプロパティを使用する際の重要なポイントを2つご紹介します。

命名規則を一貫させる

カスタムプロパティの名前付けに一貫性を持たせることが重要です。

個人制作であれば自分の分かりやすい名前でも大丈夫ですが、チーム開発の場合はしっかりと意味のある名前を選び、プロジェクト内で統一された命名規則を採用する必要があります。これにより、スタイルの管理が容易になります。

また、VSCodeなどを使用していればコードの補完機能が付いています。

カスタムプロパティに設定した名前を予測して表示してくれるので、一貫性のある名前を付けておくと呼び出す際にとても便利です。

例えば、今回のサンプルコードではこうなります。

–space」と入力するだけで、–spaceから始まるカスタムプロパティの名前が一覧で表示されます。

これにより、セクションブロック用の余白と汎用的な余白を簡単に判別することができます。

この記事のサンプルコードは一例であり、これが誰にでも分かりやすい命名規則というわけではありません。

まずはいろいろ試して、扱いやすい命名規則を考えてみると良いと思います。

コメントを書く

カスタムプロパティを宣言したら、適切なコメントを追加するよう心がけると良いです。

テーマ設定を行う場合は多くのカスタムプロパティを管理することになるので、規模が大きくなるほど管理が難しくなります。

コメントを書くことでカスタムプロパティの用途を明確にすることができます。他の開発者やの自分にとってコードを理解しやすくなり、プロジェクトの保守性が向上します。

最後に

この記事では、CSSカスタムプロパティの基本から実用的な活用方法までを解説しました。

CSSカスタムプロパティはウェブ制作における強力なツールです。スタイルの再利用性や保守性を向上させ、テーマのカスタマイズがとても簡単になります。

VSCodeなどの高機能テキストエディタにはコードの補完機能があるので、カスタムプロパティに一貫性のある名前を付けることで値の呼び出しを簡単に行うことができます。

カスタムプロパティを使いこなして、効率的で保守性の高いウェブ制作を目指しましょう。

タイトルとURLをコピーしました