遊べるUI 中級

昼夜トグル

クリックすると太陽と月が弧を描いて移動し、空の背景色が昼と夜のあいだをなめらかに遷移するトグル。雲の漂いや星の瞬きも添えました。

#css#js#toggle#animation

ライブデモ

コード

HTML
<!-- 昼夜トグル: 太陽と月が弧を描いて移動し、空の背景色が昼と夜のあいだを遷移するトグル -->
<div class="stage">
  <button class="day-night" type="button" role="switch" aria-checked="false" aria-label="昼と夜を切り替える">
    <span class="day-night__sky day-night__sky--day" aria-hidden="true"></span>
    <span class="day-night__sky day-night__sky--night" aria-hidden="true"></span>
    <span class="day-night__stars" aria-hidden="true">
      <span></span><span></span><span></span><span></span><span></span><span></span>
    </span>
    <span class="day-night__clouds" aria-hidden="true">
      <span class="day-night__cloud day-night__cloud--a"></span>
      <span class="day-night__cloud day-night__cloud--b"></span>
    </span>
    <span class="day-night__ground" aria-hidden="true"></span>
    <span class="day-night__celestial" aria-hidden="true">
      <span class="day-night__sun"></span>
      <span class="day-night__moon"></span>
    </span>
  </button>
  <p class="day-night__status" aria-hidden="true">☀️ 昼</p>
  <p class="hint">クリックすると昼と夜が入れ替わります</p>
</div>
CSS
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  overflow: hidden;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, sans-serif;
  background: radial-gradient(circle at 50% 30%, #161329 0%, #0d0e1a 72%);
  color: #f4f5fb;
}

.stage { display: grid; place-items: center; gap: 14px; padding: 20px; }

