クレイモーフィズム

内外の二重シャドウでぷっくり膨らんだ粘土風カード。やわらかく親しみやすいUIやSNS系コンポーネントに向きます。

#css#claymorphism#card#javascript#animation

ライブデモ

使用例(お題: カフェ MOON BREW)

この技法を「カフェ MOON BREW」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- MOON BREW:クレイモーフィズムのおすすめメニュー -->
<section class="mc-menu">
  <header class="mc-menu__head">
    <p class="mc-menu__eyebrow">TODAY'S PICK</p>
    <h2 class="mc-menu__title">本日のおすすめ</h2>
  </header>

  <div class="mc-cards">
    <!-- カード1 -->
    <article class="mc-card" data-name="カフェラテ">
      <span class="mc-card__emoji mc-card__emoji--latte">☕</span>
      <h3 class="mc-card__name">月見カフェラテ</h3>
      <p class="mc-card__desc">なめらかミルクと深煎り</p>
      <div class="mc-card__foot">
        <span class="mc-card__price">¥620</span>
        <button class="mc-card__add" type="button" aria-label="カートに追加">+</button>
      </div>
    </article>

    <!-- カード2 -->
    <article class="mc-card" data-name="ドリップ">
      <span class="mc-card__emoji mc-card__emoji--drip">🫖</span>
      <h3 class="mc-card__name">本日のドリップ</h3>
      <p class="mc-card__desc">エチオピア/華やかな酸</p>
      <div class="mc-card__foot">
        <span class="mc-card__price">¥540</span>
        <button class="mc-card__add" type="button" aria-label="カートに追加">+</button>
      </div>
    </article>

    <!-- カード3 -->
    <article class="mc-card" data-name="スコーン">
      <span class="mc-card__emoji mc-card__emoji--scone">🍪</span>
      <h3 class="mc-card__name">焙煎スコーン</h3>
      <p class="mc-card__desc">コーヒー香る自家製</p>
      <div class="mc-card__foot">
        <span class="mc-card__price">¥380</span>
        <button class="mc-card__add" type="button" aria-label="カートに追加">+</button>
      </div>
    </article>
  </div>

  <p class="mc-cart" id="mcCart">カートは空です</p>
</section>
CSS
/* MOON BREW:ぷっくりクレイモーフィズムのメニュー */
:root {
  --cream: #f5ede1;
  --brown: #2b1d12;
  --amber: #c98a3b;
  --clay: #fbf3e7;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  height: 400px;
  display: grid;
  place-items: center;
  background: var(--cream);
  font-family: "Hiragino Kaku Gothic ProN", "Segoe UI", system-ui, sans-serif;
  color: var(--brown);
  overflow: hidden;
}

.mc-menu {
  width: min(540px, 94vw);
  padding: 8px;
}
.mc-menu__head { text-align: center; margin-bottom: 16px; }
.mc-menu__eyebrow { margin: 0; font-size: 10px; letter-spacing: 0.3em; color: var(--amber); font-weight: 700; }
.mc-menu__title { margin: 4px 0 0; font-size: 20px; font-weight: 800; }

.mc-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}

/* クレイカード:内外の二重シャドウでぷっくり */
.mc-card {
  padding: 16px 14px 14px;
  border-radius: 26px;
  background: var(--clay);
  box-shadow:
    8px 8px 18px rgba(43,29,18,0.14),
    -6px -6px 14px rgba(255,255,255,0.9),
    inset 4px 4px 8px rgba(255,255,255,0.7),
    inset -5px -5px 10px rgba(201,138,59,0.14);
  text-align: center;
  transition: transform 0.28s cubic-bezier(0.34,1.4,0.64,1);
}
.mc-card:hover { transform: translateY(-4px); }

