【Dawn】コピペOK!自動表示付き期間限定バナーセクションの実装方法

はじめに

ECサイト運営において、季節商品のプロモーションやタイムセール、キャンペーンなどの期間限定施策は売上向上の重要な要素です。
そういった施策を打つ際には、販促用のバナーやポップアップをサイトに表示するのが鉄板。
Shopifyでも、ポップアップ専用の無料アプリが多数存在しています。

しかし、外部アプリは仕様変更などによって正常に動作しなくなったり、日本語対応していないことで設定までのハードルが高かったりと課題も少なくありません。
とはいえ、アプリを使わずバナーの表示・非表示を手動管理するのは運用負担が大きく、表示し忘れ・消し忘れといったヒューマンエラーのリスクも高まります。

そこで本記事では、ShopifyのDawnテーマにおいて、指定した日時で自動的にバナーを表示・非表示する仕組みのセクションを実装する方法をご紹介します。

このセクションの特徴は以下の通りです。
1. 日時指定での自動表示制御
2. レスポンシブデザイン対応
3. テーマエディタでの簡単設定
4. 購買意欲向上効果のあるカウントダウン機能搭載

コードもそのままコピペで使えるため、コーディング知識があまりない方でもお試しいただけます。
ぜひ最後までご覧ください!

実装方法

1. セクションファイルの作成

まず、期間限定バナー用のセクションファイルを作成します。
コード編集画面のEXPLORERで”sections”を右クリックし、 「New File...」を選択します。
ファイル名「seasonal-banner.liquid」で新規ファイルを作成し、下記コードをそのままコピペして保存すればセクションファイルの完成です。

