design-with-taste について
Design with Tasteは、UIを構築する開発者向けの設計理念とチェックリストです。シンプルさ、空間的に一貫した遷移、適度な楽しさを軸に、具体的なインタラクションパターンとアンチパターンを示します。
主な機能
- 01段階的開示による画面構成
- 02空間的に一貫した遷移の設計
- 03操作に応じたレイヤー状の動き
- 04設計チェックリストの活用
- 05よくあるUIアンチパターンの確認
活用シーン
- 01段階式フォームやオーバーレイの設計
- 02画面やタブ切り替えの動きの設計
- 03空状態や完了時のフィードバック確認
- 04UIの一貫性と使いやすさの点検
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cristicretu/ family-taste-skill --skill design-with-tasteインストール後、Claude Code で/design-with-tasteを入力して読み込まれたか確認します。
詳細
センスのあるデザイン
このスキルには、Familyのデザイン哲学が反映されています。Familyは、生き生きとしていて、親しみやすく、意図が感じられると広く評価されているプロダクトです。Benji Taylorがbenji.org/family-valuesで最初に記録しました。
UIコードを書く前に、毎回必ず読んでください。
ユーザーは何かを作ってほしいと思っています。あなたの仕事は、真剣に取り組む人が設計したと感じられるものにすることです。
3つの柱
優先順位順です。流動性なしに喜びは生まれず、シンプルさなしに流動性は生まれません。
1. シンプルさ — 段階的な情報提示
「ユーザーが操作するたびに、インターフェースが展開し、進化していきます。まるで、互いにつながった部屋を順に歩いていくようです。」
問題点:ほとんどのUIは、あらゆる機能、選択肢、例外ケースを一度に、常に表示します。その結果、デザイナーの認知的負担がユーザーに押しつけられます。
原則:今この瞬間に必要なものだけを表示します。インターフェースは部屋を歩いて進むような感覚で、次に進む前からその先を垣間見せます。
ルール:
- 1つの画面につき、主要なアクションは1つです。 同じ重みのCTAが2つあるのは失敗です。それ以外はすべて二次的なものにします。
- 機能を一度に見せるのではなく、段階的に開示します。 階層化されたトレイ、ステップ形式のフロー、展開可能なセクションを使います。4項目のステップを3回に分ければよい場合に、12項目のフォームを一度に見せてはいけません。
- ページ全体を切り替えるより、現在のコンテキストを保つオーバーレイを使います。 現在の画面に重ねるシート、トレイ、モーダルなら、ユーザーは状況を把握し続けられます。画面全体を切り替えると、ユーザーは置き去りになります。
- 重ねるレイヤーごとに高さを変えます。 後から表示するシートやトレイは、それぞれ目に見えて高さを変え、段階が明確にわかるようにします。同じ高さのレイヤーを2つ重ねてはいけません。
- すべてのシート、トレイ、モーダルには、タイトルと閉じる操作が必要です。 ユーザーは常に、何を見ているのか、どうすれば戻れるのかを把握できなければなりません。
- トレイはコンテキストに合わせます。 ダークテーマのフロー内に表示するトレイは、暗い配色にします。ユーザーに合わせて、視覚的な環境も変化させます。
- トレイから全画面フローを開始できます。 コンパクトなトレイは、複数ステップの全画面エクスペリエンスへの有効な入口です。「トレイ」か「ページ」かの二択を押しつけてはいけません。
- 一時的なアクションにはトレイを、継続的に使う画面には全画面表示を使います。 確認、警告、コンテキストに応じた情報にはトレイを使います。設定や主要なコンテンツには全画面表示を使います。
// GOOD: Progressive tray — compact, focused, context-aware
<Sheet>
<SheetTrigger>Confirm Send</SheetTrigger>
<SheetContent className="h-[45vh]"> {/* height varies from parent */}
<SheetHeader>
<SheetTitle>Review Transaction</SheetTitle>
<DismissButton />
</SheetHeader>
{/* Core info only — no extras */}
<Button>Send $42.00</Button>
</SheetContent>
</Sheet>
セルフチェック:ユーザーは、次に何をすべきかを1秒以内に判断できますか? できない場合は、もっとシンプルにしてください。
2. 流動性 — シームレスな遷移
「私たちはテレポートするのではなく、飛んで移動します。」
問題:静的な遷移では、製品が生気のないものに感じられます。生気のない製品は、気にかけてもらえていないように感じられます。瞬間的な切り替えは空間的な方向感覚を失わせます――これはどこから来たのか? どこへ行ったのか?
原則:アプリを、破られることのない物理法則を備えた空間として扱います。遷移を追加する前に、アーキテクチャ上の理由からその遷移が理にかなっている理由を理解します。すべての要素は、どこかからどこかへ移動します。
ルール:
- 瞬間的な表示/非表示は禁止です。 表示または非表示になるすべての要素をアニメーションさせます。空間的に自然な遷移――フェード、スライド、拡大・縮小、モーフィング――を選びます。
- 共有要素の遷移。 要素が状態 A と状態 B の両方に存在する場合(展開するカード、シートに変わるボタンなど)、その要素は視覚的に両者の間を移動しなければなりません。アンマウントして再マウントするのではなく、モーフィングさせます。
- 方向の一貫性。 右へ進む(次のステップ、次のタブ)場合は、コンテンツが右から入ります。戻る場合は、コンテンツが左から入ります。現在のタブより左にあるタブへ移動する場合は、左へスライドします。これにより空間的な記憶が形成されます。
- 瞬間的な置き換えよりテキストのモーフィングを優先します。 ボタンのラベルが変わる場合(例:「続行」→「確認」)、遷移をアニメーションさせます。共有する文字列(「続」など)を特定し、残りがモーフィングする間は固定します。torph(
npm i torph)を使います。依存関係がなく、React/Vue/Svelte で動作します。最低限の代替手段はクロスフェードですが、共有文字のモーフィングが理想です。 - テキストの一部だけが変わる場合は、変わる部分だけをアニメーションさせます。 文に単語が追加または削除される場合、変わらない部分は静止させます。変わらないテキストをアニメーションさせると、不自然な重複が生じます。
- 持続する要素はその場に留めます。 ヘッダー、カード、コンポーネントが遷移をまたいで残る場合、いったんアニメーションで消えて再び現れてはいけません。移動させるのは変化する部分だけです。
- ローディング状態は目的地まで移動します。 スピナーは、処理を開始した場所にとどまるだけではありません。ユーザーが結果を探す場所まで移動します(例:トランザクションの送信後、スピナーがアクティビティタブのアイコンへ移動します)。
- 細かな方向の手がかり。 シェブロン、矢印、キャレットは、実行した操作を反映するようにアニメーションさせます。戻る操作では
→が←に変わります。アコーディオンのシェブロンは、展開時に回転します。 - 補間を統一します。 同じデータによって動くすべての視覚要素で、同じ lerp/イージングを共有します。これにより、インターフェースが個々のパーツを別々に更新するのではなく、ひとつのものが呼吸しているように感じられます。値が変わるときは、線、ラベル、軸、バッジがすべて一体となって動くようにします。
// Text morphing — use torph
import { TextMorph } from 'torph/react';
<TextMorph>{label}</TextMorph> // handles shared-letter animation automatically
// Directional tab transitions
const direction = newIndex > currentIndex ? 1 : -1;
<motion.div
key={currentTab}
initial={{ x: direction * 20, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
exit={{ x: -direction * 20, opacity: 0 }}
transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
/>
// Shared element: card → detail view
<motion.div
layoutId={`card-${id}`}
className={isExpanded ? "fixed inset-0 rounded-none" : "rounded-xl"}
transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
/>
黄金のイージングカーブ:cubic-bezier(0.16, 1, 0.3, 1) — 素早く始まり、穏やかに落ち着きます。すべての登場とモーフィングのデフォルトです。退出時のみ ease-in(cubic-bezier(0.4, 0, 1, 1))を使います。linear は決して使いません。
セルフチェック:画面を録画して、0.5 倍速で再生します。すべての要素の動きを追えますか? テレポートする要素には、遷移が必要です。
3. 喜び — 選択的な強調
「喜びを極めることは、選択的な強調を極めることです。」
問題点:個性がまったくない(企業的で中身のない表現)か、何もかもが跳ねたり輝いたりする(うっとうしい)かのどちらかです。どちらも本質を外しています。
原則:喜びとインパクトの曲線 — 機能が使われる頻度が低いほど、より大きな喜びをもたらすべきです。毎日使う操作には、さりげない工夫を加えつつ効率性が必要です。めったにない瞬間には、演出を効かせましょう。
Delight ↑
| * (rare features: theatrical)
| *
| *
| * * (medium: memorable)
| * *
|* * * (frequent: subtle)
+------------------→ Feature frequency
ルール:
- すべてを等しく磨き上げます。 設定ページ、空の状態、エラー画面にも、ヒーローセクションと同じだけ心を配ります。磨かれていない箇所がひとつあるだけで、全体が未完成に感じられます。「豪華なレストランなのに、トイレが汚いようなものです。」
- イースターエッグで探索に報います。 思いがけない場所に仕掛けを隠しましょう。ユーザーが人に話したくなるような体験が生まれます。重要なのは、発見がうっとうしさではなく報酬だと感じられるよう、ほどよく使われる機能に配置することです。
- 完了を祝います。 バックアップ、オンボーディング、初回の取引など、重要な操作には紙吹雪、カスタムアニメーション、心地よいサウンドを用意しましょう。緑色のチェックマークだけでは不十分です。
- 破壊的な操作を気持ちよくします。 項目を削除するときは、効果音とともにゴミ箱へ転がり込むようにします。破壊的な操作だからといって、不快である必要はありません。
- 数値やライブチャートにアニメーションを付けます。 変化する値(価格、件数、残高)は、カウントアップ、反転、変形などのアニメーションを付けましょう。数値が大きくなるとき、カンマの位置も滑らかに移動させ、単に切り替えるだけにしないでください。リアルタイムの折れ線グラフには、liveline(
npm i liveline)を使用します。キャンバスひとつで動作し、React 18 以外の依存関係はなく、補間は 60fps です。60fps の値表示を重ねる場合は、再レンダーのオーバーヘッドを避けるため、React の状態を介さず DOM を直接更新します。 - 空の状態は第一印象を決めます。 作成ボタンを指すアニメーション付きの矢印、浮かぶイラスト、温かみのあるメッセージを用意しましょう。他に何もないまま「項目はまだありません」と表示するのは避けてください。
- サウンドデザインで実在感を高めます。 完了音や、さりげない操作時のフィードバックを取り入れましょう。サウンドは報酬を印象づけ、操作に実在感を与えます。
- ドラッグ&ドロップを心地よくします。 積み重ねるアニメーション、滑らかな並べ替え、持ち上げたときの視覚的フィードバックを用意しましょう。項目の並べ替えは、結果から想像する以上に気持ちよく感じられるべきです。
喜びのパターンライブラリ — 効果が実証されている具体的な演出:
| 機能 | 頻度 | 喜びのレベル | パターン |
|---|---|---|---|
| 数値入力 | 毎日 | 控えめ | 数字を入力するたびにカンマの位置が移動する |
| タブ/チャートの移動 | 毎日 | 控えめ | 値の変化に合わせて矢印アイコンの向きが反転する |
| 空の状態 | 初回訪問時 | 中程度 | アニメーション付きの矢印+浮かぶイラスト |
| 項目の並べ替え | 時々 | 中程度 | 積み重ねるアニメーション+滑らかなドロップ |
| 削除/ゴミ箱 | 時々 | 中程度 | 項目がスキューモーフィックなゴミ箱に転がり込み、サウンドが鳴る |
| 機能の初回利用 | 1 回 | 高い | 空の状態にアニメーション付きのガイド矢印を表示する |
| 重要な操作の完了(バックアップ、オンボーディング) | 1 回 | 演出的 | 紙吹雪が舞い、祝福のサウンドが鳴る |
| イースターエッグ(QR、隠しジェスチャー) | まれ | 演出的 | タップすると波紋が広がり、スワイプするとスパンコールのエフェクトが現れる |
// Animated number with smooth comma shifting
function AnimatedNumber({ value }) {
const spring = useSpring(value, { stiffness: 80, damping: 20 });
return <motion.span>{useTransform(spring, v => Math.round(v).toLocaleString())}</motion.span>;
}
// Real-time chart — liveline handles interpolation, momentum arrows, scrub, theming
import { Liveline } from 'liveline';
<div style={{ height: 200 }}>
<Liveline
data={history} // [{ time, value }]
value={latestValue} // current number
momentum // directional arrows (green/red/grey)
showValue // 60fps DOM overlay, no re-renders
color="#3b82f6" // derives full palette from one color
/>
</div>
// Satisfying empty state
function EmptyState() {
return (
<div className="flex flex-col items-center gap-4 py-16">
<motion.div animate={{ y: [0, -8, 0] }} transition={{ repeat: Infinity, duration: 2, ease: "easeInOut" }}>
<IllustrationIcon />
</motion.div>
<p className="text-muted">Nothing here yet</p>
<motion.div animate={{ x: [0, 5, 0] }} transition={{ repeat: Infinity, duration: 1.5 }}>
<ArrowRight className="inline mr-1" /> Create your first item
</motion.div>
</div>
);
}
// Confetti on significant completion
function CompletionScreen() {
useEffect(() => { playSound('success'); }, []);
return (
<motion.div initial={{ scale: 0.8, opacity: 0 }} animate={{ scale: 1, opacity: 1 }}
transition={{ type: "spring", damping: 15, stiffness: 200 }}>
<ConfettiExplosion />
<h2>You're all set!</h2>
</motion.div>
);
}
セルフチェック:UI を誰かに 30 秒間見てもらいましょう。笑顔になりますか? そうでなければ、喜びを加えましょう。うっとうしそうな顔をしていますか? 使用頻度の高い操作に喜びを加えすぎています。
デザインセンスのチェックリスト
UI を「完成」と判断する前に確認します。
シンプルさ
- 各画面に、明確な主要アクションがひとつある
- 複雑なフローを、理解しやすいステップに分割している
- 情報を一度にすべて見せず、段階的に表示している
- 画面遷移中もコンテキストを維持している(画面遷移よりオーバーレイを優先する)
- 重なり合うレイヤーの高さの違いが視覚的に分かる
- すべてのオーバーレイに、タイトルと閉じる操作がある
- ユーザーが現在地と戻り方を常に把握できる
流動性
- 突然表示・非表示になるものはゼロに — すべてアニメーションさせる
- 共通要素は状態間で変形させる(アンマウント・再マウントしない)
- 方向性のあるトランジションを空間の論理に合わせる
- 表示され続ける要素に重複したアニメーションを適用しない
- テキストの変更には、最低限 torph またはクロスフェードを使う
- テキストの一部だけが更新される場合は、変更部分だけをアニメーションさせる
- ローディング状態は、結果が表示される場所へ移動させる
- シェブロンや矢印に、方向を示すさりげない手がかりを加える
- 同じデータに基づく要素には、同じ lerp/イージングを使う(一貫した補間)
- デフォルトのイージングには
cubic-bezier(0.16, 1, 0.3, 1)を使う
楽しさ
- 使用頻度の高い機能には、さりげないマイクロインタラクションを加える
- 使用頻度の低い機能には、印象に残る演出を加える
- 空の状態もきちんとデザインし、後回しにしない
- 完了を祝う演出を加える(チェックマークだけで済ませない)
- 数値が変化するときはアニメーションさせる
- イースターエッグや隠し演出を少なくとも1つ用意する
- どの隅々まで丁寧に仕上げる — 手抜きの場所を残さない
- 少なくとも1か所は、誰かが「おお、いいね」と言いたくなる演出を加える
全般的なセンス
- ありきたりなAI風の美的表現(Interフォント、紫のグラデーション、画一的なレイアウト)を避ける
- ディスプレイ用と本文用のフォントを組み合わせ、タイポグラフィに意図を持たせる
- メインカラーを軸に、鮮やかなアクセントを加えたカラーパレットにする
- 余白をゆったりと取り、一貫性を保つ
- インターフェースをスライドショーではなく、物理的な空間のように感じさせる
- すべてのピクセルに、細部まで気を配る人の意図を感じさせる
センスを損なうアンチパターン
- 静的なタブ切り替え。 方向性のあるスライドがないと、画面の切り替わりに違和感が生じます。
- どこからともなく現れるモーダル。 トリガーから拡大するか、画面端からスライドさせます。中央で単に
opacity: 0 → 1と変化させるのは避けます。 - 実際のレイアウトに合わないスケルトンスクリーン。 スケルトンが3本のバーで、コンテンツが5行なら、ユーザーの期待を裏切ります。
- 重複したアニメーション。 ページ遷移中に、表示され続けるヘッダーがフェードアウトして再びフェードインすること。表示され続ける要素は、そのまま表示します。
- 線形イージング。 現実世界で線形に動くものはありません。
- 「項目はありません」という空のテキスト。 第一印象を決めるものです。きちんと作り込みます。
- 重ねたレイヤーのサイズが均一。 2枚のシートが同じ高さだと、奥行きや進行の感覚が失われます。
- 重要な結果をトーストで伝える。 トーストは補足情報向けです。成功・エラー・完了は、文脈に沿った場所にインラインで表示し、アニメーションさせます。
- 入力欄をただ積み重ねただけのフォーム。 ステップごとに進み、その間にトランジションを入れます。
- 操作に反応しないボタン。 ホバー、アクティブ、フォーカスの状態を必ず用意します。
- 変化していないテキストのアニメーション。 文中の1語だけが変わるなら、その語だけを動かします。
- 操作後も最初の位置に残るスピナー。 ユーザーが結果を探す場所へ移動させます。
イージングとタイミングの参考
| 用途 | イージング | 時間 |
|---|---|---|
| 要素の表示 | cubic-bezier(0.16, 1, 0.3, 1) | 300–400ms |
| 要素の非表示 | cubic-bezier(0.4, 0, 1, 1) | 200–250ms |
| 共通要素の変形 | cubic-bezier(0.16, 1, 0.3, 1) | 350–500ms |
| マイクロインタラクション(ホバー、押下) | cubic-bezier(0.2, 0, 0, 1) | 100–150ms |
| スプリング(弾む動き) | damping: 20, stiffness: 300 | 自動 |
| スプリング(滑らかな動き) | damping: 30, stiffness: 200 | 自動 |
| 数値のカウント | ease-out cubic | 400–800ms |
| ページ遷移 | cubic-bezier(0.16, 1, 0.3, 1) | 300ms |
| 項目間のスタッガー | — | 項目ごとに30–60ms |
おすすめのツール
これらのライブラリは Family と同じ開発者によって作られており、同じ理念を体現しています。
| ライブラリ | 用途 | インストール |
|---|---|---|
| torph | 依存関係のないテキスト変形。共通する文字間のトランジションを自動で処理します。React、Vue、Svelteに対応。 | npm i torph |
| liveline | リアルタイムのアニメーション付き折れ線グラフ。1つのキャンバス、60fpsのlerp、モメンタム矢印を備え、React 18以外の依存関係はありません。 | npm i liveline |
変化するテキストやリアルタイムの数値・グラフデータを扱うものを作るときは、自作する前にこれらを使ってみてください。
このスキルの使い方
- すべての UI タスクに取りかかる前に読んでください。 作業の後ではなく、前です。
- 柱となる要素を順番に適用してください。 シンプルさ → 流動性 → 喜び。アニメーションで悪いレイアウトを磨き上げることはできません。
- 納品前にチェックリストを実行してください。
- ビジュアル面の美しさ(タイポグラフィ、色、レイアウト)には
frontend-designスキルを組み合わせてください。 このスキルが扱うのは、感触とインタラクションの品質です。 - 迷ったら、アニメーションを加えてください。 生気のないインターフェースに活気を加えるより、控えめにするほうが簡単です。
- 0.5 倍速で録画して確認してください。 スローモーションにすると、あらゆる瞬間移動のような動き、不自然なカット、見逃された工夫の余地が明らかになります。
目標は、単に「動く」ものを作ることではありません。使った人が、*「これを作った人は、本気でこだわっている」*と思うものを作ることです。
それがセンスです。