本文へスキップ

CRG-18 — JS INTERACTIVE

JS付きインタラクション38点 在庫あり

マウス追従・カウントアップ・モーダル。倉庫で唯一、JSを同梱した棚。 すべて画像不使用・クラス接頭辞 cg- 付きで、既存サイトに安全に積み込めます。

CRG-18-001

MOVE YOUR MOUSE

マウス追従スポットライトMouse Spotlight

.cg-in-spot {
  position: relative;
  width: 280px;
  height: 150px;
  border-radius: 14px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  --x: 50%;
  --y: 50%;
  background:
    radial-gradient(circle 90px at var(--x) var(--y), rgba(232, 89, 12, .45), transparent 70%),
    #0E1220;
}
.cg-in-spot p {
  margin: 0;
  font-size: 15px;
  font-weight: 900;
  letter-spacing: .2em;
  color: #EAF2FF;
  pointer-events: none;
}
<section class="cg-in-spot"><p>MOVE YOUR MOUSE</p></section>
(function () {
  var el = document.querySelector('.cg-in-spot');
  if (!el) return;
  el.addEventListener('mousemove', function (e) {
    var r = el.getBoundingClientRect();
    el.style.setProperty('--x', (e.clientX - r.left) + 'px');
    el.style.setProperty('--y', (e.clientY - r.top) + 'px');
  });
})();
GROSS WT. 831 B +JSORIGINAL
CRG-18-002
HOVER ME3D TILT

マウス連動3DチルトカードMouse Tilt Card

.cg-in-tilt {
  width: 280px;
  height: 150px;
  display: flex;
  align-items: center;
  justify-content: center;
  perspective: 600px;
}
.cg-in-tilt-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: center;
  padding: 26px 44px;
  color: #fff;
  border-radius: 14px;
  background: linear-gradient(135deg, #4C5FD5, #7C4FC8);
  box-shadow: 0 18px 34px rgba(0, 0, 0, .3);
  transition: transform .15s ease-out;
  will-change: transform;
}
.cg-in-tilt-card b { font-size: 15px; letter-spacing: .14em; }
.cg-in-tilt-card span { font-size: 10px; letter-spacing: .3em; opacity: .75; }
<div class="cg-in-tilt"><div class="cg-in-tilt-card"><b>HOVER ME</b><span>3D TILT</span></div></div>
(function () {
  var wrap = document.querySelector('.cg-in-tilt');
  var card = document.querySelector('.cg-in-tilt-card');
  if (!wrap || !card) return;
  wrap.addEventListener('mousemove', function (e) {
    var r = wrap.getBoundingClientRect();
    var rx = ((e.clientY - r.top) / r.height - .5) * -16;
    var ry = ((e.clientX - r.left) / r.width - .5) * 16;
    card.style.transform = 'rotateX(' + rx + 'deg) rotateY(' + ry + 'deg)';
  });
  wrap.addEventListener('mouseleave', function () {
    card.style.transform = 'rotateX(0) rotateY(0)';
  });
})();
GROSS WT. 1265 B +JSORIGINAL
CRG-18-003

エリア内でマウスを動かす

カーソルストーカーCursor Follower

.cg-in-stalker {
  position: relative;
  width: 280px;
  height: 150px;
  border-radius: 14px;
  overflow: hidden;
  background: #EFEDE6;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: none;
}
.cg-in-stalker p {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  color: #8B857A;
  pointer-events: none;
}
.cg-in-stalker i {
  position: absolute;
  top: 0;
  left: 0;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  border: 2px solid #E8590C;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
}
<section class="cg-in-stalker"><p>エリア内でマウスを動かす</p><i aria-hidden="true"></i></section>
(function () {
  var area = document.querySelector('.cg-in-stalker');
  var dot = document.querySelector('.cg-in-stalker i');
  if (!area || !dot) return;
  var tx = 0, ty = 0, x = 0, y = 0;
  area.addEventListener('mousemove', function (e) {
    var r = area.getBoundingClientRect();
    tx = e.clientX - r.left;
    ty = e.clientY - r.top;
    dot.style.opacity = 1;
  });
  area.addEventListener('mouseleave', function () { dot.style.opacity = 0; });
  (function loop() {
    x += (tx - x) * .14; /* 追従の遅れ。小さいほどぬるっと */
    y += (ty - y) * .14;
    dot.style.transform = 'translate(' + x + 'px,' + y + 'px)';
    requestAnimationFrame(loop);
  })();
})();
GROSS WT. 1345 B +JSORIGINAL
CRG-18-004
0DOWNLOADS

カウントアップ数字Count Up

