第8回: 実践!よく使うWebコンポーネント実装

この回の目標

  • ハンバーガーメニューを実装できる
  • アコーディオンやスライダーを組み込める
  • Webサイトで即使えるパーツを自分で調整できる
  • HTML / CSS / JSの連携が理解できている

コンポーネントとは?

コンポーネントは「再利用可能なパーツ」

コンポーネントとは、特定の機能や見た目を持った再利用可能なパーツのことです。

ボタンや入力フォーム、ナビゲーションバーを「パーツ(コンポーネント)」として独立させて作ることで、色々な画面で使い回せるようにすします。

Webデザイン・UIコンポーネント集(HTML/CSS/JS) | ノベブロ / NOVEBLO

特徴

  • 再利用できる:
    一度「かっこいいボタン」を作っておけば、他の画面で使いたい時にまた1からコードを書かなくて済む
  • メンテナンス性:
    例えば「サイト全体のボタンの色を青から赤に変えたい」ってなった時、コンポーネント化されていれば、そのパーツ1箇所を直すだけで全ての画面に反映される
  • コードが読みやすくなる:
    パーツごとに適切な設計を行うことで「ここはヘッダー」「ここはカード」って一目で理解しやすくなる

今回作るコンポーネント

  • ハンバーガーメニュー
  • アコーディオンメニュー
  • スライダー

Web制作でよく使われるパーツ3つを実装してみましょう。

ハンバーガーメニューを実装してみよう

完成イメージ

PC時

サイトロゴとメニューを横並び(左右配置)で表示

SP時

横3本の線(ハンバーガーメニュー)をクリックするとメニューが表示される
もう一度クリックするとメニューが閉じる

「≡」をクリック

「×」をクリック

デモページ

コード

HTML

<header class="site-header">
  <div class="header-container">
    <div class="site-logo">
      <a href="#">ロゴ</a>
    </div>

    <nav class="nav-menu" id="js-nav-menu">
      <ul class="nav-list">
        <li><a href="#">Home</a></li>
        <li><a href="#">About</a></li>
        <li><a href="#">Services</a></li>
        <li><a href="#">Contact</a></li>
      </ul>
    </nav>

    <div class="hamburger" id="js-hamburger">
      <span></span>
      <span></span>
      <span></span>
    </div>
  </div>
</header>

<header>タグを使って、その中にロゴ・ナビゲーション・ハンバーガーメニューパーツを入れる。

CSS

/* --- 共通・PC向けスタイル --- */
.site-header {
  width: 100%;
  background-color: #fff;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  position: fixed;  /* ヘッダーを上部に固定 */
  top: 0;
  left: 0;
  z-index: 1000;
}

.header-container {
  max-width: 1200px;  /* コンテンツの最大幅 */
  margin: 0 auto;  /* 中央寄せ */
  padding: 30px 20px;
  display: flex;
  justify-content: space-between;  /* 両端に配置 */
  align-items: center;  /* 上下中央寄せ */
}

.hamburger {
  display: none;  /* PCでは隠す */
}

.nav-list {
  display: flex;  /* 横並び */
  gap: 30px;
}

.nav-list li {
  list-style: none;
}

.nav-list li a {
  text-decoration: none;
  color: #666;
}

/* --- スマホ向けスタイル(画面幅768px以下) --- */
@media screen and (max-width: 768px) {
  .header-container {
    padding: 15px 20px;
  }

  .hamburger {
    display: block;  /* ボタンを表示 */
    position: relative;  /* 位置固定を解除 */
    width: 30px;
    z-index: 100;
  }

  /* ハンバーガー線の調整 */
  .hamburger span {
    display: block;
    width: 100%;
    height: 2px;
    background-color: #1e90ff;
    transition: all 0.3s;
  }

  .hamburger span:not(:last-child) {
    margin-bottom: 6px;
  }

  .hamburger.is-active span:nth-child(1) {
    transform: translateY(8px) rotate(45deg);
  }

  .hamburger.is-active span:nth-child(2) {
    opacity: 0;
  }

  .hamburger.is-active span:nth-child(3) {
    transform: translateY(-8px) rotate(-45deg);
  }

  /* メニューのスタイル(全画面スライド) */
  .nav-menu {
    position: fixed;
    top: 0;
    right: -100%;
    width: 100%;
    height: 100vh;
    background-color: #e0ffff;
    transition: all 0.3s;
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 90;  /* ロゴとボタンより下に配置 */
  }

  .nav-list {
    flex-direction: column;  /* 縦並び */
    gap: 20px;
    text-align: center;
  }

  .nav-menu.is-active {
    right: 0;
  }
}

