初回・データなし

点線の枠とプラスアイコンで「まだ何もない」ことを伝えるオンボーディング型の空状態。作成ボタンを押すたびに最初のプロジェクトが増えていく体験付きです。

#css#svg#empty

ライブデモ

コード

HTML
<!-- 空状態: 初回・データなし(オンボーディング)。プラスボタンが呼吸するように点滅し、クリックで最初のカードが生まれる -->
<main class="efr-card">
  <svg class="efr-illus" viewBox="0 0 200 170" width="168" height="143" role="img" aria-label="まだプロジェクトが無いことを示す、点線の枠とプラスアイコンのイラスト">
    <g class="efr-sparks" aria-hidden="true">
      <path class="efr-spark efr-spark--1" d="M28 38 L31 45 L38 48 L31 51 L28 58 L25 51 L18 48 L25 45 Z"></path>
      <path class="efr-spark efr-spark--2" d="M168 106 L170 111 L175 113 L170 115 L168 120 L166 115 L161 113 L166 111 Z"></path>
    </g>
    <rect class="efr-tile" x="34" y="18" width="132" height="118" rx="18"></rect>
    <g class="efr-plus-group">
      <circle class="efr-plus-ring" cx="100" cy="77" r="30"></circle>
      <path class="efr-plus" d="M100 63 V91 M86 77 H114"></path>
    </g>
  </svg>

  <h2 class="efr-title">最初のプロジェクトを作成しましょう</h2>
  <p class="efr-desc">ここにはまだ何もありません。新規プロジェクトを作成すると、この場所に一覧が表示されます。</p>

  <div class="efr-list" data-list aria-live="polite"></div>

  <button class="efr-cta" type="button" data-cta>プロジェクトを作成</button>
  <button class="efr-secondary" type="button" data-secondary>テンプレートから始める</button>
</main>
CSS
/* 空状態: 初回・データなし(オンボーディング) */
* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 32px 20px;
  font-family: "Hiragino Sans", "Yu Gothic UI", "Segoe UI", system-ui, -apple-system, sans-serif;
  background:
    radial-gradient(circle at 18% 18%, rgba(45, 212, 191, .16) 0%, transparent 46%),
    radial-gradient(circle at 86% 84%, rgba(110, 231, 183, .22) 0%, transparent 50%),
    #ecfdf5;
  color: #065f46;
}

.efr-card {
  width: 100%;
  max-width: 380px;
  background: #ffffff;
  border-radius: 24px;
  padding: 36px 32px 32px;
  text-align: center;
  box-shadow: 0 24px 60px -24px rgba(13, 148, 136, .26), 0 2px 8px rgba(6, 95, 70, .06);
  border: 1px solid rgba(45, 212, 191, .14);
}

.efr-illus { display: block; margin: 0 auto 8px; overflow: visible; }

.efr-tile {
  fill: rgba(45, 212, 191, .05);
  stroke: #5eead4;
  stroke-width: 3;
  stroke-dasharray: 8 7;
}

.efr-plus-group {
  transform-box: fill-box;
  transform-origin: center;
  animation: efr-breathe 2.6s ease-in-out infinite;
}
@keyframes efr-breathe {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.06); }
}
.efr-plus-ring {
  fill: #f0fdfa;
  stroke: #14b8a6;
  stroke-width: 3;
  opacity: .9;
}
.efr-plus {
  fill: none;
  stroke: #0f766e;
  stroke-width: 7;
  stroke-linecap: round;
}

