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のポイントをまとめる。
最新のデザイントレンドを実装してみると、CSSへの理解が実践的に深まる。ライブラリに頼らずとも、CSSは十分にリッチな表現ができる。