.day-night {
  position: relative;
  width: min(78vw, 240px);
  height: 130px;
  border: none;
  border-radius: 26px;
  cursor: pointer;
  overflow: hidden;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
  box-shadow: 0 16px 32px rgba(0, 0, 0, .5);
}
.day-night:focus-visible { outline: 2px solid #ffd166; outline-offset: 4px; }

/* 空(昼/夜)はopacityのクロスフェードで色を遷移させる */
.day-night__sky {
  position: absolute;
  inset: 0;
  transition: opacity .9s ease;
}
.day-night__sky--day {
  background: linear-gradient(180deg, #7ec8f2 0%, #bfe4ff 65%, #eaf6ff 100%);
  opacity: 1;
}
.day-night__sky--night {
  background: linear-gradient(180deg, #0b1026 0%, #1b2560 65%, #232d6e 100%);
  opacity: 0;
}
.day-night[aria-checked="true"] .day-night__sky--day { opacity: 0; }
.day-night[aria-checked="true"] .day-night__sky--night { opacity: 1; }

/* 星: 夜のみ表示。個々にまたたきの時間差をつける */
.day-night__stars {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity .9s ease;
}
.day-night[aria-checked="true"] .day-night__stars { opacity: 1; }
.day-night__stars span {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #fff;
  animation: star-twinkle 2.4s ease-in-out infinite;
}
.day-night__stars span:nth-child(1) { top: 18%; left: 14%; animation-delay: 0s; }
.day-night__stars span:nth-child(2) { top: 32%; left: 32%; animation-delay: .3s; }
.day-night__stars span:nth-child(3) { top: 14%; left: 52%; animation-delay: .9s; }
.day-night__stars span:nth-child(4) { top: 28%; left: 74%; animation-delay: .5s; }
.day-night__stars span:nth-child(5) { top: 44%; left: 60%; animation-delay: 1.2s; }
.day-night__stars span:nth-child(6) { top: 12%; left: 88%; animation-delay: 1.6s; }
@keyframes star-twinkle {
  0%, 100% { opacity: .25; transform: scale(.8); }
  50%      { opacity: 1; transform: scale(1.15); }
}

/* 雲: 昼のみ表示し、常にゆっくり漂う(一覧カードでも生きて見えるアイドル演出) */
.day-night__clouds {
  position: absolute;
  inset: 0;
  opacity: 1;
  transition: opacity .9s ease;
}
.day-night[aria-checked="true"] .day-night__clouds { opacity: 0; }
.day-night__cloud {
  position: absolute;
  height: 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .85);
  box-shadow:
    12px -6px 0 -1px rgba(255, 255, 255, .85),
    -10px -3px 0 -2px rgba(255, 255, 255, .7);
}
.day-night__cloud--a { top: 24%; left: -30%; width: 44px; animation: cloud-drift 13s linear infinite; }
.day-night__cloud--b { top: 46%; left: -46%; width: 34px; animation: cloud-drift 18s linear infinite 3s; }
@keyframes cloud-drift {
  0%   { transform: translateX(0); }
  100% { transform: translateX(340px); }
}

.day-night__ground {
  position: absolute;
  left: -12%;
  right: -12%;
  bottom: -30px;
  height: 58px;
  background: #142036;
  border-radius: 50%;
  opacity: .92;
}

/* 太陽/月の本体。leftの移動+is-arcing時の弧を描くtranslateYで昇降させる */
.day-night__celestial {
  position: absolute;
  left: 12%;
  bottom: 26px;
  width: 34px;
  height: 34px;
  transition: left 1.05s cubic-bezier(.34, 1.56, .64, 1);
}
.day-night[aria-checked="true"] .day-night__celestial { left: calc(88% - 34px); }
.day-night__celestial.is-arcing { animation: celestial-arc 1.05s cubic-bezier(.34, 1.56, .64, 1); }
@keyframes celestial-arc {
  0%   { transform: translateY(0); }
  50%  { transform: translateY(-46px); }
  100% { transform: translateY(0); }
}

.day-night__sun,
.day-night__moon {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  transition: opacity .9s ease, transform .9s ease;
}
.day-night__sun {
  background: radial-gradient(circle at 32% 30%, #fff6da, #ffd166 55%, #ffb347 100%);
  box-shadow: 0 0 22px rgba(255, 209, 102, .65);
  opacity: 1;
  transform: scale(1);
}
.day-night__moon {
  background: radial-gradient(circle at 35% 32%, #f4f6ff, #cbd5f5 60%, #8892b0 100%);
  box-shadow:
    inset -4px -3px 0 1px rgba(120, 130, 170, .3),
    inset 5px 6px 0 0 rgba(120, 130, 170, .22),
    0 0 16px rgba(180, 190, 230, .5);
  opacity: 0;
  transform: scale(.6);
}
.day-night[aria-checked="true"] .day-night__sun { opacity: 0; transform: scale(.6); }
.day-night[aria-checked="true"] .day-night__moon { opacity: 1; transform: scale(1); }

.day-night__status {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
}

.hint { margin: 0; font-size: 13px; color: rgba(244, 245, 251, .5); text-align: center; }

@media (prefers-reduced-motion: reduce) {
  .day-night__celestial { transition-duration: .15s; }
  .day-night__celestial.is-arcing { animation: none; }
  .day-night__cloud { animation: none; }
  .day-night__stars span { animation: none; opacity: .8; transform: none; }
}
JavaScript
// 昼夜トグル: クリックで太陽/月が弧を描いて移動し、空の色を昼夜で入れ替える
(() => {
  const btn = document.querySelector('.day-night');
  const celestial = document.querySelector('.day-night__celestial');
  const status = document.querySelector('.day-night__status');
  if (!btn || !celestial) return; // null安全

  let isNight = btn.getAttribute('aria-checked') === 'true';

  const setStatus = () => {
    if (status) status.textContent = isNight ? '🌙 夜' : '☀️ 昼';
  };
  setStatus();

  btn.addEventListener('click', () => {
    isNight = !isNight;
    btn.setAttribute('aria-checked', String(isNight));
    setStatus();

    celestial.classList.remove('is-arcing');
    void celestial.offsetWidth; // reflow: 連続クリックでも毎回弧を描かせる
    celestial.classList.add('is-arcing');
  });

  celestial.addEventListener('animationend', () => {
    celestial.classList.remove('is-arcing');
  });
})();

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

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

# 追加してほしい効果
昼夜トグル(遊べるUI)
クリックすると太陽と月が弧を描いて移動し、空の背景色が昼と夜のあいだをなめらかに遷移するトグル。雲の漂いや星の瞬きも添えました。

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

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 昼夜トグル: 太陽と月が弧を描いて移動し、空の背景色が昼と夜のあいだを遷移するトグル -->
<div class="stage">
  <button class="day-night" type="button" role="switch" aria-checked="false" aria-label="昼と夜を切り替える">
    <span class="day-night__sky day-night__sky--day" aria-hidden="true"></span>
    <span class="day-night__sky day-night__sky--night" aria-hidden="true"></span>
    <span class="day-night__stars" aria-hidden="true">
      <span></span><span></span><span></span><span></span><span></span><span></span>
    </span>
    <span class="day-night__clouds" aria-hidden="true">
      <span class="day-night__cloud day-night__cloud--a"></span>
      <span class="day-night__cloud day-night__cloud--b"></span>
    </span>
    <span class="day-night__ground" aria-hidden="true"></span>
    <span class="day-night__celestial" aria-hidden="true">
      <span class="day-night__sun"></span>
      <span class="day-night__moon"></span>
    </span>
  </button>
  <p class="day-night__status" aria-hidden="true">☀️ 昼</p>
  <p class="hint">クリックすると昼と夜が入れ替わります</p>
</div>

【CSS】
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  overflow: hidden;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, sans-serif;
  background: radial-gradient(circle at 50% 30%, #161329 0%, #0d0e1a 72%);
  color: #f4f5fb;
}

.stage { display: grid; place-items: center; gap: 14px; padding: 20px; }

.day-night {
  position: relative;
  width: min(78vw, 240px);
  height: 130px;
  border: none;
  border-radius: 26px;
  cursor: pointer;
  overflow: hidden;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
  box-shadow: 0 16px 32px rgba(0, 0, 0, .5);
}
.day-night:focus-visible { outline: 2px solid #ffd166; outline-offset: 4px; }

/* 空(昼/夜)はopacityのクロスフェードで色を遷移させる */
.day-night__sky {
  position: absolute;
  inset: 0;
  transition: opacity .9s ease;
}
.day-night__sky--day {
  background: linear-gradient(180deg, #7ec8f2 0%, #bfe4ff 65%, #eaf6ff 100%);
  opacity: 1;
}
.day-night__sky--night {
  background: linear-gradient(180deg, #0b1026 0%, #1b2560 65%, #232d6e 100%);
  opacity: 0;
}
.day-night[aria-checked="true"] .day-night__sky--day { opacity: 0; }
.day-night[aria-checked="true"] .day-night__sky--night { opacity: 1; }

/* 星: 夜のみ表示。個々にまたたきの時間差をつける */
.day-night__stars {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity .9s ease;
}
.day-night[aria-checked="true"] .day-night__stars { opacity: 1; }
.day-night__stars span {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #fff;
  animation: star-twinkle 2.4s ease-in-out infinite;
}
.day-night__stars span:nth-child(1) { top: 18%; left: 14%; animation-delay: 0s; }
.day-night__stars span:nth-child(2) { top: 32%; left: 32%; animation-delay: .3s; }
.day-night__stars span:nth-child(3) { top: 14%; left: 52%; animation-delay: .9s; }
.day-night__stars span:nth-child(4) { top: 28%; left: 74%; animation-delay: .5s; }
.day-night__stars span:nth-child(5) { top: 44%; left: 60%; animation-delay: 1.2s; }
.day-night__stars span:nth-child(6) { top: 12%; left: 88%; animation-delay: 1.6s; }
@keyframes star-twinkle {
  0%, 100% { opacity: .25; transform: scale(.8); }
  50%      { opacity: 1; transform: scale(1.15); }
}

/* 雲: 昼のみ表示し、常にゆっくり漂う(一覧カードでも生きて見えるアイドル演出) */
.day-night__clouds {
  position: absolute;
  inset: 0;
  opacity: 1;
  transition: opacity .9s ease;
}
.day-night[aria-checked="true"] .day-night__clouds { opacity: 0; }
.day-night__cloud {
  position: absolute;
  height: 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .85);
  box-shadow:
    12px -6px 0 -1px rgba(255, 255, 255, .85),
    -10px -3px 0 -2px rgba(255, 255, 255, .7);
}
.day-night__cloud--a { top: 24%; left: -30%; width: 44px; animation: cloud-drift 13s linear infinite; }
.day-night__cloud--b { top: 46%; left: -46%; width: 34px; animation: cloud-drift 18s linear infinite 3s; }
@keyframes cloud-drift {
  0%   { transform: translateX(0); }
  100% { transform: translateX(340px); }
}

.day-night__ground {
  position: absolute;
  left: -12%;
  right: -12%;
  bottom: -30px;
  height: 58px;
  background: #142036;
  border-radius: 50%;
  opacity: .92;
}

/* 太陽/月の本体。leftの移動+is-arcing時の弧を描くtranslateYで昇降させる */
.day-night__celestial {
  position: absolute;
  left: 12%;
  bottom: 26px;
  width: 34px;
  height: 34px;
  transition: left 1.05s cubic-bezier(.34, 1.56, .64, 1);
}
.day-night[aria-checked="true"] .day-night__celestial { left: calc(88% - 34px); }
.day-night__celestial.is-arcing { animation: celestial-arc 1.05s cubic-bezier(.34, 1.56, .64, 1); }
@keyframes celestial-arc {
  0%   { transform: translateY(0); }
  50%  { transform: translateY(-46px); }
  100% { transform: translateY(0); }
}

.day-night__sun,
.day-night__moon {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  transition: opacity .9s ease, transform .9s ease;
}
.day-night__sun {
  background: radial-gradient(circle at 32% 30%, #fff6da, #ffd166 55%, #ffb347 100%);
  box-shadow: 0 0 22px rgba(255, 209, 102, .65);
  opacity: 1;
  transform: scale(1);
}
.day-night__moon {
  background: radial-gradient(circle at 35% 32%, #f4f6ff, #cbd5f5 60%, #8892b0 100%);
  box-shadow:
    inset -4px -3px 0 1px rgba(120, 130, 170, .3),
    inset 5px 6px 0 0 rgba(120, 130, 170, .22),
    0 0 16px rgba(180, 190, 230, .5);
  opacity: 0;
  transform: scale(.6);
}
.day-night[aria-checked="true"] .day-night__sun { opacity: 0; transform: scale(.6); }
.day-night[aria-checked="true"] .day-night__moon { opacity: 1; transform: scale(1); }

.day-night__status {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
}

.hint { margin: 0; font-size: 13px; color: rgba(244, 245, 251, .5); text-align: center; }

@media (prefers-reduced-motion: reduce) {
  .day-night__celestial { transition-duration: .15s; }
  .day-night__celestial.is-arcing { animation: none; }
  .day-night__cloud { animation: none; }
  .day-night__stars span { animation: none; opacity: .8; transform: none; }
}

【JavaScript】
// 昼夜トグル: クリックで太陽/月が弧を描いて移動し、空の色を昼夜で入れ替える
(() => {
  const btn = document.querySelector('.day-night');
  const celestial = document.querySelector('.day-night__celestial');
  const status = document.querySelector('.day-night__status');
  if (!btn || !celestial) return; // null安全

  let isNight = btn.getAttribute('aria-checked') === 'true';

  const setStatus = () => {
    if (status) status.textContent = isNight ? '🌙 夜' : '☀️ 昼';
  };
  setStatus();

  btn.addEventListener('click', () => {
    isNight = !isNight;
    btn.setAttribute('aria-checked', String(isNight));
    setStatus();

    celestial.classList.remove('is-arcing');
    void celestial.offsetWidth; // reflow: 連続クリックでも毎回弧を描かせる
    celestial.classList.add('is-arcing');
  });

  celestial.addEventListener('animationend', () => {
    celestial.classList.remove('is-arcing');
  });
})();

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

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