最悪のUXアンチパターンと改善策
本番環境で今も使われ続けるUXアンチパターンを、ダークパターン、壊れたトグル、アクセシビリティの観点からコード修正例つきで解説。

先週、ある大手航空会社のサイトでCookieの設定を変えようとしました。「すべて承諾」ボタンは鮮やかな青で、見逃しようがありません。一方の「設定を管理」リンクはどうでしょう?グレーの文字で11pxのフォント、法律用語だらけの段落の下にひっそり隠れています。ようやく見つけて開いたページには、47個の個別トグルスイッチが並んでいました。どれもデフォルトはオンで、「すべて拒否」ボタンはどこにもありません。1分間ひたすらトグルをクリックし続けた末に諦めて、結局ブラウザのCookieを手動で消しました。
これは一度きりの話ではありません。いつものことです。悪いUXは単に面倒なだけでなく、ユーザーに対して敵対的です。そして一番残念なのは、こうしたアンチパターンの多くが、ダークパターンを作り込む手間よりずっと短時間で直せる単純な修正で済むことです。私は10年以上Webアプリを作ってきましたが、いまだにこの手の代物を出荷し続けていることが正直なところ理解できません。というわけで、最悪の元凶と、それを退治する方法を見ていきましょう。
ユーザーをカモ扱いするダークパターン
ダークパターンは事故で生まれるものではありません。会議室で誰かがコンバージョン指標を眺めて、ユーザーを騙すのは許容できるビジネス戦略だと判断した結果です。だからこそ腹立たしいのです。意図的なんですから。
Confirmshaming(罪悪感を煽る表示):UI要素としての罪悪感
見覚えがあるはずです。ニュースレターの購読を求めるモーダルがポップアップし、承諾ボタンには「はい、お得に買い物したいです!」、拒否ボタンには「いいえ、定価で買うほうが好きです」と書かれている。これがconfirmshamingで、ECサイトからSaaSのオンボーディング、果ては一部の開発者向けツールにまで溢れています。効果があるのはごく一部のユーザーだけで、残りの全員を遠ざけています。
修正方法は情けないほど単純です。両方の選択肢で中立的な言葉を使うだけです。
<!-- BEFORE: Manipulative confirmshaming -->
<div class="modal">
<p>Join 50,000 smart developers!</p>
<button class="btn-primary">Yes, sign me up!</button>
<button class="btn-link text-sm">
No thanks, I don't care about my career
</button>
</div>
<!-- AFTER: Respectful, neutral options -->
<div class="modal">
<p>Get weekly frontend tips — no spam, unsubscribe anytime.</p>
<button class="btn-primary">Subscribe</button>
<button class="btn-secondary">No thanks</button>
</div>
修正後のバージョンでは、水増しされた社会的証明を削り、スパムはないという明確な約束を加えている点にも注目してください。誠実さは操作よりも信頼を築きます。長期的なコンバージョンがそれに報いてくれるはずです。
ルーチホテル(Roach Motel):ワンクリック登録、七段階の解約
登録は30秒で終わります。ところが解約には、設定 > アカウント > サブスクリプション > プランを管理 > プランを解約 > 理由を教えてください > 本当に解約しますか > 引き留め担当に相談 > 実際に解約、と進まされます。電話が必要なサービスすらまだあります。2026年にもなって。ワンクリックで始めた定期購読の解約に、です。
引き留め指標がどう言っているかは知ったことではありません。囚われたユーザーは忠実な顧客ではなく、チャージバックや星1つのレビューを生み出す人質です。解約は登録と同じくらい簡単にしてください。
- 解約オプションは、人が探すであろうアカウント設定に置く
- 最大2ステップまで:「本当によろしいですか?」の後に「完了。アカウントは解約されました」
- 解約ボタンを「サポートに問い合わせる」リンクの裏に隠さない
- 引き留めのための割引を出すのは構わないが、解約フローの必須ステップにはしない
- 解約が取り返しのつかないものに感じられないよう、再開用リンク付きの確認メールを送る
壊れたトグルスイッチと分かりにくいUIコントロール
ちょっとした実験をしてみましょう。スマホの設定を開いて、オンかオフか即座に判断できないトグルがいくつあるか数えてみてください。待っていますよ。
曖昧なトグルは、コードレビューで私が最もよく目にするUIの失敗の一つです。トグルが伝えるべきことは一つだけ、現在の状態です。オンなのかオフなのか。それだけです。ところが開発者は、両方の状態がほぼ同じに見えるトグル、色分けが曖昧なトグル、今起きていることではなくこの後何が起きるかを示すトグルを出荷し続けています。
/* BEFORE: Both states look nearly identical */
.toggle {
width: 48px;
height: 24px;
background: #d1d5db;
border-radius: 12px;
cursor: pointer;
}
.toggle.active {
background: #9ca3af; /* barely different from inactive */
}
/* AFTER: Unmistakable state difference + accessible */
.toggle {
width: 48px;
height: 24px;
background: #d1d5db;
border-radius: 12px;
cursor: pointer;
position: relative;
}
.toggle[aria-checked="true"] {
background: #2563eb; /* strong contrast from inactive */
}
.toggle .label {
font-size: 10px;
font-weight: 600;
color: white;
}
.toggle[aria-checked="true"] .label::after { content: "ON"; }
.toggle[aria-checked="false"] .label::after { content: "OFF"; }
人を混乱させないトグルの三つのルール:状態ごとに異なる色を使うこと(色覚多様性のあるユーザーにも機能するだけのコントラストを確保する)、トグルの中または隣にテキストラベルを置くこと、そしてスクリーンリーダーが状態を読み上げられるよう適切なaria-checked属性を付けること。色だけに頼っているなら、UXとアクセシビリティの両方で一度に失敗しています。
ネイティブのフォームコントロールを作り直すのはやめよう
フロントエンドのPRをたくさんレビューしていますが、どうしても許せないパターンが一つあります。キーボード操作を壊すカスタムドロップダウンです。開発者が二日かけて、divとクリックハンドラーを山ほど使った凝ったselectメニューをゼロから作ります。デモでは見栄えが良い。ところがユーザーがフォームをタブで移動して、そのカスタムドロップダウンに来ると、何も起きない。矢印キー操作もなければ、入力して絞り込む機能もない。パスワードマネージャーでも動かず、モバイルでも壊れている。
一方で、ネイティブの<select>要素や、Radix UIやHeadless UIのようなテスト済みのライブラリを使えば、これらすべてが無料で手に入ります。HTMLのpopover APIと<selectlist>要素も、今ではブラウザ対応がしっかりしています。ぜひ使ってください。ユーザーは、ドロップダウンにカスタムのアニメーションがあるかどうかなど気にしません。動くかどうかを気にしているのです。
実在するユーザーを締め出すアクセシビリティのアンチパターン
アクセシビリティは「あったら良いもの」ではありません。将来のスプリントで追加する機能でもありません。世界中で10億人以上が何らかの障害とともに暮らしており、WebAIM Millionレポートでは、上位100万サイトの96%以上に検出可能なWCAG違反があると一貫して報告されています。これらは珍しいエッジケースではなく、壊れたボタン、ラベルの欠落、alt属性のない画像といった、ごくありふれた問題です。
私が最もよく見かけるのは次のようなものです:
- alt属性のない画像:スクリーンリーダーは「画像」と読み上げて、そのまま次に進んでしまう
- コントラスト比が4.5:1未満:ロービジョンのユーザーにとってテキストが読めなくなる
- キーボード操作の未対応:アプリをタブで移動できなければ、キーボードユーザーやスイッチデバイスのユーザーは締め出される
- モーダル内のフォーカストラップ:ダイアログを開いたユーザーが、マウスなしでは抜け出せない
- フォームラベルの欠落:スクリーンリーダーは入力欄が何のためのものか分からない
- 一時停止ボタンのない自動再生動画:前庭障害のあるユーザーにとって平衡感覚を乱される
最も手早く効果が出るのは、CIパイプラインに自動アクセシビリティチェックを追加することです。すべてを捕まえられるわけではありません。自動ツールが見つけられるのはおそらく問題の30〜40%程度です。それでも、明らかな問題は出荷前に捕まえてくれます。
// BEFORE: No accessibility testing at all
test('homepage loads', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1')).toBeVisible();
});
// AFTER: Accessibility violations fail the build
import AxeBuilder from '@axe-core/playwright';
test('homepage loads and is accessible', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1')).toBeVisible();
const a11yResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.analyze();
expect(a11yResults.violations).toEqual([]);
});
設定にかかるのは5分です。すべてのPRで実行され、alt属性の欠落、壊れたARIAロール、不十分なコントラスト、その他数十種類の問題を自動で検出します。やらない理由はありません。
認知的過負荷:地獄の設定ページ
人間のワーキングメモリが同時に保持できるのは、およそ4〜7個の項目です。これは提案ではなく、動かしがたい認知の限界です。インターフェースが階層のないまま200個のオプションを一つのページに並べると、ユーザーに自由を与えているのではなく、決断麻痺を与えているのです。
職場で使っていたプロジェクト管理ツールの設定を数えたことがあります。347個のオプションが一つのページにありました。検索はなく、「一般」や「詳細」といった曖昧なカテゴリ以外のグルーピングもありません。チームの半分は、あまりの圧迫感に一度も設定を変えたことがなく、そのページをすぐ閉じていました。
解決策はプログレッシブ・ディスクロージャー(段階的開示)です。人が実際に変更する五つの設定だけを表示する。それ以外は、明確にラベルされた展開可能なセクションの裏に置く。検索バーを追加する。そして頼むから、適切なデフォルト値を用意して、ほとんどのユーザーが設定ページを開く必要がないようにしてください。
設定ページに専用のドキュメントが必要なら、その設定ページこそが問題です。
通知の過多が、ユーザーにすべてを無視させる
あらゆるイベントが通知を発生させるとどうなるか。チームメイトがチャンネルに参加した、誰かが絵文字でリアクションした、デプロイが終わった、30分後にカレンダーの予定が始まる。ユーザーはそのすべてを無視することを覚えます。通知システムはただのホワイトノイズになってしまいます。本当に重要な重大アラートは?未読通知47件の下に埋もれています。
答えは保守的なデフォルト設定です。新規ユーザーには重大な通知だけを届ける。緊急性の低いものは日次ダイジェストにまとめる。そして常に、ユーザーが通知そのものから直接ミュートしたりカスタマイズしたりできるようにしてください。三クリック先の奥まった設定ページからではなく、です。
遅いUIは壊れたUI:UXの問題としてのパフォーマンス
反応に2秒かかるボタンは、遅いのではなく壊れています。ユーザーは「サーバーが処理しているんだな」とは思いません。「クリックが届いたのか?」と考えて、もう一度クリックします。その結果、二重送信、混乱した状態、そしてアプリを信用しなくなったユーザーが残ります。
100ミリ秒を超えると遅く感じ始め、1秒を超えるとユーザーの思考の流れが途切れます。それでも私たちは、数メガバイトのJavaScriptバンドル、描画をブロックするサードパーティのスクリプト、人が違う要素をクリックしてしまうようなレイアウトシフトを出荷し続けています。解決は複雑ではありません。ただ気にかけるかどうかの問題です。
// BEFORE: User clicks, waits 2 seconds, nothing happens
async function handleLike(postId: string) {
const result = await fetch(`/api/posts/${postId}/like`, {
method: 'POST',
});
if (result.ok) {
setLikeCount(prev => prev + 1); // UI updates after network round-trip
}
}
// AFTER: Optimistic update — UI responds instantly
function handleLike(postId: string) {
// Update UI immediately
setLikeCount(prev => prev + 1);
// Sync with server in background
startTransition(async () => {
const result = await fetch(`/api/posts/${postId}/like`, {
method: 'POST',
});
if (!result.ok) {
// Rollback only on failure
setLikeCount(prev => prev - 1);
toast.error('Something went wrong. Try again.');
}
});
}
楽観的更新、スピナーの代わりにスケルトンスクリーン、重要でないJavaScriptの遅延読み込み、そしてCore Web Vitalsを後回しにせず実際の指標として監視すること。これらは高度なテクニックではありません。最低限の期待値です。
しぶとく生き残るモバイルUXのアンチパターン
モバイルには独自のUXの罪がいくつかあります。主な原因は、開発者が最新のiPhoneでテストして、それで良しとしてしまうことです。
正確な操作を要求するタッチターゲット
Appleのガイドラインでは、タッチターゲットは最小44×44 CSSピクセルとされています。WCAG 2.2も同じ基準です。それでも、モーダルの20ピクセルの閉じるボタン、フッターに詰め込まれた小さなリンク、スマホではほぼ押せないアイコンボタンを今でも見かけます。運動機能に障害のあるユーザーにとっては、さらに深刻です。
/* BEFORE: Tiny, frustrating touch targets */
.close-btn {
width: 20px;
height: 20px;
font-size: 12px;
}
.footer-links a {
padding: 2px 4px;
font-size: 11px;
}
/* AFTER: Actually tappable elements */
.close-btn {
min-width: 44px;
min-height: 44px;
display: flex;
align-items: center;
justify-content: center;
}
.footer-links a {
padding: 12px 8px;
font-size: 14px;
/* spacing between adjacent targets */
margin: 4px 0;
}
コンテンツを遮るフルスクリーンのインタースティシャル
検索結果をタップします。ページが読み込まれます。一語も読まないうちに、フルスクリーンのモーダルがメールアドレスを要求してくる。まだコンテンツも見ていないのに、なぜ購読しようと思うでしょうか。
Googleは2017年から、検索ランキングでインタースティシャルの押し付けを罰しています。それでも残り続けているのは、どこかの誰かが「メール登録率2%」と表示するダッシュボードを見て勝利だと喜び、その結果生まれている40%の直帰率を無視しているからです。インラインのバナーやボトムシートを使いましょう。ユーザーが実際にコンテンツに関わるまで、何かを求めるのを待ってください。記事を三本読み終えた読者は、喜んで購読してくれます。三回も邪魔された読者は、去って二度と戻ってきません。
UX品質の文化を作る(個々のバグを直すだけではなく)
アンチパターンを一つずつ直すのは必要ですが、それだけでは不十分です。プロセスがアンチパターンを生み続けているなら、プロセスを変える必要があります。実際に効果のあるものは次のとおりです:
- CIパイプラインにaxe-coreを追加する:すべてのPRで自動のa11yチェックを行う
- UXレビューを、デザイン専用の別プロセスではなく、コードレビューの一部にする
- 大きな機能をリリースする前に実在するユーザー5人でテストする:5人いれば、ユーザビリティ問題のおよそ85%が見つかる
- ページビューや滞在時間だけでなく、タスク完了率とエラー率を追跡する
- アクセシブルな部品があらかじめ用意されたデザインシステムを使い、個々の開発者が同じインタラクションの問題をゼロから解き直さないようにする
- 自社のプロダクトを徹底的に使い込む(設定ページを作った開発者が毎日それを使わなければならないなら、すぐに直されるはず)
私が今までに行った中で最も効果のあったUX改善は、自分のスマホで自社のチェックアウトフローを使ってみて、午後11時にプロダクトのチャンネルで怒りのメッセージを送ったことから始まりました。
この記事で取り上げたアンチパターンには、すべてシンプルな解決策があります。最先端の技術も大規模な再設計も必要ありません。必要なのは、ちゃんと気にかけることだけです。今日は一つだけ直してみてください。今週は一度アクセシビリティ監査を実行してください。今月は実在するユーザー一人でテストしてください。良いUXとは派手なジェスチャーのことではなく、短期的な指標よりもユーザーへの敬意を一貫して選ぶことです。人が我慢できるソフトウェアと、人が愛するソフトウェアの差は、思っているよりずっと小さいものです。それは、丁寧に行われた無数の小さな判断の積み重ねにすぎません。