{%- if section.settings.enable_banner -%}
  <div id="seasonal-banner-{{ section.id }}" class="seasonal-banner-wrapper" style="display: none;">
    <!-- メインバナー表示 -->
    <div class="seasonal-banner seasonal-banner--main" id="main-banner-{{ section.id }}"
         style="background-color: {{ section.settings.background_color }}; color: {{ section.settings.text_color }};">
      
      {%- if section.settings.banner_url != blank -%}
        <a href="{{ section.settings.banner_url }}" class="seasonal-banner__link">
      {%- endif -%}
      
      <div class="seasonal-banner__content">
        {%- if section.settings.banner_icon != blank -%}
          <div class="seasonal-banner__icon">
            <span class="banner-icon">{{ section.settings.banner_icon }}</span>
          </div>
        {%- endif -%}
        
        <div class="seasonal-banner__text">
          {%- if section.settings.banner_title != blank -%}
            <h3 class="seasonal-banner__title">{{ section.settings.banner_title }}</h3>
          {%- endif -%}
          
          {%- if section.settings.banner_subtitle != blank -%}
            <p class="seasonal-banner__subtitle">{{ section.settings.banner_subtitle }}</p>
          {%- endif -%}
          
          {%- if section.settings.show_countdown -%}
            <div class="seasonal-banner__countdown" id="countdown-{{ section.id }}"></div>
          {%- endif -%}
        </div>
        
        {%- if section.settings.banner_cta != blank -%}
          <div class="seasonal-banner__cta">
            <span class="seasonal-banner__button">{{ section.settings.banner_cta }}</span>
          </div>
        {%- endif -%}
      </div>
      
      {%- if section.settings.banner_url != blank -%}
        </a>
      {%- endif -%}
      
      {%- if section.settings.show_close_button -%}
        <button class="seasonal-banner__close" onclick="closeBanner('{{ section.id }}')">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
            <path d="M12.854 4.854a.5.5 0 0 0-.708-.708L8 8.293 3.854 4.146a.5.5 0 1 0-.708.708L7.293 9l-4.147 4.146a.5.5 0 0 0 .708.708L8 9.707l4.146 4.147a.5.5 0 0 0 .708-.708L8.707 9l4.147-4.146z"/>
          </svg>
        </button>
      {%- endif -%}
    </div>

    <!-- ティーザーバナー表示 -->
    <div class="seasonal-banner seasonal-banner--teaser" id="teaser-banner-{{ section.id }}" style="display: none;"
         onclick="expandBanner('{{ section.id }}')"
         data-teaser-text="{{ section.settings.teaser_text | default: section.settings.banner_title }}">
      
      <div class="seasonal-banner__teaser-content">
        {%- if section.settings.banner_icon != blank -%}
          <div class="seasonal-banner__teaser-icon">
            <span class="banner-icon">{{ section.settings.banner_icon }}</span>
          </div>
        {%- endif -%}
        
        <div class="seasonal-banner__teaser-text">
          <span class="seasonal-banner__teaser-title">
            {{ section.settings.teaser_text | default: section.settings.banner_title }}
          </span>
          {%- if section.settings.show_countdown -%}
            <div class="seasonal-banner__teaser-countdown" id="teaser-countdown-{{ section.id }}"></div>
          {%- endif -%}
        </div>
        
        <div class="seasonal-banner__teaser-expand">
          <svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
            <path d="M2 4l4 4 4-4" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
          </svg>
        </div>
      </div>
      
      <button class="seasonal-banner__teaser-close" onclick="event.stopPropagation(); dismissBanner('{{ section.id }}')">
        <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor">
          <path d="M11 3L3 11M3 3L11 11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
        </svg>
      </button>
    </div>
  </div>

  <style>
    /* メインバナー */
    #seasonal-banner-{{ section.id }} .seasonal-banner {
      padding: 16px 20px;
      display: flex;
      align-items: center;
      justify-content: center;
      min-height: 60px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
      background: {{ section.settings.background_color | default: '#ff6b35' }};
      color: {{ section.settings.text_color | default: '#ffffff' }};
      transition: all 0.3s ease;
      {% if section.settings.banner_position == 'bottom' %}
        position: fixed;
        bottom: 20px;
        left: 50%;
        transform: translateX(-50%) translateY(0); /* ← Yを明示 */
        width: calc(100% - 40px);
        max-width: 800px;
        border-radius: 12px;
        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
        z-index: 9999;
      {% endif %}
    }

    /* ティーザーバナー */
    #seasonal-banner-{{ section.id }} .seasonal-banner--teaser {
      background: {{ section.settings.background_color | default: '#ff6b35' }};
      color: {{ section.settings.text_color | default: '#ffffff' }};
      padding: 8px 16px;
      min-height: 40px;
      cursor: pointer;
      opacity: 0.9;
      {% if section.settings.banner_position == 'bottom' %}
        position: fixed;
        bottom: 20px;
        left: 50%;
        transform: translateX(-50%) translateY(0); /* ← hover と揃える */
        width: calc(100% - 40px);
        max-width: 400px;
        border-radius: 8px;
        z-index: 9999;
      {% endif %}
    }

    /* hover 時 */
    #seasonal-banner-{{ section.id }} .seasonal-banner--teaser:hover {
      opacity: 1;
      {% if section.settings.banner_position == 'bottom' %}
        transform: translateX(-50%) translateY(-2px);
      {% else %}
        transform: translateY(-2px);
      {% endif %}
      box-shadow: 0 6px 25px rgba(0, 0, 0, 0.25);
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-content {
      display: flex;
      align-items: center;
      gap: 12px;
      width: 90%;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-icon {
      flex-shrink: 0;
      background: rgba(255, 255, 255, 0.2);
      width: 32px;
      height: 32px;
      line-height: 32px;
      border-radius: 50%;
      text-align: center;
      backdrop-filter: blur(4px);
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-icon .banner-icon {
      font-size: 18px;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-text {
      flex: 1;
      min-width: 0;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-title {
      font-size: 14px;
      font-weight: 600;
      line-height: 1.2;
      display: block;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-countdown {
      font-size: 12px;
      font-weight: 500;
      opacity: 0.9;
      margin-top: 2px;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-expand {
      position: relative;
      flex-shrink: 0;
      opacity: 0.7;
      transition: all 0.3s ease;
      animation: bounceUpDown 2s infinite;
      z-index: 1;
      margin-left: auto;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner--teaser:hover .seasonal-banner__teaser-expand {
      opacity: 1;
      animation-play-state: paused;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-close {
      position: absolute;
      right: 8px;
      top: 50%;
      transform: translateY(-50%);
      background: none;
      border: none;
      color: inherit;
      cursor: pointer;
      padding: 4px;
      border-radius: 4px;
      opacity: 0.6;
      transition: all 0.3s ease;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-close:hover {
      opacity: 1;
      background: rgba(255, 255, 255, 0.2);
    }

    /* 既存のメインバナースタイル */
    #seasonal-banner-{{ section.id }} .seasonal-banner__link {
      color: inherit;
      text-decoration: none;
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__link:hover {
      color: inherit;
      opacity: 0.9;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__content {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 16px;
      flex-wrap: wrap;
      text-align: center;
      width: 90%;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__icon {
      flex-shrink: 0;
      background: #fff;
      width: 42px;
      height: 42px;
      line-height: 42px;
      border-radius: 50%;
    }

    #seasonal-banner-{{ section.id }} .banner-icon {
      font-size: 27px;
      display: inline-block;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__text {
      flex: 1;
      min-width: 200px;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__title {
      color: inherit;
      font-size: {{ section.settings.title_size | default: 18 }}px;
      font-weight: 700;
      margin: 0 0 4px 0;
      line-height: 1.2;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__subtitle {
      font-size: {{ section.settings.subtitle_size | default: 14 }}px;
      margin: 0;
      opacity: 0.9;
      line-height: 1.3;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__countdown {
      font-size: 16px;
      font-weight: 600;
      margin-top: 6px;
      background: rgba(255, 255, 255, 0.2);
      padding: 6px 12px;
      border-radius: 6px;
      display: inline-block;
      backdrop-filter: blur(4px);
      border: 1px solid rgba(255, 255, 255, 0.1);
      animation: pulse 1s infinite;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__cta {
      flex-shrink: 0;
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__button {
      background: rgba(255, 255, 255, 0.2);
      color: inherit;
      padding: 10px 20px;
      border-radius: 8px;
      font-size: 14px;
      font-weight: 600;
      transition: all 0.3s ease;
      border: 1px solid rgba(255, 255, 255, 0.3);
      cursor: pointer;
      display: inline-block;
      backdrop-filter: blur(4px);
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__button:hover {
      background: rgba(255, 255, 255, 0.3);
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__close {
      position: absolute;
      right: 16px;
      top: 50%;
      transform: translateY(-50%);
      background: none;
      border: none;
      color: inherit;
      cursor: pointer;
      padding: 6px;
      border-radius: 6px;
      opacity: 0.7;
      transition: all 0.3s ease;
      backdrop-filter: blur(4px);
    }

    #seasonal-banner-{{ section.id }} .seasonal-banner__close:hover {
      opacity: 1;
    }

    /* アニメーション効果 */
    #seasonal-banner-{{ section.id }} .seasonal-banner-wrapper {
      {% if section.settings.banner_position == 'bottom' %}
        animation: slideInBannerBottom 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
      {% else %}
        animation: slideInBannerTop 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
      {% endif %}
    }

    /* 上部配置用 */
    @keyframes slideInBannerTop {
      from {
        opacity: 0;
        transform: translateY(-100%);
      }
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }
    @keyframes slideOutBannerTop {
      from {
        opacity: 1;
        transform: translateY(0);
      }
      to {
        opacity: 0;
        transform: translateY(-100%);
      }
    }

    /* 下部配置用 */
    @keyframes slideInBannerBottom {
      from {
        opacity: 0;
        transform: translateX(-50%) translateY(100%);
      }
      to {
        opacity: 1;
        transform: translateX(-50%) translateY(0);
      }
    }
    @keyframes slideOutBannerBottom {
      from {
        opacity: 1;
        transform: translateX(-50%) translateY(0);
      }
      to {
        opacity: 0;
        transform: translateX(-50%) translateY(100%);
      }
    }

    @keyframes pulse {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.7; }
    }

    @keyframes bounceUpDown {
      0%, 20%, 50%, 80%, 100% {
        transform: translateY(0);
      }
      40% {
        transform: translateY(-3px);
      }
      60% {
        transform: translateY(-1px);
      }
    }

    /* レスポンシブ対応 */
    @media screen and (max-width: 768px) {
      #seasonal-banner-{{ section.id }} .seasonal-banner {
        padding: 12px 16px;
        min-height: 50px;
        {%- if section.settings.banner_position == 'bottom' -%}
          width: calc(100% - 20px);
          bottom: 10px;
        {%- endif -%}
      }
      
      #seasonal-banner-{{ section.id }} .seasonal-banner--teaser {
        justify-content: left;
        padding: 6px 12px;
      }
      
      #seasonal-banner-{{ section.id }} .seasonal-banner__content {
        gap: 12px;
        {%- unless section.settings.keep_horizontal_mobile -%}
          flex-direction: column;
        {%- endunless -%}
      }
      
      #seasonal-banner-{{ section.id }} .seasonal-banner__icon {
        width: 35px;
        height: 35px;
        line-height: 35px;
      }

      #seasonal-banner-{{ section.id }} .banner-icon {
        font-size: 20px;
      }

      #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-icon {
        width: 28px;
        height: 28px;
        line-height: 28px;
      }

      #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-icon .banner-icon {
        font-size: 16px;
      }
      
      #seasonal-banner-{{ section.id }} .seasonal-banner__title {
        font-size: {{ section.settings.title_size | default: 18 | minus: 2 }}px;
      }
      
      #seasonal-banner-{{ section.id }} .seasonal-banner__subtitle {
        font-size: {{ section.settings.subtitle_size | default: 14 | minus: 1 }}px;
      }
      
      #seasonal-banner-{{ section.id }} .seasonal-banner__countdown {
        font-size: 14px;
        padding: 4px 8px;
      }
      
      #seasonal-banner-{{ section.id }} .seasonal-banner__button {
        font-size: 13px;
        padding: 8px 16px;
      }
      
      #seasonal-banner-{{ section.id }} .seasonal-banner__close {
        right: 12px;
      }

      #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-title {
        font-size: 13px;
      }

      #seasonal-banner-{{ section.id }} .seasonal-banner__teaser-countdown {
        font-size: 11px;
      }
    }

    @media screen and (max-width: 480px) {
      #seasonal-banner-{{ section.id }} .seasonal-banner__content {
        text-align: center;
        gap: 8px;
      }
      
      #seasonal-banner-{{ section.id }} .seasonal-banner__text {
        min-width: auto;
      }
      
      #seasonal-banner-{{ section.id }} .seasonal-banner__title {
        font-size: {{ section.settings.title_size | default: 18 | minus: 3 }}px;
      }
    }
  </style>

  <script>
    window.countdownTimers = { main: null, teaser: null };

    function clearCountdown(type) {
      if (window.countdownTimers[type]) {
        clearInterval(window.countdownTimers[type]);
        window.countdownTimers[type] = null;
      }
    }

    function startCountdown(endTime, elementId, type, banner, mainBanner, teaserBanner) {
      const countdownElement = document.getElementById(elementId);
      if (!countdownElement) return;

      function updateCountdown() {
        const now = new Date();
        const jstOffset = 9 * 60;
        const utc = now.getTime() + (now.getTimezoneOffset() * 60000);
        const jst = new Date(utc + (jstOffset * 60000));

        const timeDiff = endTime.getTime() - jst.getTime();
        if (timeDiff <= 0) {
          countdownElement.innerHTML = 'キャンペーン終了';
          setTimeout(() => { banner.style.display = 'none'; }, 2000);
          clearCountdown(type);
          return;
        }

        const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
        const hours = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60));
        const seconds = Math.floor((timeDiff % (1000 * 60)) / 1000);

        let countdownHTML = '';
        {% case section.settings.countdown_format %}
          {% when 'compact' %}
            if (days > 0) countdownHTML += `${days}d `;
            countdownHTML += `${hours.toString().padStart(2,'0')}:${minutes.toString().padStart(2,'0')}:${seconds.toString().padStart(2,'0')}`;
          {% when 'detailed' %}
            if (days > 0) countdownHTML += `${days}日 `;
            if (hours > 0) countdownHTML += `${hours}時間 `;
            if (minutes > 0) countdownHTML += `${minutes}分 `;
            countdownHTML += `${seconds}秒`;
          {% else %}
            countdownHTML += '残り ';
            if (days > 0) countdownHTML += `${days}日 `;
            countdownHTML += `${hours.toString().padStart(2,'0')}:${minutes.toString().padStart(2,'0')}:${seconds.toString().padStart(2,'0')}`;
        {% endcase %}

        countdownElement.innerHTML = countdownHTML;
      }

      updateCountdown();
      clearCountdown(type);
      window.countdownTimers[type] = setInterval(updateCountdown, 1000);
    }

    document.addEventListener('DOMContentLoaded', function() {
      const bannerId = 'seasonal-banner-{{ section.id }}';
      const banner = document.getElementById(bannerId);
      const mainBanner = document.getElementById('main-banner-{{ section.id }}');
      const teaserBanner = document.getElementById('teaser-banner-{{ section.id }}');
      const startDate = '{{ section.settings.start_date }}';
      const endDate = '{{ section.settings.end_date }}';
      const startTime = '{{ section.settings.start_time }}';
      const endTime = '{{ section.settings.end_time }}';
      const showCountdown = {{ section.settings.show_countdown }};
      const bannerPosition = "{{ section.settings.banner_position }}";

      function checkBannerDisplay() {
        const now = new Date();
        const jstOffset = 9 * 60;
        const utc = now.getTime() + (now.getTimezoneOffset() * 60000);
        const jst = new Date(utc + (jstOffset * 60000));
        const startDateTime = new Date(startDate + 'T' + startTime + ':00+09:00');
        const endDateTime = new Date(endDate + 'T' + endTime + ':00+09:00');

        if (jst < startDateTime || jst > endDateTime) {
          banner.style.display = 'none';
          return;
        }

        const closedTimestamp = localStorage.getItem('banner-closed-{{ section.id }}');
        const dismissedTimestamp = localStorage.getItem('banner-dismissed-{{ section.id }}');
        const currentTime = Date.now();
        const TWELVE_HOURS = 12 * 60 * 60 * 1000;

        if (dismissedTimestamp && currentTime - parseInt(dismissedTimestamp) < TWELVE_HOURS) {
          banner.style.display = 'none';
          return;
        }

        banner.style.display = 'block';

        if (closedTimestamp) {
          mainBanner.style.display = 'none';
          teaserBanner.style.display = 'flex';
          if (showCountdown) startCountdown(endDateTime, 'teaser-countdown-{{ section.id }}', 'teaser', banner, mainBanner, teaserBanner);
        } else {
          mainBanner.style.display = 'flex';
          teaserBanner.style.display = 'none';
          if (showCountdown) startCountdown(endDateTime, 'countdown-{{ section.id }}', 'main', banner, mainBanner, teaserBanner);
        }
      }

      checkBannerDisplay();
      setInterval(checkBannerDisplay, 60000);

      // グローバル関数
      window.closeBanner = function(sectionId) {
        const animOut = bannerPosition === "bottom" ? "slideOutBannerBottom" : "slideOutBannerTop";
        const animIn  = bannerPosition === "bottom" ? "slideInBannerBottom"  : "slideInBannerTop";

        mainBanner.style.animation = animOut + " 0.4s ease-in forwards";
        setTimeout(() => {
          mainBanner.style.display = 'none';
          teaserBanner.style.display = 'flex';
          teaserBanner.style.animation = animIn + " 0.4s ease-out forwards";
          if (showCountdown) {
            const endDateTime = new Date(endDate + 'T' + endTime + ':00+09:00');
            startCountdown(endDateTime, 'teaser-countdown-' + sectionId, 'teaser', banner, mainBanner, teaserBanner);
          }
        }, 400);

        localStorage.setItem('banner-closed-' + sectionId, Date.now().toString());
      }

      window.expandBanner = function(sectionId) {
        const animOut = bannerPosition === "bottom" ? "slideOutBannerBottom" : "slideOutBannerTop";
        const animIn  = bannerPosition === "bottom" ? "slideInBannerBottom"  : "slideInBannerTop";

        teaserBanner.style.animation = animOut + " 0.3s ease-in forwards";
        setTimeout(() => {
          teaserBanner.style.display = 'none';
          mainBanner.style.display = 'flex';
          mainBanner.style.animation = animIn + " 0.4s ease-out forwards";
          if (showCountdown) {
            const endDateTime = new Date(endDate + 'T' + endTime + ':00+09:00');
            startCountdown(endDateTime, 'countdown-' + sectionId, 'main', banner, mainBanner, teaserBanner);
          }
        }, 300);

        localStorage.removeItem('banner-closed-' + sectionId);
      }

      window.dismissBanner = function(sectionId) {
        const animOut = bannerPosition === "bottom" ? "slideOutBannerBottom" : "slideOutBannerTop";
        banner.style.animation = animOut + " 0.4s ease-in forwards";
        setTimeout(() => { banner.style.display = 'none'; }, 400);
        localStorage.setItem('banner-dismissed-' + sectionId, Date.now().toString());
      }
    });
  </script>
{%- endif -%}

