CSSだけでリッチなクレイモーフィズムUIのタスク管理アプリを作った

クレイモーフィズムとは何か

ここ数年のUIデザイントレンドを追っていると、グラスモーフィズム(すりガラス風)の次に登場したのが「クレイモーフィズム(Claymorphism)」だ。粘土のような柔らかい立体感、丸みのあるフォルム、パステルカラーの組み合わせが特徴で、Apple の旧 iOS やポップなデザインツールのアイコンに近い質感がある。

今回作ったのは、このクレイモーフィズムUIをCSSだけで実装したタスク管理アプリだ。「CSSだけで」というのが肝で、フレームワークもアニメーションライブラリも使っていない。

クレイモーフィズムの視覚的要素

クレイモーフィズムを構成する要素は大きく4つある。

1. 多層box-shadow: 上からのハイライト + 下からの影で立体感を出す

2. 大きなborder-radius: 角を丸くして柔らかさを表現する

3. パステルカラー: 彩度を抑えた柔らかい色調

4. インナーシャドウ: 凹んだような質感(box-shadow: inset

これをCSSで再現する。

核心: クレイエフェクトの実装

.clay-card {
  background: #f0e6ff;
  border-radius: 24px;
  padding: 1.5rem;

  /* クレイモーフィズムの核心: 多層box-shadow */
  box-shadow:
    /* 外側の大きな影 */
    8px 8px 20px rgba(174, 140, 220, 0.35),
    /* 外側の反対側の明るい影(立体感) */
    -4px -4px 12px rgba(255, 255, 255, 0.7),
    /* 内側の上ハイライト(光が当たっている感じ) */
    inset 2px 2px 5px rgba(255, 255, 255, 0.6),
    /* 内側の下シャドウ(奥行き感) */
    inset -2px -2px 5px rgba(174, 140, 220, 0.2);

  border: 1.5px solid rgba(255, 255, 255, 0.8);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.clay-card:hover {
  transform: translateY(-4px) scale(1.01);

  box-shadow:
    12px 16px 28px rgba(174, 140, 220, 0.45),
    -6px -6px 16px rgba(255, 255, 255, 0.8),
    inset 3px 3px 7px rgba(255, 255, 255, 0.7),
    inset -3px -3px 7px rgba(174, 140, 220, 0.25);
}

box-shadow を4層重ねることで「粘土のような押し込み感」が生まれる。この多層シャドウがクレイモーフィズムのCSSポイントだ。

カラーパレットの定義

クレイモーフィズムの色はシステム的に管理する。

:root {
  /* ベースカラー */
  --clay-bg: #fdf6ff;

  /* タスク優先度別カラー */
  --clay-red:    #ffcdd2;
  --clay-red-shadow: rgba(229, 115, 115, 0.4);

  --clay-yellow: #fff9c4;
  --clay-yellow-shadow: rgba(255, 213, 79, 0.4);

  --clay-green:  #c8e6c9;
  --clay-green-shadow: rgba(102, 187, 106, 0.4);

  --clay-blue:   #bbdefb;
  --clay-blue-shadow: rgba(100, 181, 246, 0.4);

  --clay-purple: #f0e6ff;
  --clay-purple-shadow: rgba(174, 140, 220, 0.35);

  /* 共通 */
  --clay-highlight: rgba(255, 255, 255, 0.7);
  --clay-radius: 24px;
  --clay-radius-sm: 16px;
}

CSS変数で定義しておくと、カラーテーマの変更が一箇所で済む。

ボタンの立体感

ボタンもクレイモーフィズムで仕上げる。押したときに凹む感触をCSSだけで表現する。

.clay-btn {
  background: var(--clay-purple);
  border: 1.5px solid rgba(255, 255, 255, 0.8);
  border-radius: var(--clay-radius-sm);
  padding: 0.75rem 1.5rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.15s ease;

  box-shadow:
    5px 5px 12px var(--clay-purple-shadow),
    -3px -3px 8px var(--clay-highlight),
    inset 1px 1px 3px rgba(255, 255, 255, 0.6),
    inset -1px -1px 3px var(--clay-purple-shadow);
}

.clay-btn:hover {
  transform: translateY(-2px);
  box-shadow:
    7px 9px 16px var(--clay-purple-shadow),
    -4px -4px 10px var(--clay-highlight),
    inset 1px 1px 3px rgba(255, 255, 255, 0.7),
    inset -1px -1px 3px var(--clay-purple-shadow);
}

.clay-btn:active {
  transform: translateY(1px);
  /* 押し込まれた状態: 影の方向を逆にする */
  box-shadow:
    2px 3px 6px var(--clay-purple-shadow),
    -1px -1px 4px var(--clay-highlight),
    inset 3px 3px 7px var(--clay-purple-shadow),
    inset -1px -1px 3px rgba(255, 255, 255, 0.5);
}

:active 状態で inset の影を強くすることで「ボタンが押し込まれた」感触を演出できる。

タスクカードのHTML構造

デザインシステムの調査

SLDSとMaterial Designを比較する

デザイン

Vanilla JS でタスクを動かす

状態管理と描画はシンプルなパターンで実装する。

var TaskApp = (function () {
  var tasks = loadFromStorage();

  function loadFromStorage() {
    var saved = localStorage.getItem('clay-tasks');
    return saved ? JSON.parse(saved) : [];
  }

  function saveToStorage() {
    localStorage.setItem('clay-tasks', JSON.stringify(tasks));
  }

  function addTask(title, memo, priority, dueDate, tag) {
    var task = {
      id: Date.now().toString(),
      title: title,
      memo: memo || '',
      priority: priority,
      dueDate: dueDate || '',
      tag: tag || '',
      completed: false,
      createdAt: new Date().toISOString(),
    };

    tasks.unshift(task); // 先頭に追加
    saveToStorage();
    renderAll();
  }

  function toggleComplete(id) {
    var task = tasks.find(function (t) { return t.id === id; });
    if (task) {
      task.completed = !task.completed;
      saveToStorage();
      renderAll();
    }
  }

  function deleteTask(id) {
    tasks = tasks.filter(function (t) { return t.id !== id; });
    saveToStorage();
    renderAll();
  }

  function renderAll() {
    var container = document.getElementById('task-list');
    container.innerHTML = '';

    var filtered = currentFilter === 'all'
      ? tasks
      : tasks.filter(function (t) {
          return currentFilter === 'active' ? !t.completed : t.completed;
        });

    if (filtered.length === 0) {
      container.innerHTML = '

タスクがありません

'; return; } filtered.forEach(function (task) { var card = createCardElement(task); container.appendChild(card); // カードを少し遅らせてフェードイン requestAnimationFrame(function () { card.classList.add('is-visible'); }); }); } return { addTask: addTask, toggleComplete: toggleComplete, deleteTask: deleteTask }; })();

localStorage で永続化しているので、ページをリロードしてもタスクが消えない。

フェードインアニメーション

タスク追加時のアニメーションをCSSだけで実装する。

.task-card {
  opacity: 0;
  transform: translateY(12px) scale(0.97);
  transition:
    opacity 0.3s ease,
    transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.task-card.is-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}

/* 完了時のストライク */
.task-card.is-completed .task-title {
  text-decoration: line-through;
  opacity: 0.5;
}

/* 削除アニメーション */
.task-card.is-removing {
  animation: remove-card 0.25s ease forwards;
}

@keyframes remove-card {
  to {
    opacity: 0;
    transform: scale(0.9) translateX(20px);
    max-height: 0;
    margin: 0;
    padding: 0;
  }
}

cubic-bezier(0.34, 1.56, 0.64, 1) は「バウンス感のある」イージングだ。spring 的な動きで、クレイモーフィズムの「柔らかい」世界観に合う。

作ってみた感想

クレイモーフィズムを実装して感じたのは、「box-shadow の理解が深まる」ことだ。多層シャドウを組み合わせることで、CSS単体でここまでリッチな質感が出せると改めて驚いた。

トレンドデザインを自分で実装してみることで、「なぜこのデザインが気持ちいいか」を技術的に理解できる。グラスモーフィズムなら backdrop-filter の挙動、ニューモーフィズムなら inset シャドウの使い方——同じCSSプロパティでも使い方で全く違う質感が生まれる。

次はクレイモーフィズムにダークモードを加えたバリエーションを作ってみたい。パステルカラーのダークモードは「ネオンクレイ」と呼ばれる派生スタイルがあり、これも面白そうだ。

まとめ

クレイモーフィズムUIのポイントをまとめる。

  • 多層 `box-shadow`(外側の影 + 内側のハイライト)が核心
  • パステルカラー + CSS変数でテーマ管理
  • `:active` 状態で `inset` を強化して「押し込み感」を演出
  • `cubic-bezier` でバウンス感のあるアニメーション
  • Vanilla JS + `localStorage` でシンプルな状態管理
  • 最新のデザイントレンドを実装してみると、CSSへの理解が実践的に深まる。ライブラリに頼らずとも、CSSは十分にリッチな表現ができる。