.cg-in-count {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.cg-in-count b {
  font-family: 'Courier New', monospace;
  font-size: 44px;
  font-weight: 700;
  color: #E8590C;
  font-variant-numeric: tabular-nums;
}
.cg-in-count span {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .3em;
  color: #8B857A;
}
<div class="cg-in-count"><b data-count="1280">0</b><span>DOWNLOADS</span></div>
(function () {
  var els = document.querySelectorAll('[data-count]');
  if (!els.length || !('IntersectionObserver' in window)) return;
  var io = new IntersectionObserver(function (entries) {
    entries.forEach(function (en) {
      if (!en.isIntersecting) return;
      io.unobserve(en.target);
      var el = en.target;
      var goal = +el.getAttribute('data-count');
      var t0 = performance.now(), dur = 1400;
      (function tick(t) {
        var p = Math.min((t - t0) / dur, 1);
        p = 1 - Math.pow(1 - p, 3); /* ease-out */
        el.textContent = Math.round(goal * p).toLocaleString();
        if (p < 1) requestAnimationFrame(tick);
      })(t0);
    });
  }, { threshold: .6 });
  els.forEach(function (el) { io.observe(el); });
})();
GROSS WT. 1193 B +JSORIGINAL
CRG-18-005

サービスの概要パネルです。

料金プランのパネルです。

よくある質問のパネルです。

タブ切替Tabs

.cg-in-tabs { width: 270px; }
.cg-in-tabs .bar {
  display: flex;
  gap: 6px;
  border-bottom: 2.5px solid #1B1A17;
}
.cg-in-tabs .bar button {
  padding: 9px 18px;
  font-size: 13px;
  font-weight: 700;
  border: none;
  background: #E5E1D6;
  border-radius: 8px 8px 0 0;
  cursor: pointer;
  color: #5A5650;
}
.cg-in-tabs .bar button.on {
  background: #1B1A17;
  color: #fff;
}
.cg-in-tabs .panels p {
  display: none;
  margin: 0;
  padding: 16px 6px;
  font-size: 13px;
}
.cg-in-tabs .panels p.on { display: block; animation: cg-in-tabs-fade .3s ease; }
@keyframes cg-in-tabs-fade { from { opacity: 0; transform: translateY(4px); } }
<div class="cg-in-tabs"><div class="bar" role="tablist"><button class="on" role="tab" aria-selected="true">概要</button><button role="tab" aria-selected="false">料金</button><button role="tab" aria-selected="false">FAQ</button></div><div class="panels"><p class="on">サービスの概要パネルです。</p><p>料金プランのパネルです。</p><p>よくある質問のパネルです。</p></div></div>
(function () {
  var root = document.querySelector('.cg-in-tabs');
  if (!root) return;
  var btns = root.querySelectorAll('.bar button');
  var panels = root.querySelectorAll('.panels p');
  btns.forEach(function (btn, i) {
    btn.addEventListener('click', function () {
      btns.forEach(function (b, j) {
        b.classList.toggle('on', i === j);
        b.setAttribute('aria-selected', i === j ? 'true' : 'false');
      });
      panels.forEach(function (p, j) { p.classList.toggle('on', i === j); });
    });
  });
})();
GROSS WT. 1582 B +JSORIGINAL
CRG-18-006
ネイティブdialog要素

Escキーや背景クリックで閉じられます。フォーカス管理もブラウザ任せ。

モーダル(dialog)Dialog Modal

.cg-in-modal-open {
  padding: 13px 28px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
  background: #1B1A17;
  border: none;
  border-radius: 10px;
  cursor: pointer;
}
.cg-in-modal {
  width: 270px;
  padding: 26px 24px;
  border: 2.5px solid #1B1A17;
  border-radius: 14px;
  box-shadow: 8px 8px 0 rgba(27, 26, 23, .85);
}
.cg-in-modal::backdrop {
  background: rgba(27, 26, 23, .45);
  backdrop-filter: blur(3px);
}
.cg-in-modal b { font-size: 15px; }
.cg-in-modal p { font-size: 13px; color: #5A5650; margin: 10px 0 18px; line-height: 1.7; }
.cg-in-modal-close {
  padding: 10px 22px;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  background: #E8590C;
  border: none;
  border-radius: 8px;
  cursor: pointer;
}
<button class="cg-in-modal-open" type="button">モーダルを開く</button>
<dialog class="cg-in-modal"><b>ネイティブdialog要素</b><p>Escキーや背景クリックで閉じられます。フォーカス管理もブラウザ任せ。</p><button class="cg-in-modal-close" type="button">閉じる</button></dialog>
(function () {
  var dlg = document.querySelector('.cg-in-modal');
  var open = document.querySelector('.cg-in-modal-open');
  if (!dlg || !open) return;
  open.addEventListener('click', function () { dlg.showModal(); });
  dlg.querySelector('.cg-in-modal-close').addEventListener('click', function () { dlg.close(); });
  dlg.addEventListener('click', function (e) {
    if (e.target === dlg) dlg.close(); /* 背景クリックで閉じる */
  });
})();
GROSS WT. 1510 B +JSORIGINAL
CRG-18-007

トースト通知を出すToast Trigger

.cg-in-toastarea {
  position: relative;
  width: 280px;
  height: 150px;
  border-radius: 14px;
  background: #EFEDE6;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.cg-in-toast-btn {
  padding: 13px 30px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
  background: #2FA45E;
  border: none;
  border-radius: 10px;
  cursor: pointer;
}
.cg-in-toast {
  position: absolute;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  padding: 11px 20px;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  background: #1B1A17;
  border-radius: 10px;
  animation: cg-in-toast-pop .3s cubic-bezier(.2, .9, .3, 1.2), cg-in-toast-out .3s ease 2.2s forwards;
}
@keyframes cg-in-toast-pop { from { transform: translate(-50%, 16px); opacity: 0; } }
@keyframes cg-in-toast-out { to { transform: translate(-50%, 16px); opacity: 0; } }
<div class="cg-in-toastarea"><button class="cg-in-toast-btn" type="button">保存する</button></div>
(function () {
  var area = document.querySelector('.cg-in-toastarea');
  var btn = document.querySelector('.cg-in-toast-btn');
  if (!area || !btn) return;
  btn.addEventListener('click', function () {
    var old = area.querySelector('.cg-in-toast');
    if (old) old.remove();
    var t = document.createElement('div');
    t.className = 'cg-in-toast';
    t.setAttribute('role', 'status');
    t.textContent = '保存しました ✓';
    area.appendChild(t);
    setTimeout(function () { t.remove(); }, 2600);
  });
})();
GROSS WT. 1510 B +JSORIGINAL
CRG-18-008

ご注文から3営業日以内に発送します。

未開封に限り14日以内は返品可能です。

高さアニメ付きアコーディオンAnimated Accordion

.cg-in-acc { width: 270px; }
.cg-in-acc > button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 14px 16px;
  font-size: 13.5px;
  font-weight: 900;
  font-family: inherit;
  background: #fff;
  border: 2px solid #1B1A17;
  border-radius: 10px;
  cursor: pointer;
  margin-top: 8px;
}
.cg-in-acc > button i {
  width: 10px;
  height: 10px;
  border: solid #E8590C;
  border-width: 0 3px 3px 0;
  transform: rotate(45deg);
  transition: transform .3s;
  margin-top: -4px;
}
.cg-in-acc > button[aria-expanded="true"] i { transform: rotate(225deg); margin-top: 4px; }
.cg-in-acc .body {
  height: 0;
  overflow: hidden;
  transition: height .3s ease;
}
.cg-in-acc .body p {
  margin: 0;
  padding: 12px 16px 6px;
  font-size: 13px;
  color: #5A5650;
}
<div class="cg-in-acc"><button type="button" aria-expanded="true">配送について<i></i></button><div class="body" style="height:auto"><p>ご注文から3営業日以内に発送します。</p></div><button type="button" aria-expanded="false">返品について<i></i></button><div class="body"><p>未開封に限り14日以内は返品可能です。</p></div></div>
(function () {
  var acc = document.querySelector('.cg-in-acc');
  if (!acc) return;
  acc.querySelectorAll(':scope > button').forEach(function (btn) {
    btn.addEventListener('click', function () {
      var body = btn.nextElementSibling;
      var open = btn.getAttribute('aria-expanded') === 'true';
      btn.setAttribute('aria-expanded', open ? 'false' : 'true');
      body.style.height = body.scrollHeight + 'px'; /* auto→px に固定 */
      if (open) {
        requestAnimationFrame(function () { body.style.height = '0px'; });
      } else {
        setTimeout(function () { body.style.height = 'auto'; }, 300);
      }
    });
  });
})();
GROSS WT. 1826 B +JSORIGINAL
CRG-18-009

私たちはが得意です

ループタイピングTyping Loop

.cg-in-typing {
  margin: 0;
  font-size: 18px;
  font-weight: 900;
}
.cg-in-typing b { color: #E8590C; }
.cg-in-typing i {
  display: inline-block;
  width: 3px;
  height: 1.1em;
  margin: 0 2px -3px;
  background: #E8590C;
  animation: cg-in-typing-blink .75s step-end infinite;
}
@keyframes cg-in-typing-blink { 50% { opacity: 0; } }
<p class="cg-in-typing">私たちは<b data-phrases="デザイン,コーディング,SEO対策"></b><i aria-hidden="true"></i>が得意です</p>
(function () {
  var el = document.querySelector('.cg-in-typing b');
  if (!el) return;
  var phrases = el.getAttribute('data-phrases').split(',');
  var pi = 0, ci = 0, deleting = false;
  (function step() {
    var word = phrases[pi];
    ci += deleting ? -1 : 1;
    el.textContent = word.slice(0, ci);
    var wait = deleting ? 55 : 130;
    if (!deleting && ci === word.length) { wait = 1600; deleting = true; }
    if (deleting && ci === 0) { deleting = false; pi = (pi + 1) % phrases.length; wait = 400; }
    setTimeout(step, wait);
  })();
})();
GROSS WT. 1035 B +JSORIGINAL
CRG-18-010

クリック波紋ボタンRipple Button

.cg-in-ripple {
  position: relative;
  overflow: hidden;
  padding: 15px 36px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
  background: #3A5A78;
  border: none;
  border-radius: 10px;
  cursor: pointer;
}
.cg-in-ripple span {
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 255, 255, .45);
  transform: scale(0);
  animation: cg-in-ripple-grow .55s ease-out forwards;
  pointer-events: none;
}
@keyframes cg-in-ripple-grow {
  to { transform: scale(4); opacity: 0; }
}
<button class="cg-in-ripple" type="button">クリックしてみて</button>
(function () {
  var btn = document.querySelector('.cg-in-ripple');
  if (!btn) return;
  btn.addEventListener('click', function (e) {
    var r = btn.getBoundingClientRect();
    var d = Math.max(r.width, r.height);
    var s = document.createElement('span');
    s.style.width = s.style.height = d + 'px';
    s.style.left = (e.clientX - r.left - d / 2) + 'px';
    s.style.top = (e.clientY - r.top - d / 2) + 'px';
    btn.appendChild(s);
    s.addEventListener('animationend', function () { s.remove(); });
  });
})();
GROSS WT. 1098 B +JSORIGINAL
CRG-18-011

この箱をスクロールすると上のバーが進みます。CSSのscroll-timelineが使えない古いブラウザでも、この方式ならどこでも動きます。ページ全体に使う場合は対象を window にして position: fixed で置きます。

スクロール進捗バー(全ブラウザ版)Scroll Progress (JS)

.cg-in-progbox {
  position: relative;
  width: 270px;
  height: 140px;
  overflow-y: auto;
  border: 2px solid #1B1A17;
  border-radius: 10px;
  background: #fff;
}
.cg-in-progbox p {
  margin: 0;
  padding: 16px;
  font-size: 12.5px;
  line-height: 1.9;
}
.cg-in-progbar {
  position: sticky;
  top: 0;
  display: block;
  height: 6px;
  background: linear-gradient(90deg, #E8590C, #F2B707);
  transform-origin: left;
  transform: scaleX(0);
}
<div class="cg-in-progbox"><i class="cg-in-progbar" aria-hidden="true"></i><p>この箱をスクロールすると上のバーが進みます。CSSのscroll-timelineが使えない古いブラウザでも、この方式ならどこでも動きます。ページ全体に使う場合は対象を window にして position: fixed で置きます。</p></div>
(function () {
  var box = document.querySelector('.cg-in-progbox');
  var bar = document.querySelector('.cg-in-progbar');
  if (!box || !bar) return;
  box.addEventListener('scroll', function () {
    var max = box.scrollHeight - box.clientHeight;
    bar.style.transform = 'scaleX(' + (max ? box.scrollTop / max : 0) + ')';
  }, { passive: true });
})();
GROSS WT. 1157 B +JSORIGINAL
CRG-18-012
HEADER

下にスクロールするとヘッダーが隠れ、上に戻すと現れます。実サイトでは対象を window にして header を position: fixed にします。

スクロールで隠れるヘッダーAuto-Hide Header

.cg-in-hidebox {
  position: relative;
  width: 270px;
  height: 150px;
  overflow-y: auto;
  border: 2px solid #1B1A17;
  border-radius: 10px;
  background: #fff;
}
.cg-in-hidehead {
  position: sticky;
  top: 0;
  padding: 11px 16px;
  font-size: 12px;
  font-weight: 900;
  letter-spacing: .18em;
  color: #fff;
  background: #1B1A17;
  transition: transform .3s ease;
}
.cg-in-hidebox.scrolling-down .cg-in-hidehead {
  transform: translateY(-100%);
}
.cg-in-hidebody p {
  margin: 0;
  padding: 14px 16px 18px;
  font-size: 12.5px;
  line-height: 2;
}
<div class="cg-in-hidebox"><header class="cg-in-hidehead">HEADER</header><div class="cg-in-hidebody"><p>下にスクロールするとヘッダーが隠れ、上に戻すと現れます。実サイトでは対象を window にして header を position: fixed にします。</p></div></div>
(function () {
  var box = document.querySelector('.cg-in-hidebox');
  if (!box) return;
  var last = 0;
  box.addEventListener('scroll', function () {
    var y = box.scrollTop;
    box.classList.toggle('scrolling-down', y > last && y > 24);
    last = y;
  }, { passive: true });
})();
GROSS WT. 1134 B +JSORIGINAL
CRG-18-013

コピー完了ボタンCopy Feedback

.cg-in-copybtn {
  position: relative;
  padding: 13px 28px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
  background: #3A5A78;
  border: none;
  border-radius: 10px;
  cursor: pointer;
  transition: background .25s;
}
.cg-in-copybtn .d { display: none; }
.cg-in-copybtn.done { background: #2FA45E; }
.cg-in-copybtn.done .t { display: none; }
.cg-in-copybtn.done .d { display: inline; }
<button class="cg-in-copybtn" type="button" data-copy="https://example.com/invite/abc123"><span class="t">招待リンクをコピー</span><span class="d">コピーしました ✓</span></button>
(function () {
  var btn = document.querySelector('.cg-in-copybtn');
  if (!btn) return;
  btn.addEventListener('click', function () {
    var text = btn.getAttribute('data-copy');
    var done = function () {
      btn.classList.add('done');
      setTimeout(function () { btn.classList.remove('done'); }, 1800);
    };
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(text).then(done);
    } else {
      var ta = document.createElement('textarea');
      ta.value = text;
      document.body.appendChild(ta);
      ta.select();
      document.execCommand('copy');
      ta.remove();
      done();
    }
  });
})();
GROSS WT. 1261 B +JSORIGINAL
CRG-18-014

展開するFABメニューExpanding FAB

.cg-in-fab {
  position: relative;
  width: 56px;
  height: 160px;
  display: flex;
  align-items: flex-end;
}
.cg-in-fab .main {
  position: relative;
  z-index: 1;
  width: 56px;
  height: 56px;
  font-size: 24px;
  font-weight: 700;
  color: #fff;
  background: #E8590C;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(232, 89, 12, .4);
  transition: transform .3s cubic-bezier(.3, 1.3, .5, 1);
}
.cg-in-fab.open .main { transform: rotate(45deg); }
.cg-in-fab .sub {
  position: absolute;
  bottom: 8px;
  left: 8px;
  width: 40px;
  height: 40px;
  color: #1B1A17;
  background: #fff;
  border: 2px solid #1B1A17;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: transform .3s cubic-bezier(.3, 1.3, .5, 1), opacity .2s;
}
.cg-in-fab.open .sub {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(calc(var(--i) * -52px));
}
<div class="cg-in-fab"><button class="main" type="button" aria-expanded="false" aria-label="メニュー">+</button><button class="sub" type="button" aria-label="共有" style="--i:1"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.6 13.5 6.8 4M15.4 6.5l-6.8 4"/></svg></button><button class="sub" type="button" aria-label="お気に入り" style="--i:2"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg></button></div>
(function () {
  var fab = document.querySelector('.cg-in-fab');
  if (!fab) return;
  var main = fab.querySelector('.main');
  main.addEventListener('click', function () {
    var open = fab.classList.toggle('open');
    main.setAttribute('aria-expanded', open ? 'true' : 'false');
  });
})();
GROSS WT. 2135 B +JSORIGINAL
CRG-18-015

ビフォーアフター比較Before/After Slider

.cg-in-compare {
  position: relative;
  width: 260px;
  height: 140px;
  border-radius: 12px;
  overflow: hidden;
  --pos: 50%;
}
.cg-in-compare i {
  position: absolute;
  inset: 0;
}
.cg-in-compare .before {
  background: linear-gradient(135deg, #8B857A, #5A5650);
  filter: grayscale(1);
}
.cg-in-compare .after {
  background: linear-gradient(135deg, #E8590C, #F2B707);
  clip-path: inset(0 calc(100% - var(--pos)) 0 0);
}
.cg-in-compare b {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--pos);
  width: 3px;
  margin-left: -1.5px;
  background: #fff;
  box-shadow: 0 0 8px rgba(0, 0, 0, .4);
}
.cg-in-compare input {
  position: absolute;
  inset: 0;
  width: 100%;
  opacity: 0;
  cursor: ew-resize;
}
<div class="cg-in-compare"><i class="before"></i><i class="after"></i><b aria-hidden="true"></b><input type="range" min="0" max="100" value="50" aria-label="比較位置"></div>
(function () {
  var box = document.querySelector('.cg-in-compare');
  if (!box) return;
  box.querySelector('input').addEventListener('input', function (e) {
    box.style.setProperty('--pos', e.target.value + '%');
  });
})();
GROSS WT. 1125 B +JSORIGINAL
CRG-18-016

スクロールしてください ↓

1つ目のブロック
2つ目のブロック
3つ目のブロック

スクロールで順に現れるScroll Reveal

.cg-in-reveal {
  width: 260px;
  height: 150px;
  overflow-y: auto;
  border: 2px solid #1B1A17;
  border-radius: 12px;
  background: #fff;
  padding: 14px;
  box-sizing: border-box;
}
.cg-in-reveal p {
  margin: 0 0 70px;
  font-size: 12.5px;
  font-weight: 700;
  color: #8B857A;
  text-align: center;
}
.cg-in-reveal .item {
  margin-bottom: 60px;
  padding: 18px 16px;
  font-size: 13px;
  font-weight: 700;
  background: #FFF1E7;
  border-left: 4px solid #E8590C;
  border-radius: 0 10px 10px 0;
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .6s ease, transform .6s cubic-bezier(.2, .8, .3, 1);
}
.cg-in-reveal .item.in {
  opacity: 1;
  transform: translateY(0);
}
<div class="cg-in-reveal"><p>スクロールしてください ↓</p><div class="item">1つ目のブロック</div><div class="item">2つ目のブロック</div><div class="item">3つ目のブロック</div></div>
(function () {
  var root = document.querySelector('.cg-in-reveal');
  if (!root || !('IntersectionObserver' in window)) return;
  var io = new IntersectionObserver(function (entries) {
    entries.forEach(function (en) {
      if (en.isIntersecting) {
        en.target.classList.add('in');
        io.unobserve(en.target);
      }
    });
  }, { root: root, threshold: .4 });
  root.querySelectorAll('.item').forEach(function (el) { io.observe(el); });
})();
GROSS WT. 1366 B +JSORIGINAL
CRG-18-017

パスワード表示切替Password Toggle

.cg-in-pw {
  display: inline-flex;
  align-items: center;
  width: 240px;
  border: 2px solid #1B1A17;
  border-radius: 10px;
  background: #fff;
  overflow: hidden;
}
.cg-in-pw input {
  flex: 1;
  padding: 12px 14px;
  font-size: 14px;
  border: none;
  min-width: 0;
}
.cg-in-pw input:focus { outline: none; }
.cg-in-pw button {
  flex: none;
  width: 44px;
  align-self: stretch;
  border: none;
  background: #F3EFE5;
  color: #5A5650;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cg-in-pw button:hover { color: #1B1A17; }
.cg-in-pw .eyeoff { display: none; }
.cg-in-pw.show .eye { display: none; }
.cg-in-pw.show .eyeoff { display: block; }
<label class="cg-in-pw"><input type="password" value="secret123" aria-label="パスワード"><button type="button" aria-label="パスワードを表示"><svg class="eye" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg><svg class="eyeoff" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-.72-.72a10 10 0 0 1-8.8-4.3s1-2 3-3.9"/><path d="M9.5 5.4A10.2 10.2 0 0 1 12 5c6.5 0 10 7 10 7a13.2 13.2 0 0 1-1.7 2.4"/><path d="m2 2 20 20"/></svg></button></label>
(function () {
  var wrap = document.querySelector('.cg-in-pw');
  if (!wrap) return;
  var input = wrap.querySelector('input');
  var btn = wrap.querySelector('button');
  btn.addEventListener('click', function () {
    var show = input.type === 'password';
    input.type = show ? 'text' : 'password';
    wrap.classList.toggle('show', show);
    btn.setAttribute('aria-label', show ? 'パスワードを隠す' : 'パスワードを表示');
  });
})();
GROSS WT. 1937 B +JSORIGINAL
CRG-18-018

CSS変数を切り替えるだけの最小ダークモード。実サイトでは html 要素に付け、localStorageに保存します。

エリア内ダークモード切替Scoped Theme Toggle

.cg-in-theme {
  --bg: #FBF9F4;
  --tx: #1B1A17;
  --line: #1B1A17;
  width: 260px;
  padding: 18px;
  background: var(--bg);
  color: var(--tx);
  border: 2px solid var(--line);
  border-radius: 12px;
  transition: background .35s, color .35s;
  box-sizing: border-box;
}
.cg-in-theme.dark {
  --bg: #161511;
  --tx: #EDEBE4;
  --line: #EDEBE4;
}
.cg-in-theme button {
  padding: 9px 18px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--bg);
  background: var(--tx);
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: background .35s, color .35s;
}
.cg-in-theme p {
  margin: 12px 0 0;
  font-size: 12px;
  line-height: 1.8;
}
<div class="cg-in-theme"><button type="button">テーマ切替</button><p>CSS変数を切り替えるだけの最小ダークモード。実サイトでは html 要素に付け、localStorageに保存します。</p></div>
(function () {
  var area = document.querySelector('.cg-in-theme');
  if (!area) return;
  area.querySelector('button').addEventListener('click', function () {
    area.classList.toggle('dark');
  });
})();
GROSS WT. 1084 B +JSORIGINAL
CRG-18-019

下にスクロールすると右下にボタンが出ます。押すと一番上へ戻ります。実サイトでは対象を window にします。

出現する「上に戻る」Back to Top

.cg-in-btt {
  position: relative;
  width: 260px;
  height: 150px;
  overflow: hidden;
  border: 2px solid #1B1A17;
  border-radius: 12px;
  background: #fff;
}
.cg-in-btt .body {
  height: 100%;
  overflow-y: auto;
}
.cg-in-btt p {
  margin: 0;
  padding: 16px 16px 130px;
  font-size: 12.5px;
  line-height: 2;
}
.cg-in-btt button {
  position: absolute;
  right: 12px;
  bottom: 12px;
  width: 42px;
  height: 42px;
  color: #fff;
  background: #E8590C;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6px 14px rgba(232, 89, 12, .4);
  opacity: 0;
  transform: translateY(14px);
  pointer-events: none;
  transition: opacity .3s, transform .3s cubic-bezier(.3, 1.3, .5, 1);
}
.cg-in-btt.show button {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
<div class="cg-in-btt"><div class="body"><p>下にスクロールすると右下にボタンが出ます。押すと一番上へ戻ります。実サイトでは対象を window にします。</p></div><button type="button" aria-label="上に戻る"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m18 15-6-6-6 6"/></svg></button></div>
(function () {
  var box = document.querySelector('.cg-in-btt');
  if (!box) return;
  var body = box.querySelector('.body');
  var btn = box.querySelector('button');
  body.addEventListener('scroll', function () {
    box.classList.toggle('show', body.scrollTop > 40);
  }, { passive: true });
  btn.addEventListener('click', function () {
    body.scrollTo({ top: 0, behavior: 'smooth' });
  });
})();
GROSS WT. 1732 B +JSORIGINAL
CRG-18-020

クリック紙吹雪Click Confetti

.cg-in-confetti {
  position: relative;
  width: 280px;
  height: 150px;
  border-radius: 14px;
  background: #FBF3E4;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.cg-in-confetti button {
  padding: 14px 32px;
  font-size: 15px;
  font-weight: 900;
  color: #fff;
  background: #E8557E;
  border: none;
  border-radius: 30px;
  cursor: pointer;
}
.cg-in-confetti i {
  position: absolute;
  width: 8px;
  height: 12px;
  pointer-events: none;
  animation: cg-in-confetti-fall 1.1s ease-out forwards;
}
@keyframes cg-in-confetti-fall {
  0% { transform: translate(0, 0) rotate(0); opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)) rotate(var(--rot)); opacity: 0; }
}
<div class="cg-in-confetti"><button type="button">おめでとう!</button></div>
(function () {
  var area = document.querySelector('.cg-in-confetti');
  if (!area) return;
  var colors = ['#E8590C', '#F2B707', '#2FA45E', '#3A9BD5', '#E8557E'];
  area.querySelector('button').addEventListener('click', function (e) {
    var r = area.getBoundingClientRect();
    var x = e.clientX - r.left;
    var y = e.clientY - r.top;
    for (var k = 0; k < 24; k++) {
      var p = document.createElement('i');
      p.style.left = x + 'px';
      p.style.top = y + 'px';
      p.style.background = colors[k % colors.length];
      p.style.setProperty('--dx', (Math.random() * 220 - 110) + 'px');
      p.style.setProperty('--dy', (Math.random() * -140 - 20) + 'px');
      p.style.setProperty('--rot', (Math.random() * 720 - 360) + 'deg');
      area.appendChild(p);
      p.addEventListener('animationend', function (ev) { ev.target.remove(); });
    }
  });
})();
GROSS WT. 1683 B +JSORIGINAL
CRG-18-021

ドロワーメニューDrawer Menu

.cg-in-drawer {
  position: relative;
  width: 270px;
  height: 160px;
  border: 2px solid #1B1A17;
  border-radius: 12px;
  overflow: hidden;
  background: #EFEDE6;
}
.cg-in-drawer .open {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin: 14px;
  padding: 10px;
  border: none;
  background: #1B1A17;
  border-radius: 8px;
  cursor: pointer;
}
.cg-in-drawer .open i { width: 20px; height: 2.5px; background: #fff; border-radius: 2px; }
.cg-in-drawer .veil {
  position: absolute;
  inset: 0;
  background: rgba(27, 26, 23, .4);
  opacity: 0;
  pointer-events: none;
  transition: opacity .3s;
}
.cg-in-drawer .panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 150px;
  padding: 40px 18px 0;
  background: #fff;
  border-left: 2px solid #1B1A17;
  transform: translateX(105%);
  transition: transform .32s cubic-bezier(.3, 1, .4, 1);
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.cg-in-drawer .panel a {
  font-size: 13px;
  font-weight: 700;
  color: #1B1A17;
  text-decoration: none;
  border-bottom: 1.5px solid #E3DFD4;
  padding-bottom: 8px;
}
.cg-in-drawer .panel .close {
  position: absolute;
  top: 8px;
  right: 10px;
  border: none;
  background: none;
  font-size: 18px;
  cursor: pointer;
}
.cg-in-drawer.show .panel { transform: translateX(0); }
.cg-in-drawer.show .veil { opacity: 1; pointer-events: auto; }
<div class="cg-in-drawer"><button class="open" type="button" aria-label="メニューを開く" aria-expanded="false"><i></i><i></i><i></i></button><div class="panel"><button class="close" type="button" aria-label="閉じる">×</button><a href="#">ホーム</a><a href="#">制作実績</a><a href="#">お問い合わせ</a></div><b class="veil" aria-hidden="true"></b></div>
(function () {
  var root = document.querySelector('.cg-in-drawer');
  if (!root) return;
  var openBtn = root.querySelector('.open');
  function toggle(show) {
    root.classList.toggle('show', show);
    openBtn.setAttribute('aria-expanded', show ? 'true' : 'false');
  }
  openBtn.addEventListener('click', function () { toggle(true); });
  root.querySelector('.close').addEventListener('click', function () { toggle(false); });
  root.querySelector('.veil').addEventListener('click', function () { toggle(false); });
})();
GROSS WT. 2289 B +JSORIGINAL
CRG-18-022

ドロップダウンメニューDropdown

.cg-in-dd {
  position: relative;
  display: inline-block;
}
.cg-in-dd > button {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 12px 20px;
  font-size: 14px;
  font-weight: 700;
  background: #fff;
  border: 2px solid #1B1A17;
  border-radius: 10px;
  cursor: pointer;
}
.cg-in-dd > button i {
  width: 8px;
  height: 8px;
  border: solid #E8590C;
  border-width: 0 2.5px 2.5px 0;
  transform: rotate(45deg) translateY(-2px);
  transition: transform .25s;
}
.cg-in-dd > button[aria-expanded="true"] i { transform: rotate(225deg) translateY(-2px); }
.cg-in-dd ul {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 160px;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: #fff;
  border: 2px solid #1B1A17;
  border-radius: 10px;
  box-shadow: 4px 4px 0 rgba(27, 26, 23, .85);
  z-index: 5;
  animation: cg-in-dd-pop .18s cubic-bezier(.3, 1.3, .5, 1);
}
.cg-in-dd li a {
  display: block;
  padding: 9px 12px;
  font-size: 13px;
  font-weight: 700;
  color: #1B1A17;
  text-decoration: none;
  border-radius: 6px;
}
.cg-in-dd li a:hover { background: #FFF1E7; color: #B84508; }
@keyframes cg-in-dd-pop {
  from { opacity: 0; transform: translateY(-6px); }
}
<div class="cg-in-dd"><button type="button" aria-expanded="false">アカウント<i aria-hidden="true"></i></button><ul hidden><li><a href="#">プロフィール</a></li><li><a href="#">設定</a></li><li><a href="#">ログアウト</a></li></ul></div>
(function () {
  var dd = document.querySelector('.cg-in-dd');
  if (!dd) return;
  var btn = dd.querySelector('button');
  var menu = dd.querySelector('ul');
  btn.addEventListener('click', function (e) {
    e.stopPropagation();
    var open = menu.hidden;
    menu.hidden = !open;
    btn.setAttribute('aria-expanded', open ? 'true' : 'false');
  });
  /* 外側クリックとEscで閉じる */
  document.addEventListener('click', function (e) {
    if (!dd.contains(e.target)) { menu.hidden = true; btn.setAttribute('aria-expanded', 'false'); }
  });
  document.addEventListener('keydown', function (e) {
    if (e.key === 'Escape') { menu.hidden = true; btn.setAttribute('aria-expanded', 'false'); }
  });
})();
GROSS WT. 2184 B +JSORIGINAL
CRG-18-023

文字数カウンターCharacter Counter

.cg-in-cc {
  display: inline-flex;
  flex-direction: column;
  gap: 6px;
  width: 260px;
}
.cg-in-cc textarea {
  padding: 12px 14px;
  font-size: 13.5px;
  font-family: inherit;
  line-height: 1.7;
  border: 2px solid #C9C5BA;
  border-radius: 10px;
  resize: none;
}
.cg-in-cc textarea:focus { outline: none; border-color: #E8590C; }
.cg-in-cc span {
  align-self: flex-end;
  font-family: 'Courier New', monospace;
  font-size: 12px;
  font-weight: 700;
  color: #8B857A;
}
.cg-in-cc span.warn b { color: #C7791D; }
.cg-in-cc span.max b { color: #C0392B; }
<label class="cg-in-cc"><textarea rows="3" maxlength="60" placeholder="60文字まで入力できます">CSSパーツの倉庫、CSS CARGOをよろしく!</textarea><span><b>0</b> / 60</span></label>
(function () {
  var wrap = document.querySelector('.cg-in-cc');
  if (!wrap) return;
  var ta = wrap.querySelector('textarea');
  var label = wrap.querySelector('span');
  var num = wrap.querySelector('b');
  var max = +ta.getAttribute('maxlength');
  function update() {
    var len = ta.value.length;
    num.textContent = len;
    label.classList.toggle('warn', len >= max * .8 && len < max);
    label.classList.toggle('max', len >= max);
  }
  ta.addEventListener('input', update);
  update();
})();
GROSS WT. 1266 B +JSORIGINAL
CRG-18-024

セール終了まで

01HOURS30MIN00SEC

カウントダウンタイマーCountdown Timer

.cg-in-cd { text-align: center; }
.cg-in-cd p {
  margin: 0 0 10px;
  font-size: 12.5px;
  font-weight: 900;
  letter-spacing: .1em;
  color: #C0392B;
}
.cg-in-cd .row { display: inline-flex; gap: 8px; }
.cg-in-cd span {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.cg-in-cd b {
  min-width: 56px;
  padding: 12px 8px;
  font-family: 'Courier New', monospace;
  font-size: 24px;
  color: #fff;
  background: #1B1A17;
  border-radius: 10px;
  font-variant-numeric: tabular-nums;
}
.cg-in-cd i {
  font-size: 9px;
  font-style: normal;
  font-weight: 700;
  letter-spacing: .18em;
  color: #8B857A;
}
<div class="cg-in-cd" data-minutes="90"><p>セール終了まで</p><div class="row"><span><b class="h">01</b><i>HOURS</i></span><span><b class="m">30</b><i>MIN</i></span><span><b class="s">00</b><i>SEC</i></span></div></div>
(function () {
  var box = document.querySelector('.cg-in-cd');
  if (!box) return;
  /* 実運用では deadline を固定日時にする: new Date('2026-12-31T23:59:59') */
  var deadline = Date.now() + (+box.getAttribute('data-minutes')) * 60000;
  var h = box.querySelector('.h'), m = box.querySelector('.m'), s = box.querySelector('.s');
  function pad(n) { return String(n).padStart(2, '0'); }
  function tick() {
    var left = Math.max(0, deadline - Date.now());
    var sec = Math.floor(left / 1000);
    h.textContent = pad(Math.floor(sec / 3600));
    m.textContent = pad(Math.floor(sec / 60) % 60);
    s.textContent = pad(sec % 60);
    if (left > 0) setTimeout(tick, 250);
  }
  tick();
})();
GROSS WT. 1542 B +JSORIGINAL
CRG-18-025

CSS CARGO

スクランブル解読テキストText Scramble

.cg-in-scramble {
  margin: 0;
  font-family: 'Courier New', monospace;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: .08em;
  color: #1B1A17;
  min-width: 9ch;
}
<p class="cg-in-scramble" data-text="CSS CARGO">CSS CARGO</p>
(function () {
  var el = document.querySelector('.cg-in-scramble');
  if (!el) return;
  var target = el.getAttribute('data-text');
  var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#$%&*+=?';
  function run() {
    var frame = 0;
    var iv = setInterval(function () {
      var out = '';
      for (var i = 0; i < target.length; i++) {
        if (target[i] === ' ') { out += ' '; continue; }
        /* 左から順に確定していき、未確定はランダム文字 */
        out += (i < frame / 3) ? target[i] : chars[Math.floor(Math.random() * chars.length)];
      }
      el.textContent = out;
      frame++;
      if (frame / 3 > target.length) { clearInterval(iv); el.textContent = target; }
    }, 40);
  }
  run();
  setInterval(run, 5000); /* デモ用に繰り返し。実運用では1回でOK */
})();
GROSS WT. 1046 B +JSORIGINAL
CRG-18-027
クリックか Esc で閉じる

画像ライトボックスLightbox

.cg-in-lb { display: flex; gap: 10px; }
.cg-in-lb .thumb {
  width: 66px;
  height: 66px;
  border: 2px solid #1B1A17;
  border-radius: 10px;
  cursor: zoom-in;
  transition: transform .2s;
}
.cg-in-lb .thumb:hover { transform: scale(1.06); }
.cg-in-lb .t1 { background: linear-gradient(135deg, #E8590C, #F2B707); }
.cg-in-lb .t2 { background: linear-gradient(135deg, #3A9BD5, #2B8A78); }
.cg-in-lb .t3 { background: linear-gradient(135deg, #C2426B, #7A4FA0); }
.cg-in-lb .viewer {
  border: 2.5px solid #1B1A17;
  border-radius: 14px;
  padding: 12px;
  text-align: center;
  cursor: zoom-out;
}
.cg-in-lb .viewer::backdrop { background: rgba(27, 26, 23, .7); }
.cg-in-lb .viewer i {
  display: block;
  width: 220px;
  height: 150px;
  border-radius: 8px;
  background: var(--lb-bg, linear-gradient(135deg, #E8590C, #F2B707));
}
.cg-in-lb .viewer span {
  display: block;
  margin-top: 8px;
  font-size: 11px;
  font-weight: 700;
  color: #8B857A;
}
<div class="cg-in-lb"><button class="thumb t1" type="button" aria-label="画像1を拡大"></button><button class="thumb t2" type="button" aria-label="画像2を拡大"></button><button class="thumb t3" type="button" aria-label="画像3を拡大"></button><dialog class="viewer"><i></i><span>クリックか Esc で閉じる</span></dialog></div>
(function () {
  var root = document.querySelector('.cg-in-lb');
  if (!root) return;
  var dlg = root.querySelector('.viewer');
  var pic = dlg.querySelector('i');
  root.querySelectorAll('.thumb').forEach(function (t) {
    t.addEventListener('click', function () {
      /* 実運用では img要素のsrcを差し替える */
      pic.style.background = getComputedStyle(t).background;
      dlg.showModal();
    });
  });
  dlg.addEventListener('click', function () { dlg.close(); });
})();
GROSS WT. 1793 B +JSORIGINAL
CRG-18-028

概要

スクロールすると左のナビが現在地に追従します。

特徴

IntersectionObserverで軽量に検知しています。

料金

実サイトではnavをposition:stickyにします。

スクロール連動ナビScroll Spy Nav

.cg-in-spy {
  display: flex;
  width: 280px;
  height: 160px;
  border: 2px solid #1B1A17;
  border-radius: 12px;
  overflow: hidden;
  background: #fff;
}
.cg-in-spy nav {
  flex: none;
  width: 74px;
  padding: 12px 0;
  background: #F3EFE5;
  border-right: 2px solid #1B1A17;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.cg-in-spy nav b {
  padding: 7px 12px;
  font-size: 12px;
  color: #8B857A;
  border-left: 3px solid transparent;
  transition: all .2s;
}
.cg-in-spy nav b.on {
  color: #B84508;
  border-left-color: #E8590C;
  background: #FFF1E7;
}
.cg-in-spy .body {
  flex: 1;
  overflow-y: auto;
  scroll-behavior: smooth;
}
.cg-in-spy section { padding: 14px 14px 40px; }
.cg-in-spy h4 { margin: 0 0 6px; font-size: 13px; }
.cg-in-spy p { margin: 0; font-size: 11.5px; line-height: 1.8; color: #5A5650; }
<div class="cg-in-spy"><nav><b data-t="a" class="on">概要</b><b data-t="b">特徴</b><b data-t="c">料金</b></nav><div class="body"><section id="cg-spy-a" data-s="a"><h4>概要</h4><p>スクロールすると左のナビが現在地に追従します。</p></section><section id="cg-spy-b" data-s="b"><h4>特徴</h4><p>IntersectionObserverで軽量に検知しています。</p></section><section id="cg-spy-c" data-s="c"><h4>料金</h4><p>実サイトではnavをposition:stickyにします。</p></section></div></div>
(function () {
  var root = document.querySelector('.cg-in-spy');
  if (!root || !('IntersectionObserver' in window)) return;
  var body = root.querySelector('.body');
  var links = root.querySelectorAll('nav b');
  var io = new IntersectionObserver(function (entries) {
    entries.forEach(function (en) {
      if (!en.isIntersecting) return;
      var key = en.target.getAttribute('data-s');
      links.forEach(function (l) { l.classList.toggle('on', l.getAttribute('data-t') === key); });
    });
  }, { root: body, threshold: .55 });
  root.querySelectorAll('section').forEach(function (s) { io.observe(s); });
  links.forEach(function (l) {
    l.addEventListener('click', function () {
      var target = root.querySelector('[data-s="' + l.getAttribute('data-t') + '"]');
      body.scrollTo({ top: target.offsetTop - body.offsetTop, behavior: 'smooth' });
    });
  });
})();
GROSS WT. 2239 B +JSORIGINAL
CRG-18-029

WINTER SALE

雪の降る背景Snowfall Canvas

.cg-in-snow {
  position: relative;
  width: 280px;
  height: 150px;
  border-radius: 14px;
  overflow: hidden;
  background: linear-gradient(180deg, #1A2342, #2C3A5C);
  display: flex;
  align-items: center;
  justify-content: center;
}
.cg-in-snow canvas { position: absolute; inset: 0; }
.cg-in-snow p {
  position: relative;
  margin: 0;
  font-size: 17px;
  font-weight: 900;
  letter-spacing: .3em;
  color: #EAF2FF;
}
<div class="cg-in-snow"><canvas width="280" height="150" aria-hidden="true"></canvas><p>WINTER SALE</p></div>
(function () {
  var cv = document.querySelector('.cg-in-snow canvas');
  if (!cv) return;
  var ctx = cv.getContext('2d');
  var W = cv.width, H = cv.height;
  var flakes = [];
  for (var i = 0; i < 40; i++) {
    flakes.push({
      x: Math.random() * W,
      y: Math.random() * H,
      r: Math.random() * 2.2 + .8,
      v: Math.random() * .7 + .35,
      sway: Math.random() * 2 * Math.PI
    });
  }
  (function loop() {
    ctx.clearRect(0, 0, W, H);
    ctx.fillStyle = 'rgba(255,255,255,.85)';
    flakes.forEach(function (f) {
      f.y += f.v;
      f.sway += .02;
      f.x += Math.sin(f.sway) * .3;
      if (f.y > H + 4) { f.y = -4; f.x = Math.random() * W; }
      ctx.beginPath();
      ctx.arc(f.x, f.y, f.r, 0, Math.PI * 2);
      ctx.fill();
    });
    requestAnimationFrame(loop);
  })();
})();
GROSS WT. 1349 B +JSORIGINAL
CRG-18-030

リアルタイム検証フォームLive Validation

.cg-in-valid {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  gap: 6px;
  width: 250px;
}
.cg-in-valid input {
  padding: 12px 40px 12px 14px;
  font-size: 14px;
  border: 2px solid #C9C5BA;
  border-radius: 10px;
}
.cg-in-valid input:focus { outline: none; }
.cg-in-valid.ok input { border-color: #2FA45E; }
.cg-in-valid.ng input { border-color: #C0392B; }
.cg-in-valid i {
  position: absolute;
  right: 12px;
  top: 12px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  opacity: 0;
  transition: opacity .2s;
}
.cg-in-valid.ok i {
  opacity: 1;
  background: #2FA45E url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 12.5l3.5 3.5L18 8' stroke='%23fff' stroke-width='3' fill='none'/%3E%3C/svg%3E") center / 13px no-repeat;
}
.cg-in-valid.ng i {
  opacity: 1;
  background: #C0392B url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8 8l8 8M16 8l-8 8' stroke='%23fff' stroke-width='3'/%3E%3C/svg%3E") center / 11px no-repeat;
}
.cg-in-valid span { font-size: 11.5px; font-weight: 700; min-height: 1em; }
.cg-in-valid.ok span { color: #1E5A38; }
.cg-in-valid.ng span { color: #8A2A20; }
<label class="cg-in-valid"><input type="email" placeholder="メールアドレスを入力"><i aria-hidden="true"></i><span></span></label>
(function () {
  var wrap = document.querySelector('.cg-in-valid');
  if (!wrap) return;
  var input = wrap.querySelector('input');
  var msg = wrap.querySelector('span');
  input.addEventListener('input', function () {
    var v = input.value;
    if (!v) { wrap.className = 'cg-in-valid'; msg.textContent = ''; return; }
    var ok = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v);
    wrap.className = 'cg-in-valid ' + (ok ? 'ok' : 'ng');
    msg.textContent = ok ? '使用できます' : 'メールアドレスの形式が正しくありません';
  });
})();
GROSS WT. 1917 B +JSORIGINAL
CRG-18-031

ようこそ、CSSの倉庫へ。

1文字ずつ登場Split Text Reveal

.cg-in-split {
  margin: 0;
  font-size: 20px;
  font-weight: 900;
}
.cg-in-split span {
  display: inline-block;
  opacity: 0;
  transform: translateY(14px) rotate(6deg);
  animation: cg-in-split-in .5s cubic-bezier(.3, 1.4, .5, 1) forwards;
}
@keyframes cg-in-split-in {
  to { opacity: 1; transform: translateY(0) rotate(0); }
}
<p class="cg-in-split">ようこそ、CSSの倉庫へ。</p>
(function () {
  var el = document.querySelector('.cg-in-split');
  if (!el) return;
  var text = el.textContent;
  function play() {
    el.textContent = '';
    text.split('').forEach(function (ch, i) {
      var s = document.createElement('span');
      s.textContent = ch === ' ' ? '\u00A0' : ch;
      s.style.animationDelay = (i * 55) + 'ms';
      el.appendChild(s);
    });
  }
  play();
  setInterval(play, 4500); /* デモ用ループ。実運用は1回でOK */
})();
GROSS WT. 868 B +JSORIGINAL
CRG-18-032

シェアボタンWeb Share

.cg-in-share {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 13px 26px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
  background: #1B1A17;
  border: none;
  border-radius: 30px;
  cursor: pointer;
  transition: background .25s;
}
.cg-in-share:hover { background: #E8590C; }
.cg-in-share.copied { background: #2FA45E; }
<button class="cg-in-share" type="button"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.6 13.5 6.8 4M15.4 6.5l-6.8 4"/></svg><span>このページを共有</span></button>
(function () {
  var btn = document.querySelector('.cg-in-share');
  if (!btn) return;
  var label = btn.querySelector('span');
  btn.addEventListener('click', function () {
    var data = { title: document.title, url: location.href };
    if (navigator.share) {
      navigator.share(data).catch(function () { /* キャンセルは無視 */ });
    } else if (navigator.clipboard) {
      /* Web Share非対応(主にPC)はURLコピーにフォールバック */
      navigator.clipboard.writeText(data.url).then(function () {
        btn.classList.add('copied');
        var old = label.textContent;
        label.textContent = 'URLをコピーしました ✓';
        setTimeout(function () {
          btn.classList.remove('copied');
          label.textContent = old;
        }, 1800);
      });
    }
  });
})();
GROSS WT. 1566 B +JSORIGINAL
CRG-18-033

カーソルを近づけてみて

こわがりカードScaredy Card

.cg-in-scaredy {
  position: relative;
  width: 280px;
  height: 170px;
  border-radius: 14px;
  overflow: hidden;
  background: #EFEDE6;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cg-in-scaredy p {
  position: absolute;
  bottom: 10px;
  left: 0;
  right: 0;
  margin: 0;
  text-align: center;
  font-size: 11px;
  font-weight: 700;
  color: #8B857A;
  pointer-events: none;
}
.cg-in-scaredy .card {
  width: 170px;
  padding: 16px 18px;
  background: #fff;
  border: 2.5px solid #1B1A17;
  border-radius: 14px;
  box-shadow: 5px 6px 0 rgba(27, 26, 23, .18);
  /* 逃げる動きの「ぬるっと感」はCSS transitionで出す(JSループ不要) */
  transition: transform .22s ease-out;
}
.cg-in-scaredy.scared .card {
  animation: cg-in-scaredy-tremble .12s linear infinite;
}
.cg-in-scaredy .face {
  position: relative;
  display: block;
  height: 26px;
  margin-bottom: 10px;
}
.cg-in-scaredy .eye {
  position: absolute;
  top: 8px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #1B1A17;
  transition: all .15s;
}
.cg-in-scaredy .eye.l { left: 6px; }
.cg-in-scaredy .eye.r { left: 26px; }
.cg-in-scaredy .mouth {
  position: absolute;
  top: 16px;
  left: 12px;
  width: 12px;
  height: 6px;
  border: 2px solid #1B1A17;
  border-top: none;
  border-radius: 0 0 12px 12px;
  transition: all .15s;
}
/* 怯え顔: 目が見開き、口が「わ〜」の形に */
.cg-in-scaredy.scared .eye { width: 8px; height: 8px; top: 5px; }
.cg-in-scaredy.scared .mouth {
  top: 15px;
  width: 10px;
  height: 10px;
  border: 2px solid #1B1A17;
  border-radius: 50%;
  background: #FDDCE4;
}
.cg-in-scaredy .sweat {
  position: absolute;
  top: 0;
  left: 44px;
  width: 7px;
  height: 10px;
  background: #7FD4FF;
  border-radius: 50% 50% 50% 50% / 30% 30% 70% 70%;
  opacity: 0;
  transition: opacity .15s;
}
.cg-in-scaredy.scared .sweat {
  opacity: 1;
  animation: cg-in-scaredy-drip 1s ease-in infinite;
}
.cg-in-scaredy .line {
  display: block;
  height: 8px;
  border-radius: 5px;
  background: #E5E1D6;
  margin-top: 8px;
}
.cg-in-scaredy .w70 { width: 70%; }
.cg-in-scaredy .w90 { width: 90%; }
@keyframes cg-in-scaredy-tremble {
  0% { margin-top: 0; margin-left: 0; }
  25% { margin-top: -1.5px; margin-left: 1.5px; }
  50% { margin-top: 1px; margin-left: -1.5px; }
  75% { margin-top: -1px; margin-left: 1px; }
  100% { margin-top: 0; margin-left: 0; }
}
@keyframes cg-in-scaredy-drip {
  0%, 60% { transform: translateY(0); opacity: 1; }
  100% { transform: translateY(8px); opacity: 0; }
}
<div class="cg-in-scaredy"><div class="card"><i class="face" aria-hidden="true"><b class="eye l"></b><b class="eye r"></b><b class="mouth"></b><b class="sweat"></b></i><span class="line w70"></span><span class="line w90"></span></div><p>カーソルを近づけてみて</p></div>
(function () {
  var area = document.querySelector('.cg-in-scaredy');
  if (!area) return;
  var card = area.querySelector('.card');
  var RANGE = 150; /* 怖がり始める距離(px) */
  var FLEE = .5;   /* 仰け反りの強さ(0〜1) */
  area.addEventListener('mousemove', function (e) {
    /* 逃げたカード自身ではなく、定位置(コンテナ中心)からの距離で判定する。
       スクロールしても毎回計算し直すのでズレない */
    var rect = area.getBoundingClientRect();
    var dx = (rect.left + rect.width / 2) - e.clientX;
    var dy = (rect.top + rect.height / 2) - e.clientY;
    var dist = Math.sqrt(dx * dx + dy * dy) || 1;
    if (dist < RANGE) {
      var power = (RANGE - dist) / RANGE; /* 近いほど強く逃げる */
      /* コンテナの外に逃げすぎないようクランプ */
      var x = Math.max(-46, Math.min(46, (dx / dist) * power * RANGE * FLEE));
      var y = Math.max(-30, Math.min(30, (dy / dist) * power * RANGE * FLEE * .6));
      var r = (dx > 0 ? 1 : -1) * power * 10; /* 逃げる方向に仰け反る */
      card.style.transform = 'translate(' + x + 'px,' + y + 'px) rotate(' + r + 'deg)';
      area.classList.add('scared');
    } else {
      card.style.transform = '';
      area.classList.remove('scared');
    }
  });
  area.addEventListener('mouseleave', function () {
    card.style.transform = '';
    area.classList.remove('scared');
  });
})();
GROSS WT. 4276 B +JSORIGINAL
CRG-18-034

SECURITY CHECK

認証コードを入力

6桁のコードを送信しました(デモ: 204815)

6桁のコードを入力してください

ヒント: 貼り付けると全マスが一度に埋まります

6桁OTP認証(自動フォーカス+照合)OTP Verify

.cg-in-otp {
  width: 280px;
  padding: 26px 24px;
  border-radius: 16px;
  text-align: center;
  color: #E8ECF4;
  background: radial-gradient(at 50% 0%, #1C2233, #0E1119);
  border: 1px solid #2A3145;
}
.cg-in-otp .lead { margin: 0 0 6px; font-size: 10px; font-weight: 700; letter-spacing: .3em; color: #6B7488; }
.cg-in-otp h4 { margin: 0 0 6px; font-size: 18px; }
.cg-in-otp .sub { margin: 0 0 18px; font-size: 11.5px; color: #8B93A6; }
.cg-in-otp .fields { display: flex; gap: 7px; justify-content: center; margin-bottom: 16px; }
.cg-in-otp input {
  width: 34px;
  height: 44px;
  text-align: center;
  font-size: 20px;
  font-weight: 700;
  color: #fff;
  background: #171C29;
  border: 1.5px solid #2E3548;
  border-radius: 10px;
  transition: border-color .2s, box-shadow .2s;
}
.cg-in-otp input:focus {
  outline: none;
  border-color: #5B8DEF;
  box-shadow: 0 0 0 3px rgba(91, 141, 239, .25);
}
.cg-in-otp .status { margin: 0 0 6px; font-size: 12px; font-weight: 700; color: #8B93A6; }
.cg-in-otp small { font-size: 10.5px; color: #5A6072; }
/* 合否の状態(JSが .ok / .ng を付与) */
.cg-in-otp.ok input { border-color: #3AA76D; box-shadow: 0 0 0 2px rgba(58, 167, 109, .2); }
.cg-in-otp.ok .status { color: #4FC083; }
.cg-in-otp.ng .fields { animation: cg-in-otp-shake .4s ease; }
.cg-in-otp.ng input { border-color: #E05B4E; }
.cg-in-otp.ng .status { color: #FF7B6E; }
@keyframes cg-in-otp-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-6px); }
  50% { transform: translateX(6px); }
  75% { transform: translateX(-3px); }
}
<div class="cg-in-otp"><p class="lead">SECURITY CHECK</p><h4>認証コードを入力</h4><p class="sub">6桁のコードを送信しました(デモ: 204815)</p><div class="fields"><input inputmode="numeric" maxlength="1" aria-label="1桁目"><input inputmode="numeric" maxlength="1" aria-label="2桁目"><input inputmode="numeric" maxlength="1" aria-label="3桁目"><input inputmode="numeric" maxlength="1" aria-label="4桁目"><input inputmode="numeric" maxlength="1" aria-label="5桁目"><input inputmode="numeric" maxlength="1" aria-label="6桁目"></div><p class="status" role="status">6桁のコードを入力してください</p><small>ヒント: 貼り付けると全マスが一度に埋まります</small></div>
(function () {
  var root = document.querySelector('.cg-in-otp');
  if (!root) return;
  var CODE_OK = '204815'; /* デモ用の正解。実運用はサーバー側で照合する */
  var inputs = Array.prototype.slice.call(root.querySelectorAll('input'));
  var status = root.querySelector('.status');
  function collect() { return inputs.map(function (i) { return i.value; }).join(''); }
  function verify() {
    var code = collect();
    root.classList.remove('ok', 'ng');
    if (code.length < 6) { status.textContent = '6桁のコードを入力してください'; return; }
    if (code === CODE_OK) { root.classList.add('ok'); status.textContent = '認証に成功しました'; }
    else { root.classList.add('ng'); status.textContent = 'コードが正しくありません'; }
  }
  inputs.forEach(function (input, idx) {
    input.addEventListener('input', function () {
      input.value = input.value.replace(/[^0-9]/g, '').slice(0, 1);
      if (input.value && idx < inputs.length - 1) inputs[idx + 1].focus();
      verify();
    });
    input.addEventListener('keydown', function (e) {
      if (e.key === 'Backspace' && !input.value && idx > 0) inputs[idx - 1].focus();
    });
    input.addEventListener('paste', function (e) {
      e.preventDefault();
      var digits = (e.clipboardData || window.clipboardData).getData('text').replace(/[^0-9]/g, '').slice(0, 6);
      inputs.forEach(function (inp, j) { inp.value = digits[j] || ''; });
      (inputs[digits.length] || inputs[inputs.length - 1]).focus();
      verify();
    });
  });
})();
GROSS WT. 3871 B +JSORIGINAL
CRG-18-035
OCEANCSS CARGO · HOLO★ RARE ★

ホログラフィックカード(3Dティルト)Holographic Card

.cg-in-holo {
  perspective: 800px;
  padding: 16px;
}
.cg-in-holo .card {
  --px: .5; --py: .5; --mx: 50%; --my: 50%; --d: 0;
  position: relative;
  width: 178px;
  height: 248px;
  border-radius: 16px;
  overflow: hidden;
  background: linear-gradient(160deg, #202B4A, #0A0E1C);
  box-shadow:
    0 18px 40px rgba(0, 0, 0, .5),
    0 2px 8px rgba(0, 0, 0, .4);
  transform: rotateX(calc((var(--py) - .5) * -24deg)) rotateY(calc((var(--px) - .5) * 24deg));
  transition: transform .12s ease-out;
  transform-style: preserve-3d;
}
/* ① ベースの下地シャイン(斜めに走る白光) */
.cg-in-holo .shine {
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,.14) 48%, transparent 62%);
  background-size: 220% 220%;
  background-position: calc(var(--mx) * 1.4) calc(var(--my) * 1.4);
  pointer-events: none;
}
/* ② 主ホログラム箔: 虹色ストライプ。マウスで移動 */
.cg-in-holo .foil {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(115deg,
    #FF5EA8 0%, #FFD86B 12%, #66FFB2 24%, #5EC8FF 36%, #C46BFF 48%, #FF5EA8 60%);
  background-size: 260% 260%;
  background-position: var(--mx) var(--my);
  mix-blend-mode: color-dodge;
  opacity: calc(.28 + var(--d) * .5);
  pointer-events: none;
}
/* ③ 逆位相の第2箔: 角度で虹が振動して奥行きが出る */
.cg-in-holo .foil2 {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(-70deg,
    #7CF9FF 0%, #B98CFF 20%, #FF8FD0 40%, #FFE58A 60%, #7CF9FF 80%);
  background-size: 300% 300%;
  background-position: calc(100% - var(--mx)) calc(100% - var(--my));
  mix-blend-mode: overlay;
  opacity: calc(.18 + var(--d) * .35);
  pointer-events: none;
}
/* ④ きらめくスパークル粒子(SVGノイズをマスク) */
.cg-in-holo .sparkle {
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 130px 130px;
  mix-blend-mode: color-dodge;
  opacity: calc(var(--d) * .6);
  -webkit-mask: radial-gradient(circle 90px at var(--mx) var(--my), #000 10%, transparent 60%);
  mask: radial-gradient(circle 90px at var(--mx) var(--my), #000 10%, transparent 60%);
  pointer-events: none;
}
/* ⑤ カーソル直下の白い強光沢 */
.cg-in-holo .glare {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle 100px at var(--mx) var(--my), rgba(255,255,255,.65), transparent 55%);
  mix-blend-mode: overlay;
  opacity: calc(.4 + var(--d) * .6);
  pointer-events: none;
}
/* ⑥ 傾けた側が発光するエッジライト */
.cg-in-holo .edge {
  position: absolute;
  inset: 0;
  border-radius: 16px;
  box-shadow: inset 0 0 0 1.5px rgba(150,190,255,.35);
  background: linear-gradient(var(--mx), rgba(120,180,255,.0), rgba(120,180,255,.0));
  pointer-events: none;
}
.cg-in-holo .card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 16px;
  box-shadow: inset calc((var(--px) - .5) * -20px) calc((var(--py) - .5) * -20px) 30px rgba(130,180,255,.4);
  pointer-events: none;
}
.cg-in-holo .face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: #EAF0FF;
  transform: translateZ(40px);
}
/* パララックス: 要素ごとに違う深度で動く */
.cg-in-holo b {
  font-size: 24px;
  font-weight: 900;
  letter-spacing: .16em;
  text-shadow: 0 2px 8px rgba(0,0,0,.5);
  transform: translate(calc((var(--px) - .5) * 14px), calc((var(--py) - .5) * 14px));
}
.cg-in-holo small { font-size: 8.5px; letter-spacing: .22em; color: #93A8D0; }
.cg-in-holo .orb {
  width: 58px;
  height: 58px;
  margin-top: 10px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, #CFE0FF, #3A6FD8 52%, #0C1A3C);
  box-shadow: 0 0 26px rgba(90,140,255,.75), inset 0 -6px 12px rgba(0,0,0,.4);
  transform: translate(calc((var(--px) - .5) * 26px), calc((var(--py) - .5) * 26px));
}
.cg-in-holo em {
  font-style: normal;
  font-size: 9px;
  font-weight: 900;
  letter-spacing: .18em;
  color: #FFD86B;
  margin-top: 8px;
  text-shadow: 0 0 8px rgba(255,216,107,.6);
}
/* reduced-motion: 傾き・箔移動を止め、静止した箔だけ薄く残す */
@media (prefers-reduced-motion: reduce) {
  .cg-in-holo .card { transition: none; transform: none; }
  .cg-in-holo .sparkle, .cg-in-holo .foil2 { opacity: .12; }
}
<div class="cg-in-holo"><div class="card"><span class="shine"></span><span class="foil"></span><span class="foil2"></span><span class="sparkle"></span><span class="glare"></span><span class="edge"></span><div class="face"><b>OCEAN</b><small>CSS CARGO · HOLO</small><i class="orb"></i><em>★ RARE ★</em></div></div></div>
(function () {
  var wrap = document.querySelector('.cg-in-holo');
  if (!wrap) return;
  var card = wrap.querySelector('.card');
  var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)');
  /* rAFで目標値へ滑らかに追従(生のmousemoveより上質な動き) */
  var tx = .5, ty = .5, x = .5, y = .5, td = 0, d = 0, raf = 0;
  function apply() {
    card.style.setProperty('--px', x);
    card.style.setProperty('--py', y);
    card.style.setProperty('--mx', (x * 100) + '%');
    card.style.setProperty('--my', (y * 100) + '%');
    card.style.setProperty('--d', d);
  }
  function loop() {
    var k = .18;
    x += (tx - x) * k; y += (ty - y) * k; d += (td - d) * k;
    apply();
    if (Math.abs(tx - x) > .001 || Math.abs(ty - y) > .001 || Math.abs(td - d) > .001) {
      raf = requestAnimationFrame(loop);
    } else { raf = 0; }
  }
  function kick() { if (!raf) raf = requestAnimationFrame(loop); }
  if (reduce && reduce.matches) return;
  wrap.addEventListener('pointermove', function (e) {
    var r = card.getBoundingClientRect();
    tx = Math.max(0, Math.min(1, (e.clientX - r.left) / r.width));
    ty = Math.max(0, Math.min(1, (e.clientY - r.top) / r.height));
    td = 1; /* ホバー中は箔・スパークルを強める */
    kick();
  });
  wrap.addEventListener('pointerleave', function () {
    tx = .5; ty = .5; td = 0; /* 正面へ戻り、箔が鎮まる */
    kick();
  });
})();
GROSS WT. 6432 B +JSORIGINAL
CRG-18-036

折り紙バッグ購入(タイムライン版)Origami Bag (Full)

.cg-in-obag button {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 14px 30px;
  min-width: 170px;
  justify-content: center;
  font-size: 14px;
  font-weight: 900;
  color: #2A2622;
  background: #F3EDE3;
  border: 2px solid #2A2622;
  border-radius: 12px;
  cursor: pointer;
  overflow: hidden;
}
.cg-in-obag .paper {
  position: relative;
  width: 20px;
  height: 18px;
  background: #C89B6C;
  border-radius: 2px 2px 4px 4px;
  transform-origin: bottom center;
}
.cg-in-obag .handle {
  position: absolute;
  top: -6px; left: 4px; right: 4px;
  height: 8px;
  border: 2px solid #8A6A44;
  border-bottom: 0;
  border-radius: 6px 6px 0 0;
  transform: scaleX(0);
  transition: transform .3s ease;
}
.cg-in-obag .fold {
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, transparent 48%, rgba(0,0,0,.15) 50%, transparent 52%);
}
.cg-in-obag .t-done { position: absolute; opacity: 0; color: #2F8A4E; }
/* タイムラインで付与されるクラスごとの状態 */
.cg-in-obag.p-fold .paper { transform: scaleY(.6) rotate(-3deg); }
.cg-in-obag.p-bag .paper { transform: scaleY(1.15); }
.cg-in-obag.p-bag .handle { transform: scaleX(1); }
.cg-in-obag.p-puff .paper { transform: scaleY(1.15) scaleX(1.12); }
.cg-in-obag.p-tag .paper { transform: scaleY(1.1) rotate(4deg); }
.cg-in-obag.p-done .t-buy { opacity: 0; }
.cg-in-obag.p-done .t-done { opacity: 1; }
.cg-in-obag .paper, .cg-in-obag .t-buy { transition: transform .4s cubic-bezier(.3,1.4,.5,1), opacity .3s; }
@media (prefers-reduced-motion: reduce) {
  .cg-in-obag .paper, .cg-in-obag .handle { transition: none; }
}
<div class="cg-in-obag"><button type="button"><span class="paper" aria-hidden="true"><i class="handle"></i><i class="fold"></i></span><b class="t-buy">かごに入れる</b><b class="t-done">追加しました</b></button><span class="live" aria-live="polite"></span></div>
(function () {
  var root = document.querySelector('.cg-in-obag');
  if (!root) return;
  var btn = root.querySelector('button');
  var live = root.querySelector('.live');
  var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)');
  var running = false, timers = [];
  function later(fn, ms) { timers.push(setTimeout(fn, ms)); }
  function reset() {
    ['p-fold','p-bag','p-puff','p-tag','p-done'].forEach(function (c) { root.classList.remove(c); });
    running = false;
  }
  btn.addEventListener('click', function () {
    if (running) return;
    running = true;
    timers.forEach(clearTimeout); timers = [];
    live.textContent = '';
    if (reduce && reduce.matches) { /* アニメ省略の即時完了 */
      root.classList.add('p-done'); live.textContent = '商品を追加しました';
      later(reset, 1400); return;
    }
    /* タイムライン振り付け: 折る→袋→膨らむ→タグ→完了 */
    root.classList.add('p-fold');
    later(function () { root.classList.add('p-bag'); }, 200);
    later(function () { root.classList.add('p-puff'); }, 700);
    later(function () { root.classList.add('p-tag'); }, 1050);
    later(function () { root.classList.add('p-done'); live.textContent = '商品を追加しました'; }, 1400);
    later(reset, 2600);
  });
})();
GROSS WT. 3246 B +JSORIGINAL
CRG-18-037

ロケット発射(カウントダウン版)Rocket Launch (Full)

.cg-in-rocket button {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 15px 34px;
  min-width: 180px;
  justify-content: center;
  font-size: 15px;
  font-weight: 900;
  letter-spacing: .06em;
  color: #fff;
  background: linear-gradient(135deg, #2B2F55, #14162C);
  border: none;
  border-radius: 12px;
  overflow: hidden;
  cursor: pointer;
}
.cg-in-rocket .scene { position: relative; width: 18px; height: 24px; }
.cg-in-rocket .rocket {
  position: absolute;
  inset: 0;
  border-radius: 50% 50% 40% 40%;
  background: linear-gradient(#fff, #C7D0E0);
  transition: transform .5s cubic-bezier(.5,-0.3,.7,1), opacity .3s;
}
.cg-in-rocket .rocket::before {
  content: "";
  position: absolute; bottom: 2px; left: -4px; right: -4px; height: 6px;
  background: #E8557E; clip-path: polygon(0 100%, 30% 0, 70% 0, 100% 100%);
}
.cg-in-rocket .flame {
  position: absolute; bottom: -6px; left: 50%; width: 8px; height: 0; margin-left: -4px;
  border-radius: 50% 50% 60% 60%; background: linear-gradient(#FFE14D, #FF8A4D);
  transform-origin: top;
}
.cg-in-rocket .t-count, .cg-in-rocket .t-done { position: absolute; opacity: 0; }
.cg-in-rocket .t-done { color: #6FE39B; }
/* 状態クラス */
.cg-in-rocket.is-count .t-go { opacity: 0; }
.cg-in-rocket.is-count .t-count { opacity: 1; }
.cg-in-rocket.is-launch .t-count { opacity: 0; }
.cg-in-rocket.is-launch .rocket { transform: translateY(-42px); opacity: 0; }
.cg-in-rocket.is-launch .flame { height: 18px; }
.cg-in-rocket.is-done .t-done { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .cg-in-rocket .rocket { transition: none; }
}
<div class="cg-in-rocket"><button type="button"><span class="scene" aria-hidden="true"><i class="rocket"><b class="flame"></b></i></span><b class="t-go">発射する</b><b class="t-count">3</b><b class="t-done">打ち上げ成功</b></button><span class="live" aria-live="polite"></span></div>
(function () {
  var root = document.querySelector('.cg-in-rocket');
  if (!root) return;
  var btn = root.querySelector('button');
  var count = root.querySelector('.t-count');
  var live = root.querySelector('.live');
  var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)');
  var running = false, timers = [];
  function later(fn, ms) { timers.push(setTimeout(fn, ms)); }
  function reset() {
    ['is-count','is-launch','is-done'].forEach(function (c) { root.classList.remove(c); });
    count.textContent = '3'; running = false;
  }
  btn.addEventListener('click', function () {
    if (running) return;
    running = true;
    timers.forEach(clearTimeout); timers = [];
    live.textContent = '';
    if (reduce && reduce.matches) {
      root.classList.add('is-done'); live.textContent = '打ち上げ成功';
      later(reset, 1600); return;
    }
    /* 3→2→1 カウントダウン → 発射 → 成功 → リセット */
    root.classList.add('is-count');
    later(function () { count.textContent = '2'; }, 500);
    later(function () { count.textContent = '1'; }, 1000);
    later(function () { root.classList.add('is-launch'); }, 1500);
    later(function () { root.classList.add('is-done'); live.textContent = '打ち上げ成功'; }, 2050);
    later(reset, 3400);
  });
})();
GROSS WT. 3265 B +JSORIGINAL
CRG-18-038

カーゴドロップ購入(コンベア版)Cargo Drop (Full)

.cg-in-cargo button {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 14px 28px;
  min-width: 190px;
  justify-content: center;
  font-size: 14px;
  font-weight: 900;
  color: #fff;
  background: #1C2130;
  border: none;
  border-radius: 12px;
  overflow: hidden;
  cursor: pointer;
}
.cg-in-cargo .track { position: relative; display: flex; align-items: center; width: 52px; height: 24px; color: #9FB0C8; }
.cg-in-cargo .box {
  position: absolute;
  left: 0;
  width: 16px; height: 16px;
  border-radius: 3px;
  background: linear-gradient(135deg, #E0A050, #B87830);
  transition: transform .6s cubic-bezier(.5,0,.5,1), opacity .2s;
}
.cg-in-cargo .cart { position: absolute; right: 0; }
.cg-in-cargo .t-done { position: absolute; opacity: 0; color: #6FE39B; }
/* 状態: 箱がカートへ運ばれて消える → バッジ的に完了 */
.cg-in-cargo.is-run .box { transform: translateX(34px) scale(.4); opacity: 0; }
.cg-in-cargo.is-run .cart { animation: cg-in-cargo-bump .4s ease .5s; }
.cg-in-cargo.is-done .t-buy { opacity: 0; }
.cg-in-cargo.is-done .t-done { opacity: 1; }
@keyframes cg-in-cargo-bump { 40% { transform: translateY(-4px) scale(1.15); } }
@media (prefers-reduced-motion: reduce) {
  .cg-in-cargo .box { transition: none; }
}
<div class="cg-in-cargo"><button type="button"><span class="track" aria-hidden="true"><i class="box"></i><svg class="cart" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg></span><b class="t-buy">カートに入れる</b><b class="t-done">追加しました</b></button><span class="live" aria-live="polite"></span></div>
(function () {
  var root = document.querySelector('.cg-in-cargo');
  if (!root) return;
  var btn = root.querySelector('button');
  var live = root.querySelector('.live');
  var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)');
  var running = false, timers = [];
  function later(fn, ms) { timers.push(setTimeout(fn, ms)); }
  function reset() {
    root.classList.remove('is-run', 'is-done'); running = false;
  }
  btn.addEventListener('click', function () {
    if (running) return;
    running = true;
    timers.forEach(clearTimeout); timers = [];
    live.textContent = '';
    if (reduce && reduce.matches) {
      root.classList.add('is-done'); live.textContent = 'カートに追加しました';
      later(reset, 1500); return;
    }
    /* コンベアで箱がカートへ → 完了ラベル → リセット */
    root.classList.add('is-run');
    later(function () { root.classList.add('is-done'); live.textContent = 'カートに追加しました'; }, 950);
    later(reset, 2400);
  });
})();
GROSS WT. 2900 B +JSORIGINAL