Flexboxによるレイアウト(PC・SP共通)

  • display: flex;justify-content: space-between;
    これを使うことで、左側に「ロゴ」、右側に「メニュー」を自動で振り分けている
    .header-container {
      max-width: 1200px;  /* コンテンツの最大幅 */
      margin: 0 auto;  /* 中央寄せ */
      padding: 30px 20px;
      display: flex;
      justify-content: space-between;  /* 両端に配置 */
      align-items: center;  /* 上下中央寄せ */
    }
  • position: fixed;
    画面をスクロールしてもヘッダーが一番上にずっと残るように固定している
    .site-header {
      width: 100%;
      background-color: #fff;
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
      position: fixed;  /* ヘッダーを上部に固定 */
      top: 0;
      left: 0;
      z-index: 1000;
    }

ハンバーガーボタンのアニメーション(スマホ)

  • span:nth-child()transform
    3本の線のうち、1番目を45度、3番目をマイナス45度回転させて、2番目を opacity: 0 で消すことで「三」から「×」に変化させている
    .hamburger.is-active span:nth-child(1) {
      transform: translateY(8px) rotate(45deg);
    }
    
    .hamburger.is-active span:nth-child(2) {
      opacity: 0;
    }
    
    .hamburger.is-active span:nth-child(3) {
      transform: translateY(-8px) rotate(-45deg);
    }
  • transition: all 0.3s;
    これがあるおかげで、パッと切り替わるのではなくて滑らかに形が変わるアニメーションになる
    .hamburger span {
      display: block;
      width: 100%;
      height: 2px;
      background-color: #1e90ff;
      transition: all 0.3s;
    }

メディアクエリとメニューのスライド(スマホ)

  • @media screen and (max-width: 768px)
    「画面幅が768px以下の時だけこのスタイルを適用する」という命令で、これでPCとスマホの出し分けをしている
  • right: -100%; から right: 0;
    メニュー本体は最初、画面の右外側(-100%)に隠れている
    ボタンが押されて .is-active がついた瞬間に、画面内にスッと滑り込んでくる(right: 0)仕組み
    .nav-menu {
      position: fixed;
      top: 0;
      right: -100%;
      width: 100%;
      height: 100vh;
      background-color: #e0ffff;
      transition: all 0.3s;
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 90;
      /* ロゴとボタンより下に配置 */
    }
    
    .nav-menu.is-active {
      right: 0;
    }

JavaScript

const btn = document.getElementById('js-hamburger');
const nav = document.getElementById('js-nav-menu');

btn.addEventListener('click', () => {
  btn.classList.toggle('is-active');
  nav.classList.toggle('is-active');
});

1. 要素を見つける(取得)

  • document.getElementById を使って、HTML側で付けておいた id="js-hamburger"(ボタン)と id="js-nav-menu"(メニュー本体)を探し出して、変数(btn と nav)に格納する

2. 動きを待ち構える(イベントリスナー)

  • btn.addEventListener('click', () => { ... });は「ボタンがクリックされたら、{ } の中の命令を実行してね!」という機能
    ユーザーがアクションを起こすまで、JSはじっと待機している状態

3. クラスを入れ替える(トグル処理)

  • toggle(トグル)というのは、スイッチのON/OFFのようなもの
    .is-active クラスがついていない時: クラスを追加する(ON)
    .is-active クラスがついている時: クラスを削除する(OFF)

ポイント

クラスの付け外しをJSでやって、見た目の変化は全部CSSで制御する

アコーディオンメニューを実装してみよう

完成イメージ

再利用ができること、修正が楽になること、そしてコードが読みやすくなることです。

FAQ(よくある質問)や、スマホで情報量が多いページをスッキリ見せたい時によく使われます。

コード

HTML

<div class="accordion">
  <div class="accordion-item">
    <button class="accordion-header js-accordion-trigger">
      Q1. コンポーネント化のメリットは何ですか?
    </button>
    <div class="accordion-content">
      <p>再利用ができること、修正が楽になること、そしてコードが読みやすくなることです。</p>
    </div>
  </div>

  <div class="accordion-item">
    <button class="accordion-header js-accordion-trigger">
      Q2. どんな時にアコーディオンを使いますか?
    </button>
    <div class="accordion-content">
      <p>FAQ(よくある質問)や、スマホで情報量が多いページをスッキリ見せたい時によく使われます。</p>
    </div>
  </div>