{% schema %}
{
  "name": "期間限定バナー",
  "settings": [
    {
      "type": "checkbox",
      "id": "enable_banner",
      "label": "バナーを有効にする",
      "default": true
    },
    {
      "type": "header",
      "content": "表示期間設定"
    },
    {
      "type": "text",
      "id": "start_date",
      "label": "開始日(YYYY-MM-DD形式)",
      "info": "例:2025-01-01"
    },
    {
      "type": "text",
      "id": "start_time",
      "label": "開始時刻(HH:MM形式)",
      "default": "00:00",
      "info": "24時間表記で入力(例:09:00, 14:30)"
    },
    {
      "type": "text",
      "id": "end_date",
      "label": "終了日(YYYY-MM-DD形式)",
      "info": "例:2025-12-31"
    },
    {
      "type": "text",
      "id": "end_time",
      "label": "終了時刻(HH:MM形式)",
      "default": "23:59",
      "info": "24時間表記で入力(例:23:59, 18:00)"
    },
    {
      "type": "header",
      "content": "バナー内容設定"
    },
    {
      "type": "text",
      "id": "banner_icon",
      "label": "アイコン(絵文字)",
      "info": "絵文字を1つ入力(例:⚡, 🎁, 🛍️, 🎄)",
      "default": "🎯"
    },
    {
      "type": "text",
      "id": "banner_title",
      "label": "バナータイトル",
      "default": "期間限定キャンペーン開催中!"
    },
    {
      "type": "text",
      "id": "banner_subtitle",
      "label": "バナーサブタイトル",
      "default": "今だけお得な特別価格でご提供"
    },
    {
      "type": "url",
      "id": "banner_url",
      "label": "リンクURL"
    },
    {
      "type": "text",
      "id": "banner_cta",
      "label": "CTAボタンテキスト",
      "default": "詳細はこちら"
    },
    {
      "type": "header",
      "content": "ティーザー設定"
    },
    {
      "type": "text",
      "id": "teaser_text",
      "label": "ティーザー表示テキスト",
      "info": "空白の場合はバナータイトルが使用されます",
      "placeholder": "期間限定キャンペーン中"
    },
    {
      "type": "header",
      "content": "カウントダウン設定"
    },
    {
      "type": "checkbox",
      "id": "show_countdown",
      "label": "カウントダウンを表示",
      "default": true
    },
    {
      "type": "select",
      "id": "countdown_format",
      "label": "カウントダウン表示形式",
      "options": [
        {
          "value": "standard",
          "label": "標準(残り XX日 HH:MM:SS)"
        },
        {
          "value": "compact",
          "label": "コンパクト(XXd HH:MM:SS)"
        },
        {
          "value": "detailed",
          "label": "詳細(XX日 XX時間 XX分 XX秒)"
        }
      ],
      "default": "standard"
    },
    {
      "type": "checkbox",
      "id": "show_close_button",
      "label": "閉じるボタンを表示",
      "default": true
    },
    {
      "type": "header",
      "content": "デザイン設定"
    },
    {
      "type": "select",
      "id": "banner_position",
      "label": "バナー表示位置",
      "options": [
        {
          "value": "top",
          "label": "ページ上部"
        },
        {
          "value": "bottom",
          "label": "ページ下部(フローティング)"
        }
      ],
      "default": "top"
    },
    {
      "type": "color",
      "id": "background_color",
      "label": "背景色",
      "default": "#ff6b35"
    },
    {
      "type": "color",
      "id": "text_color",
      "label": "テキスト色",
      "default": "#ffffff"
    },
    {
      "type": "range",
      "id": "title_size",
      "min": 14,
      "max": 24,
      "step": 1,
      "unit": "px",
      "label": "タイトルサイズ",
      "default": 18
    },
    {
      "type": "range",
      "id": "subtitle_size",
      "min": 12,
      "max": 18,
      "step": 1,
      "unit": "px",
      "label": "サブタイトルサイズ",
      "default": 14
    },
    {
      "type": "checkbox",
      "id": "keep_horizontal_mobile",
      "label": "モバイルでも横並びレイアウトを維持",
      "default": false,
      "info": "チェックを外すと、モバイルでは縦並びレイアウトになります"
    }
  ],
  "presets": [
    {
      "name": "期間限定バナー"
    }
  ]
}
{% endschema %}