/* 絵文字を入れる丸いクレイバッジ */
.mc-card__emoji {
  display: inline-grid;
  place-items: center;
  width: 50px; height: 50px;
  border-radius: 50%;
  font-size: 22px;
  margin-bottom: 8px;
  box-shadow:
    inset 3px 3px 6px rgba(255,255,255,0.9),
    inset -4px -4px 8px rgba(43,29,18,0.12),
    4px 4px 10px rgba(43,29,18,0.1);
}
.mc-card__emoji--latte { background: #f3e3cf; }
.mc-card__emoji--drip { background: #ecd9c2; }
.mc-card__emoji--scone { background: #f6e8d3; }

.mc-card__name { margin: 0 0 3px; font-size: 13.5px; font-weight: 700; }
.mc-card__desc { margin: 0 0 12px; font-size: 10.5px; color: #8a7560; line-height: 1.5; }

.mc-card__foot { display: flex; align-items: center; justify-content: space-between; }
.mc-card__price { font-size: 14px; font-weight: 800; color: var(--amber); }

/* +ボタン:小さなぷっくりクレイ */
.mc-card__add {
  width: 30px; height: 30px;
  border: none;
  border-radius: 50%;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  color: #fff;
  background: var(--amber);
  box-shadow:
    4px 4px 9px rgba(201,138,59,0.4),
    -3px -3px 7px rgba(255,255,255,0.8),
    inset 2px 2px 4px rgba(255,255,255,0.5),
    inset -2px -2px 5px rgba(43,29,18,0.18);
  transition: transform 0.18s ease;
}
.mc-card__add:hover { transform: scale(1.12); }
.mc-card__add:active { transform: scale(0.92); }

.mc-cart {
  margin: 16px 0 0;
  text-align: center;
  font-size: 12px;
  color: #8a7560;
  min-height: 1em;
}

@media (prefers-reduced-motion: reduce) {
  .mc-card, .mc-card__add { transition: none; }
}
JavaScript
// +ボタンで簡易カート(個数を数えて文章に反映)
const cart = document.getElementById("mcCart");
const adds = document.querySelectorAll(".mc-card__add");
let count = 0;
let lastName = "";

adds.forEach((btn) => {
  btn.addEventListener("click", () => {
    // 親カードから商品名を取得(null安全)
    const card = btn.closest(".mc-card");
    lastName = card?.dataset.name || "商品";
    count += 1;
    if (cart) cart.textContent = `カートに「${lastName}」を追加(合計 ${count} 点)`;

    // ぷにっと弾むフィードバック
    if (card) {
      card.style.transform = "scale(0.96)";
      setTimeout(() => { card.style.transform = ""; }, 160);
    }
  });
});

実装ガイド

使いどころ

親しみやすさを前面に出したいSNS系コンポーネント、子ども向け/カジュアルなアプリ、ウェルカムカードや空状態(エンプティステート)の通知カードに向きます。ぷっくりした質感で『触れたくなる』ボタンやアイコンを作れます。

実装時の注意点

クレイ感は『外側の落ち影+反対向きの淡い外側ハイライト+内側の二重 inset シャドウ(明と暗)』を一つの box-shadow に4層重ねて作るのが核心です。大きめの border-radius(30px前後)で角を丸め、cubic-bezier のオーバーシュート(1.56等)でぽよんとした弾みを付けると粘土らしさが増します。色味はパステル基調で、影は背景に馴染む彩度を選ぶと自然です。

対応ブラウザ

多層 box-shadow(inset 併用)と linear-gradient、transform/animation だけで成立し、backdrop-filter は不使用のため全モダンブラウザで安定して再現できます。プレフィックスや対応バージョンの懸念はありません。

よくある失敗

影を4層も重ねるため、明暗のバランスを誤ると膨らみ過ぎて安っぽく見えたり、逆に潰れて立体感が消えたりします。パステル背景にパステル文字でコントラスト不足(4.5:1割れ)になりやすく、要素が小さいと内側シャドウのぼかしが食い合って濁ります。落ち影を濃くしすぎると粘土ではなく古い影付きカードになります。

応用例

アイコンに pop アニメ(scale+rotate)を足してタップフィードバックを強める、prefers-reduced-motion でアニメを無効化する(本デモ対応済み)、グラデのアクセントボタンと組み合わせてプライマリ/セカンダリを差別化する、ダークなクレイ(暗い base+柔らかい inset)へ展開する、などが応用例です。

コード

HTML
<!-- クレイモーフィズム:ぷっくりした粘土風カード -->
<div class="clay-stage">
  <article class="clay-card">
    <!-- ぷにっとしたアイコン -->
    <div class="clay-icon" id="clayIcon">
      <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2l2.6 6.3L21 9l-5 4.3L17.5 20 12 16.5 6.5 20 8 13.3 3 9l6.4-.7z"/></svg>
    </div>
    <h2 class="clay-title">Claymorphism</h2>
    <p class="clay-text">内側と外側の影でぷっくり膨らんだ粘土のような質感。やわらかなUIにぴったり。</p>

    <div class="clay-actions">
      <button class="clay-btn clay-btn--primary" id="clayLike" type="button">
        <span class="clay-btn__emoji">♥</span> いいね <em id="clayCount">0</em>
      </button>
      <button class="clay-btn" type="button" id="clayShare">シェア</button>
    </div>
  </article>
</div>
CSS
/* クレイモーフィズム:外側の落ち影+内側の二重ハイライト影 */
:root {
  --clay-bg: #f7d4e4;
  --card: #fef3f8;
  --accent: #ff5e8a;
  --accent-2: #8b5cf6;
  --ink: #5b4660;
  /* ぷっくり感を生む内側シャドウのセット */
  --clay-shadow:
    8px 8px 16px rgba(176, 110, 145, 0.35),
    -6px -6px 14px rgba(255, 255, 255, 0.9),
    inset 6px 6px 12px rgba(255, 255, 255, 0.7),
    inset -6px -6px 12px rgba(176, 110, 145, 0.18);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 360px;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  color: var(--ink);
  background: radial-gradient(circle at 30% 20%, #ffe0ef, var(--clay-bg) 70%);
}

.clay-stage { padding: 16px; }

/* 粘土カード */
.clay-card {
  width: min(310px, 84vw);
  padding: 28px 26px 26px;
  text-align: center;
  border-radius: 34px;
  background: var(--card);
  box-shadow: var(--clay-shadow);
}

/* ぷにっとアイコン */
.clay-icon {
  width: 70px; height: 70px;
  margin: 0 auto 16px;
  display: grid; place-items: center;
  border-radius: 24px;
  color: #fff;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  box-shadow:
    5px 5px 12px rgba(176, 110, 145, 0.4),
    inset 3px 3px 7px rgba(255, 255, 255, 0.5),
    inset -3px -3px 7px rgba(0, 0, 0, 0.12);
  cursor: pointer;
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.clay-icon svg { width: 34px; height: 34px; fill: currentColor; }
.clay-icon:hover { transform: rotate(-8deg) scale(1.06); }
.clay-icon.is-pop { animation: pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); }

@keyframes pop {
  0% { transform: scale(1); }
  50% { transform: scale(1.22) rotate(12deg); }
  100% { transform: scale(1); }
}

.clay-title { margin: 0 0 8px; font-size: 22px; font-weight: 800; color: #6d4a63; }
.clay-text { margin: 0 0 22px; font-size: 13px; line-height: 1.7; color: #8a7384; }

.clay-actions { display: flex; gap: 12px; justify-content: center; }

/* 粘土ボタン */
.clay-btn {
  font: inherit; font-weight: 700; font-size: 13.5px;
  color: var(--ink);
  border: none; cursor: pointer;
  padding: 11px 18px;
  border-radius: 18px;
  background: var(--card);
  box-shadow:
    5px 5px 11px rgba(176, 110, 145, 0.32),
    -4px -4px 10px rgba(255, 255, 255, 0.85),
    inset 2px 2px 5px rgba(255, 255, 255, 0.6),
    inset -2px -2px 5px rgba(176, 110, 145, 0.12);
  transition: transform 0.14s ease, box-shadow 0.14s ease;
}
.clay-btn:active { transform: translateY(2px) scale(0.98); }

.clay-btn--primary {
  color: #fff;
  background: linear-gradient(135deg, var(--accent), #ff8fb0);
  box-shadow:
    5px 5px 12px rgba(255, 94, 138, 0.4),
    inset 2px 2px 6px rgba(255, 255, 255, 0.5),
    inset -2px -2px 6px rgba(0, 0, 0, 0.1);
}
.clay-btn__emoji { font-size: 14px; }
.clay-btn em { font-style: normal; font-weight: 800; }

@media (prefers-reduced-motion: reduce) {
  .clay-icon { transition: none; }
  .clay-icon.is-pop { animation: none; }
}
JavaScript
// いいねカウントとぷにっとポップ演出
const likeBtn = document.getElementById("clayLike");
const countEl = document.getElementById("clayCount");
const icon = document.getElementById("clayIcon");
const shareBtn = document.getElementById("clayShare");

let likes = 0;

// いいね:数を増やし、アイコンを弾ませる
likeBtn?.addEventListener("click", () => {
  likes += 1;
  if (countEl) countEl.textContent = String(likes);
  popIcon();
});

// アイコン自体のクリックでも弾む
icon?.addEventListener("click", popIcon);

// アニメ用クラスを付け外し(連打にも対応)
function popIcon() {
  if (!icon) return;
  icon.classList.remove("is-pop");
  // リフローを挟んでアニメを再起動
  void icon.offsetWidth;
  icon.classList.add("is-pop");
}

// シェア:簡易フィードバック
shareBtn?.addEventListener("click", () => {
  const original = shareBtn.textContent;
  shareBtn.textContent = "コピー済";
  setTimeout(() => { shareBtn.textContent = original; }, 1200);
});

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

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

# 追加してほしい効果
クレイモーフィズム(グラス / ニューモーフィズム)
内外の二重シャドウでぷっくり膨らんだ粘土風カード。やわらかく親しみやすいUIやSNS系コンポーネントに向きます。

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

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- クレイモーフィズム:ぷっくりした粘土風カード -->
<div class="clay-stage">
  <article class="clay-card">
    <!-- ぷにっとしたアイコン -->
    <div class="clay-icon" id="clayIcon">
      <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2l2.6 6.3L21 9l-5 4.3L17.5 20 12 16.5 6.5 20 8 13.3 3 9l6.4-.7z"/></svg>
    </div>
    <h2 class="clay-title">Claymorphism</h2>
    <p class="clay-text">内側と外側の影でぷっくり膨らんだ粘土のような質感。やわらかなUIにぴったり。</p>

    <div class="clay-actions">
      <button class="clay-btn clay-btn--primary" id="clayLike" type="button">
        <span class="clay-btn__emoji">♥</span> いいね <em id="clayCount">0</em>
      </button>
      <button class="clay-btn" type="button" id="clayShare">シェア</button>
    </div>
  </article>
</div>

【CSS】
/* クレイモーフィズム:外側の落ち影+内側の二重ハイライト影 */
:root {
  --clay-bg: #f7d4e4;
  --card: #fef3f8;
  --accent: #ff5e8a;
  --accent-2: #8b5cf6;
  --ink: #5b4660;
  /* ぷっくり感を生む内側シャドウのセット */
  --clay-shadow:
    8px 8px 16px rgba(176, 110, 145, 0.35),
    -6px -6px 14px rgba(255, 255, 255, 0.9),
    inset 6px 6px 12px rgba(255, 255, 255, 0.7),
    inset -6px -6px 12px rgba(176, 110, 145, 0.18);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 360px;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  color: var(--ink);
  background: radial-gradient(circle at 30% 20%, #ffe0ef, var(--clay-bg) 70%);
}

.clay-stage { padding: 16px; }

/* 粘土カード */
.clay-card {
  width: min(310px, 84vw);
  padding: 28px 26px 26px;
  text-align: center;
  border-radius: 34px;
  background: var(--card);
  box-shadow: var(--clay-shadow);
}

/* ぷにっとアイコン */
.clay-icon {
  width: 70px; height: 70px;
  margin: 0 auto 16px;
  display: grid; place-items: center;
  border-radius: 24px;
  color: #fff;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  box-shadow:
    5px 5px 12px rgba(176, 110, 145, 0.4),
    inset 3px 3px 7px rgba(255, 255, 255, 0.5),
    inset -3px -3px 7px rgba(0, 0, 0, 0.12);
  cursor: pointer;
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.clay-icon svg { width: 34px; height: 34px; fill: currentColor; }
.clay-icon:hover { transform: rotate(-8deg) scale(1.06); }
.clay-icon.is-pop { animation: pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); }

@keyframes pop {
  0% { transform: scale(1); }
  50% { transform: scale(1.22) rotate(12deg); }
  100% { transform: scale(1); }
}

.clay-title { margin: 0 0 8px; font-size: 22px; font-weight: 800; color: #6d4a63; }
.clay-text { margin: 0 0 22px; font-size: 13px; line-height: 1.7; color: #8a7384; }

.clay-actions { display: flex; gap: 12px; justify-content: center; }

/* 粘土ボタン */
.clay-btn {
  font: inherit; font-weight: 700; font-size: 13.5px;
  color: var(--ink);
  border: none; cursor: pointer;
  padding: 11px 18px;
  border-radius: 18px;
  background: var(--card);
  box-shadow:
    5px 5px 11px rgba(176, 110, 145, 0.32),
    -4px -4px 10px rgba(255, 255, 255, 0.85),
    inset 2px 2px 5px rgba(255, 255, 255, 0.6),
    inset -2px -2px 5px rgba(176, 110, 145, 0.12);
  transition: transform 0.14s ease, box-shadow 0.14s ease;
}
.clay-btn:active { transform: translateY(2px) scale(0.98); }

.clay-btn--primary {
  color: #fff;
  background: linear-gradient(135deg, var(--accent), #ff8fb0);
  box-shadow:
    5px 5px 12px rgba(255, 94, 138, 0.4),
    inset 2px 2px 6px rgba(255, 255, 255, 0.5),
    inset -2px -2px 6px rgba(0, 0, 0, 0.1);
}
.clay-btn__emoji { font-size: 14px; }
.clay-btn em { font-style: normal; font-weight: 800; }

@media (prefers-reduced-motion: reduce) {
  .clay-icon { transition: none; }
  .clay-icon.is-pop { animation: none; }
}

【JavaScript】
// いいねカウントとぷにっとポップ演出
const likeBtn = document.getElementById("clayLike");
const countEl = document.getElementById("clayCount");
const icon = document.getElementById("clayIcon");
const shareBtn = document.getElementById("clayShare");

let likes = 0;

// いいね:数を増やし、アイコンを弾ませる
likeBtn?.addEventListener("click", () => {
  likes += 1;
  if (countEl) countEl.textContent = String(likes);
  popIcon();
});

// アイコン自体のクリックでも弾む
icon?.addEventListener("click", popIcon);

// アニメ用クラスを付け外し(連打にも対応)
function popIcon() {
  if (!icon) return;
  icon.classList.remove("is-pop");
  // リフローを挟んでアニメを再起動
  void icon.offsetWidth;
  icon.classList.add("is-pop");
}

// シェア:簡易フィードバック
shareBtn?.addEventListener("click", () => {
  const original = shareBtn.textContent;
  shareBtn.textContent = "コピー済";
  setTimeout(() => { shareBtn.textContent = original; }, 1200);
});

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

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