</div>

「質問」と「回答」を1つのセット accordion-item にすることで、JSで parentNode(親要素)を辿って簡単に操作できるようにしている

CSS

/* --- アコーディオン全体 --- */
.accordion {
  width: 100%;
  margin: 20px auto;
  border-top: 1px solid #ddd;
}

/* 各アイテムの区切り線 */
.accordion-item {
  border-bottom: 1px solid #ddd;
}

/* クリックするヘッダー部分 */
.accordion-header {
  width: 100%;
  padding: 20px;
  text-align: left;
  background: none;
  border: none;
  font-size: 16px;
  font-weight: bold;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  position: relative; /* 疑似要素の基準点 */
}

/* +アイコンの共通スタイル(横線と縦線) */
.accordion-header::before,
.accordion-header::after {
  content: '';
  position: absolute;
  right: 20px;
  width: 15px; /* 線の長さ */
  height: 2px; /* 線の太さ */
  background-color: #333;
  transition: all 0.3s; /* アニメーションの速度 */
}

/* 縦線:90度回転させて「+」を作る */
.accordion-header::after {
  transform: rotate(90deg);
}

/* --- 開閉の中身(重要:高さのアニメーション) --- */
.accordion-content {
  display: grid;
  grid-template-rows: 0fr; /* 初期状態は高さを0にする */
  transition: grid-template-rows 0.3s;
  overflow: hidden;
}

.accordion-content p {
  min-height: 0; /* gridでのアニメーションを正しく動かすための記述 */
  padding: 0 20px;
}

/* --- 開いた時(.is-open クラスがついた時)のスタイル --- */

/* 縦線を0度に戻して横線と重ねる(=マイナスに見える) */
.accordion-item.is-open .accordion-header::after {
  transform: rotate(0deg);
}

/* 中身の表示:高さを 1fr(中身に合わせる)に変更 */
.accordion-item.is-open .accordion-content {
  grid-template-rows: 1fr;
  padding-bottom: 20px;
}

疑似要素で作る「+」と「ー」

  • 疑似要素とは、HTMLソースに手を加えることなくCSSで特定の要素の一部や仮想的な要素を追加・装飾できる機能
  • 画像を使わずに、CSSの ::before(横線)と ::after(縦線)だけでアイコンを作っている
  • 開いた時に transform: rotate(0deg); で縦線を横線と全く同じ向きに重ねることで、消えたように見せて「ー」を表現する
    /* +アイコンの共通スタイル(横線と縦線) */
    .accordion-header::before,
    .accordion-header::after {
      content: '';  /* 必ずcontentを指定 */
      position: absolute;
      right: 20px;
      width: 15px; /* 線の長さ */
      height: 2px; /* 線の太さ */
      background-color: #333;
      transition: all 0.3s; /* アニメーションの速度 */
    }
    
    /* 縦線:90度回転させて「+」を作る */
    .accordion-header::after {
      transform: rotate(90deg);
    }
    
    /* 縦線を0度に戻して横線と重ねる(=マイナスに見える) */
    .accordion-item.is-open .accordion-header::after {
      transform: rotate(0deg);
    }

高さのアニメーション手法

  • display: grid;grid-template-rows を使うことで「中身の量に関わらず、高さを0から1倍(1fr)まで滑らかに変化させる」 ことができる

状態の管理(is-open)

  • .is-open という「状態を表すクラス」を1つ付け外しするだけで、アイコンの形も中身の表示も一気に切り替える

JavaScript

const triggers = document.querySelectorAll('.js-accordion-trigger');

triggers.forEach(trigger => {
  trigger.addEventListener('click', () => {
    // 親要素(accordion-item)を取得してクラスを切り替える
    const parent = trigger.parentNode;
    parent?.classList.toggle('is-open');
  });
});

1. 操作したい要素を「全部」つかまえる

  • querySelectorAll:
    ページ内にある .js-accordion-trigger というクラスがついた要素を、全部まとめて持ってくる
  • getElementById は1つしか捕まえられないけど、querySelectorAll とクラス(.js-accordion-trigger)を使えば、ページ内にアコーディオンが何個あっても、この1通りのコードだけで全部動くようになる

2. 一つひとつのボタンに「見張り役」をつける

  • forEach:
    「全部まとめて」持ってきたボタンたちを、1つずつ順番に処理する(ループ処理)
  • addEventListener('click', ...):
    それぞれのボタンに対して、「クリックされたら、この中の命令を実行してね!」という見張り役(イベントリスナー)をセット