2. バナーセクションの追加・設定

テーマ > カスタマイズ にて、「セクションを追加」から「期間限定バナー」を選択し、任意の場所に配置します。
表示期間やバナー内容、表示オプション(カウントダウン・閉じるボタンなど)を入力して保存すれば、サイト上にバナーが表示されるようになります。

本記事ではタイムセールバナーとして下記のようなバナーをヘッダー下に追加しました。

エンドユーザーが閉じるボタンを一度クリックするとティーザーが表示され、いつでもバナーを再表示できるようになっています。
ティーザーを閉じた場合は完全に非表示となりますが、エンドユーザーが12時間後に再訪問した場合やキャッシュクリアされた場合はバナーが再度表示されます。


上記活用例の他にも、以下のように幅広い応用が可能です。

■季節キャンペーン

開始日時:2025-12-01 00:00
終了日時:2025-12-25 23:59
タイトル:クリスマス特別セール開催中!
カウントダウン:有効

■新商品告知

開始日時:2025-09-01 00:00
終了日時:2025-09-22 23:59
タイトル:NEW ARRIVAL 先行予約開始!
カウントダウン:有効

まとめ

いかがでしたか?
この期間限定バナーセクションの活用により、店舗運営者は一度の設定で自動的にキャンペーンバナーを管理でき、エンドユーザーには適切なタイミングで情報を提供できます。
カウントダウン機能により購買意欲も高められ、コンバージョン率の向上が期待できます。
ぜひ季節商品やタイムセール、キャンペーンの際にご活用くださいね。

実装する中でのご不明点や運用における課題、お悩みなどがあるマーチャントさんはどうぞお気軽にお問い合わせください!

ストア構築から運用コンサルまで売上UPのための全てをサポート!

共にストアを成長させる併走型コンサルサービスをご提供します
ご相談・ご質問はまずお気軽にご連絡ください

一覧に戻る