.efr-spark { fill: #34d399; transform-box: fill-box; transform-origin: center; animation: efr-twinkle 2.8s ease-in-out infinite; }
.efr-spark--2 { animation-delay: 1.1s; }
@keyframes efr-twinkle {
  0%, 100% { opacity: .2; transform: scale(.7) rotate(0deg); }
  50% { opacity: 1; transform: scale(1) rotate(12deg); }
}

.efr-title { margin: 6px 0 10px; font-size: 21px; font-weight: 700; color: #065f46; }
.efr-desc { margin: 0 0 18px; font-size: 14.5px; line-height: 1.7; color: #4d7c6f; }

/* 作成されたプロジェクトのチップが並ぶ場所 */
.efr-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  min-height: 0;
  margin-bottom: 4px;
}
.efr-chip {
  font-size: 12.5px;
  font-weight: 600;
  color: #0f766e;
  background: #ccfbf1;
  border: 1px solid #5eead4;
  border-radius: 12px;
  padding: 8px 12px;
  animation: efr-pop .35s cubic-bezier(.34,1.56,.64,1);
}
@keyframes efr-pop {
  0% { opacity: 0; transform: scale(.6) translateY(6px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
.efr-list:not(:empty) { margin-top: 4px; margin-bottom: 18px; }

.efr-cta {
  appearance: none;
  border: none;
  cursor: pointer;
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 13px 30px;
  font-size: 15px;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(135deg, #14b8a6 0%, #0d9488 100%);
  border-radius: 999px;
  box-shadow: 0 12px 26px -10px rgba(13, 148, 136, .5);
  transition: transform .25s cubic-bezier(.22,1,.36,1), box-shadow .25s ease;
}
.efr-cta:hover { transform: translateY(-2px); box-shadow: 0 16px 32px -10px rgba(13, 148, 136, .6); }
.efr-cta:active { transform: translateY(0); }
.efr-cta.is-limit { animation: efr-shake .4s ease; }
@keyframes efr-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

.efr-secondary {
  appearance: none;
  border: none;
  background: none;
  cursor: pointer;
  display: block;
  margin: 12px auto 0;
  padding: 4px 6px;
  font-size: 13px;
  font-weight: 600;
  color: #0f766e;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.efr-secondary:hover { color: #065f46; }

@media (prefers-reduced-motion: reduce) {
  .efr-plus-group, .efr-spark { animation: none !important; }
}
JavaScript
// 空状態: 初回・データなし — 「プロジェクトを作成」を押すたびに簡易チップが追加される(最大4件)デモ
(() => {
  const cta = document.querySelector('[data-cta]');
  const secondary = document.querySelector('[data-secondary]');
  const list = document.querySelector('[data-list]');
  if (!cta || !list) return; // null安全

  const MAX = 4;
  let count = 0;

  cta.addEventListener('click', () => {
    if (count >= MAX) {
      cta.classList.remove('is-limit');
      void cta.offsetWidth;
      cta.classList.add('is-limit');
      return;
    }
    count += 1;
    const chip = document.createElement('span');
    chip.className = 'efr-chip';
    chip.textContent = `プロジェクト ${count}`;
    list.appendChild(chip);
  });

  if (secondary) {
    secondary.addEventListener('click', () => {
      // デモ用: テンプレートから作った1件目を追加する簡易フィードバック
      if (count >= MAX || list.childElementCount > 0) return;
      count += 1;
      const chip = document.createElement('span');
      chip.className = 'efr-chip';
      chip.textContent = 'テンプレート: 基本プラン';
      list.appendChild(chip);
    });
  }
})();

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「初回・データなし」の効果を追加してください。

# 追加してほしい効果
初回・データなし(アラート & 空状態)
点線の枠とプラスアイコンで「まだ何もない」ことを伝えるオンボーディング型の空状態。作成ボタンを押すたびに最初のプロジェクトが増えていく体験付きです。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 空状態: 初回・データなし(オンボーディング)。プラスボタンが呼吸するように点滅し、クリックで最初のカードが生まれる -->
<main class="efr-card">
  <svg class="efr-illus" viewBox="0 0 200 170" width="168" height="143" role="img" aria-label="まだプロジェクトが無いことを示す、点線の枠とプラスアイコンのイラスト">
    <g class="efr-sparks" aria-hidden="true">
      <path class="efr-spark efr-spark--1" d="M28 38 L31 45 L38 48 L31 51 L28 58 L25 51 L18 48 L25 45 Z"></path>
      <path class="efr-spark efr-spark--2" d="M168 106 L170 111 L175 113 L170 115 L168 120 L166 115 L161 113 L166 111 Z"></path>
    </g>
    <rect class="efr-tile" x="34" y="18" width="132" height="118" rx="18"></rect>
    <g class="efr-plus-group">
      <circle class="efr-plus-ring" cx="100" cy="77" r="30"></circle>
      <path class="efr-plus" d="M100 63 V91 M86 77 H114"></path>
    </g>
  </svg>

  <h2 class="efr-title">最初のプロジェクトを作成しましょう</h2>
  <p class="efr-desc">ここにはまだ何もありません。新規プロジェクトを作成すると、この場所に一覧が表示されます。</p>

  <div class="efr-list" data-list aria-live="polite"></div>

  <button class="efr-cta" type="button" data-cta>プロジェクトを作成</button>
  <button class="efr-secondary" type="button" data-secondary>テンプレートから始める</button>
</main>

【CSS】
/* 空状態: 初回・データなし(オンボーディング) */
* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 32px 20px;
  font-family: "Hiragino Sans", "Yu Gothic UI", "Segoe UI", system-ui, -apple-system, sans-serif;
  background:
    radial-gradient(circle at 18% 18%, rgba(45, 212, 191, .16) 0%, transparent 46%),
    radial-gradient(circle at 86% 84%, rgba(110, 231, 183, .22) 0%, transparent 50%),
    #ecfdf5;
  color: #065f46;
}

.efr-card {
  width: 100%;
  max-width: 380px;
  background: #ffffff;
  border-radius: 24px;
  padding: 36px 32px 32px;
  text-align: center;
  box-shadow: 0 24px 60px -24px rgba(13, 148, 136, .26), 0 2px 8px rgba(6, 95, 70, .06);
  border: 1px solid rgba(45, 212, 191, .14);
}

.efr-illus { display: block; margin: 0 auto 8px; overflow: visible; }

.efr-tile {
  fill: rgba(45, 212, 191, .05);
  stroke: #5eead4;
  stroke-width: 3;
  stroke-dasharray: 8 7;
}

.efr-plus-group {
  transform-box: fill-box;
  transform-origin: center;
  animation: efr-breathe 2.6s ease-in-out infinite;
}
@keyframes efr-breathe {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.06); }
}
.efr-plus-ring {
  fill: #f0fdfa;
  stroke: #14b8a6;
  stroke-width: 3;
  opacity: .9;
}
.efr-plus {
  fill: none;
  stroke: #0f766e;
  stroke-width: 7;
  stroke-linecap: round;
}

.efr-spark { fill: #34d399; transform-box: fill-box; transform-origin: center; animation: efr-twinkle 2.8s ease-in-out infinite; }
.efr-spark--2 { animation-delay: 1.1s; }
@keyframes efr-twinkle {
  0%, 100% { opacity: .2; transform: scale(.7) rotate(0deg); }
  50% { opacity: 1; transform: scale(1) rotate(12deg); }
}

.efr-title { margin: 6px 0 10px; font-size: 21px; font-weight: 700; color: #065f46; }
.efr-desc { margin: 0 0 18px; font-size: 14.5px; line-height: 1.7; color: #4d7c6f; }

/* 作成されたプロジェクトのチップが並ぶ場所 */
.efr-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  min-height: 0;
  margin-bottom: 4px;
}
.efr-chip {
  font-size: 12.5px;
  font-weight: 600;
  color: #0f766e;
  background: #ccfbf1;
  border: 1px solid #5eead4;
  border-radius: 12px;
  padding: 8px 12px;
  animation: efr-pop .35s cubic-bezier(.34,1.56,.64,1);
}
@keyframes efr-pop {
  0% { opacity: 0; transform: scale(.6) translateY(6px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
.efr-list:not(:empty) { margin-top: 4px; margin-bottom: 18px; }

.efr-cta {
  appearance: none;
  border: none;
  cursor: pointer;
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 13px 30px;
  font-size: 15px;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(135deg, #14b8a6 0%, #0d9488 100%);
  border-radius: 999px;
  box-shadow: 0 12px 26px -10px rgba(13, 148, 136, .5);
  transition: transform .25s cubic-bezier(.22,1,.36,1), box-shadow .25s ease;
}
.efr-cta:hover { transform: translateY(-2px); box-shadow: 0 16px 32px -10px rgba(13, 148, 136, .6); }
.efr-cta:active { transform: translateY(0); }
.efr-cta.is-limit { animation: efr-shake .4s ease; }
@keyframes efr-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

.efr-secondary {
  appearance: none;
  border: none;
  background: none;
  cursor: pointer;
  display: block;
  margin: 12px auto 0;
  padding: 4px 6px;
  font-size: 13px;
  font-weight: 600;
  color: #0f766e;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.efr-secondary:hover { color: #065f46; }

@media (prefers-reduced-motion: reduce) {
  .efr-plus-group, .efr-spark { animation: none !important; }
}

【JavaScript】
// 空状態: 初回・データなし — 「プロジェクトを作成」を押すたびに簡易チップが追加される(最大4件)デモ
(() => {
  const cta = document.querySelector('[data-cta]');
  const secondary = document.querySelector('[data-secondary]');
  const list = document.querySelector('[data-list]');
  if (!cta || !list) return; // null安全

  const MAX = 4;
  let count = 0;

  cta.addEventListener('click', () => {
    if (count >= MAX) {
      cta.classList.remove('is-limit');
      void cta.offsetWidth;
      cta.classList.add('is-limit');
      return;
    }
    count += 1;
    const chip = document.createElement('span');
    chip.className = 'efr-chip';
    chip.textContent = `プロジェクト ${count}`;
    list.appendChild(chip);
  });

  if (secondary) {
    secondary.addEventListener('click', () => {
      // デモ用: テンプレートから作った1件目を追加する簡易フィードバック
      if (count >= MAX || list.childElementCount > 0) return;
      count += 1;
      const chip = document.createElement('span');
      chip.className = 'efr-chip';
      chip.textContent = 'テンプレート: 基本プラン';
      list.appendChild(chip);
    });
  }
})();

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。