3. 親要素の「状態(クラス)」を切り替える

  • parentNode:
    クリックされたボタンのすぐ外側にある親要素(.accordion-item)を探しに行く
  • classList.toggle('is-open'):
    .is-open というクラスが「なければ付ける、あれば外す」という切り替え(トグル)をやる

実はHTML/CSSだけでも作れる!

完成イメージ

Q1. detailsタグのメリットは?

JSが不要で、HTMLだけで完結するのが最大の魅力だね!

コード

<div class="html-accordion">
  <details class="html-accordion-item">
    <summary class="html-accordion-header">
      Q1. detailsタグのメリットは?
    </summary>
    <div class="html-accordion-content">
      <p>JSが不要で、HTMLだけで完結するのが最大の魅力だね!</p>
    </div>
  </details>
</div>

<details> :折りたたみの「親玉」

  • これがアコーディオン全体の「箱」になる
  • クリックされると、ブラウザが自動的に open という属性を付け外ししてくれる

<summary> :クリックする「見出し」

  • details の中の一番上に書くことで、常に表示される「タイトル」になる
  • ここをクリックすると、箱が開いたり閉じたりするスイッチの役割を果たす

隠される「コンテンツ」

  • summary より下に書いた要素は、すべて「開いた時だけ見える中身」になる
  • div で囲むことで、余白をつけたりなどCSSの調整がしやすくなる
/* アイテム全体の枠線(薄いグレー) */
.html-accordion-item {
  border-bottom: 1px solid #eee;
}

/* --- 【通常時】の見出し --- */
.html-accordion-header {
  padding: 20px;
  font-weight: bold;
  cursor: pointer;
  list-style: none;
  background: #f9f9f9;
  color: #333;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* 擬似要素で「+」を作る */
.html-accordion-header::after {
  content: '+';
  font-size: 1.2rem;
  color: #1e90ff;
  /* +だけ青くして目立たせる */
}

/* 「+」を「ー」に変える */
.html-accordion-item[open] .html-accordion-header::after {
  content: 'ー';
}

/* 中身の余白とデザイン */
.html-accordion-content {
  padding: 20px 20px;
}

/* デフォルトの矢印を消す */
.html-accordion-header::-webkit-details-marker {
  display: none;
}
  • details[open] というセレクタを使うことで、「開いている時だけ」のスタイルを指定できるのが最大の特徴
  • details タグはパッと切り替わるのがデフォルト

JavaScript方式とdetailsタグ方式の比較

JavaScript方式

  • 難易度:少し高い(JSの知識が必要)
  • 自由度:複雑な動きも自由自在
  • 推奨シーン:高度な演出や、特殊な連動をさせたい時

detailsタグ方式

  • 難易度:かんたん(HTMLタグだけ)
  • 自由度:シンプルな開閉がメイン
  • 推奨シーン:FAQなど、標準的なアコーディオンを作る時

スライダーを実装してみよう

完成イメージ

スライド1
スライド2
スライド3

スライダーの実装は「ライブラリ」を使う

実務では「Swiper(スワイパー)」というライブラリ(読み込んで使える便利なコード部品)を使用

なぜライブラリを使う?

スライダーをゼロから作るのは、実はとても大変

  • スワイプ・フリック操作に対応させるのが難しい
  • 無限ループさせようとするとバグりやすい

既に完成されたパーツ(ライブラリ)を借りてくることで、品質も安定し開発スピードも上がる

Swiperを使うまでの流れ

① ライブラリを読み込む
② HTMLを書く
③ JavaScriptで初期化
④ CSSで見た目を調整する

① ライブラリを読み込む

CDN読み込み

Swiperを使うため、今回は CDN を使用して CSS と JS の2つのコードを読み込む

CDNは「ネット上にあるお店から、必要なパーツをレンタルしてくるURL」というイメージ

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />

<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
  • CSS(見た目): <head> タグの中に貼る
  • JS(動き): </body> の直前に貼る

② HTMLを書く

HTML

<div class="swiper js-008swiper">
  <div class="swiper-wrapper">
    <div class="swiper-slide slide01">スライド1</div>
    <div class="swiper-slide slide02">スライド2</div>
    <div class="swiper-slide slide03">スライド3</div>
  </div>

  <!-- ナビゲーション -->
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>

  <!-- ページネーション -->
  <div class="swiper-pagination"></div>
</div>

Swiperには「この順番でタグを書いてね」という厳格なルールがある

  • .swiper → スライダー全体
  • .swiper-wrapper → スライドの親
  • .swiper-slide → 1枚ずつ

上記はSwiperが動くために変えてはいけないクラス、最低限この3つが必須

.js-008swiper は、自分たちがJSで操作するための目印なので変えてもOK

③ JavaScriptで初期化

JavaScript

Swiperを使用するためには、初期化が必要

const swiper = new Swiper(".js-008swiper", { ... });で定義する

const swiper = new Swiper(".js-008swiper", {
  loop: true, // 最後まで行ったら最初に戻る(無限ループ)
  // ナビゲーションボタン
  navigation: {
    nextEl: '.swiper-button-next',
    prevEl: '.swiper-button-prev',
  },
  // ページネーション
  pagination: {
    el: ".swiper-pagination", // 表示する場所を指定
    clickable: true,          // ドットをクリックして飛ばせるようにする
  },
  slidesPerView: 1, // スライドの枚数
});

よく使う基本オプション

const swiper = new Swiper(".swiper", {
  loop: true,   // 無限ループ

  // 自動再生
  autoplay: {
    delay: 3000,  // 3秒ごとに次のスライドへ
  },

  speed: 800,  // スライド間の遷移時間(ミリ秒)
  slidesPerView: 1, // 表示されるスライドの数
  spaceBetween: 20, // スライド間の余白

  // 画面幅によって枚数を変える(レスポンシブ)
  breakpoints: {
    768: {  // 768px以上の場合
      slidesPerView: 2,
    },
  },
});

オプションを増やす際のポイント

  • オプションを増やす時は、前の設定の最後に必ずカンマ( , )を付ける
  • オプションを全部暗記するのは難しいので、必要に応じてコードを調べて使う

④ CSSで見た目を調整する

Swiperのデフォルトのデザインを、CSSで「上書き」していく
ライブラリが用意してくれている「決まったクラス名」を狙い撃ちして、自分好みにアレンジする

左右の「矢印(ナビゲーション)」の色を変える

/* 矢印のデザインを変える */
.swiper-button-prev,
.swiper-button-next {
  background-color: #fff;
  color: #ff6347;
  border-radius: 50%;
  box-sizing: border-box;
  padding: 13px;
}

下の「ドット(ページネーション)」の色を変える

/* ドット全体のベース色 */
.swiper-pagination-bullet {
  background: #fff;
  opacity: 1;
  transition: all .3s;
}

/* 「今表示されているスライド」のドット色 */
.swiper-pagination-bullet-active {
  background: #ff6347;
  width: 20px;
  border-radius: 4px;
}

-active がついているのが、今表示されているスライド

CSSで調整する際のポイント

  • Swiperがどんなクラス名を使っているかは、検証ツールで確認できるので探してみる
  • Swiperの元のデザインが強すぎて色が変わらない時は、クラス名を .js-008swiper .swiper-button-next のように、自分の付けたクラス名を頭に添えて『より詳しく』指定してあげる

Webサイトでの使いどころ

ハンバーガーメニュー

  • スマホサイトのメインナビゲーション
  • ほぼ必須、どのサイトでも使われている

アコーディオンメニュー

  • FAQ(よくある質問)や、製品の仕様書などの補足情報の整理
  • 情報量は多いけど、最初から全部見せるとユーザーが疲れてしまうような場所に最適

スライダー

  • メインビジュアルや、実績・商品の紹介
  • 限られたスペースで大量の情報(写真など)を効率よく見せられる

ミニ課題

課題①:ハンバーガーのメニュー色を変えてみよう

  • 背景を自分の好きな色に、文字を白に変えてみよう
  • 画面の「右から」ではなく「上から」メニューが降ってくるように、CSSの top や right の数値をいじってみよう

課題②:アコーディオンに「矢印」をつけてみよう

  • + ではなく、下向き矢印 v が回転して上向きになるように transform: rotate() を調整してみよう

課題③:Swiperを「2枚表示」にしてみよう

  • slidesPerView を使って、PCの時だけスライドが2枚見えるように設定してみよう

この回のまとめ

  • コンポーネント = 再利用できる部品(1つ作れば一生モノ)
    今日作ったコードは、これからの制作で何度でも使い回せる武器(コンポーネント庫)になる
  • JSでクラスをつけて、見た目はCSSで調整する
    役割を完全に分けることで、エラーが起きた際や修正する際にメンテナンスしやすい
  • 必要に応じてライブラリを使用する
    簡単なものは自分で作り(アコーディオン)、複雑なものはプロの道具を借りる(Swiper)