メインコンテンツへスキップ
Code & Craft
Web制作 約10分で読めます

Progressive Enhancement 実践ガイド|堅牢なWebサイトを作る設計哲学

Progressive Enhancementの原則と実装方法を解説。JavaScript なしでも動作するベースから段階的に機能を拡張する、堅牢で保守性の高いWeb開発アプローチ。

Progressive Enhancement(段階的拡張) は、2003年から提唱されている Web 開発の設計哲学です。「まず基本機能を HTML だけで実装し、CSS で見た目を追加し、最後に JavaScript で体験を向上させる」という考え方で、20年以上経った今でも最も堅牢な Web サイトを作る方法として支持されています。

Progressive Enhancement とは

Progressive Enhancement は、以下の3層構造で Web を設計する考え方です。

┌─────────────────────────────────────┐
│     3. JavaScript(振る舞い)         │
│  インタラクティブな拡張機能            │
├─────────────────────────────────────┤
│     2. CSS(プレゼンテーション)       │
│  見た目とレイアウト                  │
├─────────────────────────────────────┤
│     1. HTML(コンテンツ)             │
│  意味のある構造とコンテンツ            │
└─────────────────────────────────────┘

基本原則: 下の層だけでも完全に機能するように設計する。上の層は「あると嬉しい」拡張。

対義語: Graceful Degradation

対照的な考え方として Graceful Degradation(優雅な退化) があります。

  • Graceful Degradation: 最新機能を使って作り、古い環境では機能を減らす
  • Progressive Enhancement: 基本機能から作り、最新環境では機能を追加する

現代の Web では Progressive Enhancement が推奨されます。理由は後述します。

なぜ Progressive Enhancement が重要か

1. ユーザーの多様性

すべてのユーザーが同じ環境ではありません。

  • JavaScript を無効化している(広告ブロッカーや企業ネットワーク)
  • スクリーンリーダーを使用
  • キーボードのみで操作
  • 古いブラウザ
  • ネットワークが不安定

Progressive Enhancement なら、こうした全環境で基本機能が動きます。

2. パフォーマンス

JavaScript に依存しない設計は、初回表示が圧倒的に速いです。Core Web Vitals の LCP 改善にも直結します。

3. SEO

検索エンジンのクローラは JavaScript を実行しますが、HTML だけで完結するコンテンツの方が確実に評価されます

4. 堅牢性

JavaScript のエラーでサイトが真っ白になる … そんな事態を防げます。

5. アクセシビリティ

セマンティック HTML は、スクリーンリーダーに最も優しい形式です。

実践パターン

パターン1: リンクとボタン

Bad: JavaScript 依存のナビゲーション

<div onclick="navigate('/about')">About</div>
  • JavaScript が無効だと動かない
  • キーボード操作不可
  • スクリーンリーダーが認識できない

Good: セマンティックな <a> タグ

<a href="/about">About</a>
  • JavaScript なしで動く
  • キーボード操作可能
  • スクリーンリーダーが認識
  • 右クリック → 新しいタブで開くができる

パターン2: フォーム送信

Bad: Fetch 依存

<div class="form">
  <input id="email" />
  <div onclick="submitForm()">送信</div>
</div>

Good: ネイティブフォーム + JS拡張

<form method="POST" action="/api/submit">
  <label>
    メールアドレス
    <input type="email" name="email" required />
  </label>
  <button type="submit">送信</button>
</form>

<script>
// JavaScript で拡張(Fetch で非同期送信)
document.querySelector('form').addEventListener('submit', async (e) => {
  e.preventDefault();
  const formData = new FormData(e.target);
  await fetch('/api/submit', { method: 'POST', body: formData });
  showSuccess();
});
</script>

JavaScript 有効時は非同期送信、無効時は通常のフォーム送信で動作します。

パターン3: タブコンテンツ

Bad: div ベースの Tab

<div class="tabs">
  <div onclick="showTab(0)">Tab 1</div>
  <div onclick="showTab(1)">Tab 2</div>
</div>
<div id="content-0">内容1</div>
<div id="content-1" style="display:none">内容2</div>

Good: リンクとセクション

<nav class="tabs">
  <a href="#tab1">Tab 1</a>
  <a href="#tab2">Tab 2</a>
</nav>
<section id="tab1">内容1</section>
<section id="tab2">内容2</section>
/* :target でアクティブタブを制御 */
section { display: none; }
section:target { display: block; }
section#tab1 { display: block; } /* デフォルト */
section#tab1:target ~ section { display: none; }
section:target { display: block; }

JavaScript なしでもタブが機能し、URL 共有もできます。

パターン4: モーダル

Bad: div + JS

<button onclick="openModal()">開く</button>
<div id="modal" style="display:none">...</div>

Good: HTML <dialog> または popover

<button popovertarget="modal">開く</button>
<div id="modal" popover>
  <p>モーダル内容</p>
  <button popovertarget="modal" popovertargetaction="hide">閉じる</button>
</div>

HTML popover 属性 を使えば、JavaScript なしでモーダルが実装できます。

パターン5: 画像の遅延読み込み

Bad: Intersection Observer 必須

<img data-src="/photo.jpg" class="lazy" />
<script src="/lazyload.js"></script>

Good: ネイティブ属性

<img src="/photo.jpg" loading="lazy" alt="..." />

loading="lazy" 属性でネイティブに遅延読み込みできます。JavaScript 不要です。

パターン6: フォームバリデーション

Bad: JavaScript のみ

if (!email.value.includes('@')) {
  alert('メールアドレスが不正です');
}

Good: HTML 属性 + JavaScript 拡張

<input
  type="email"
  required
  pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
  title="有効なメールアドレスを入力してください"
/>

HTML5 のバリデーション属性だけで十分な場合が多いです。JavaScript はより高度なバリデーション(非同期チェック等)にだけ使いましょう。

パターン7: 動的コンテンツの読み込み

Bad: クライアントレンダリングのみ

<div id="posts"></div>
<script>
  fetch('/api/posts').then(res => res.json()).then(data => {
    document.getElementById('posts').innerHTML = data.map(...).join('');
  });
</script>

JavaScript 無効時は空のページが表示されます。

Good: サーバーサイドレンダリング + 拡張

<div id="posts">
  <!-- サーバーで初期データをレンダリング -->
  <article>...</article>
  <article>...</article>
</div>

<script>
// JavaScript で追加読み込みを実装
if ('IntersectionObserver' in window) {
  setupInfiniteScroll();
}
</script>

Astro、Next.js、SvelteKit などのフレームワークが標準でサポートしています。

Feature Detection

新しい機能を使う場合は、サポートチェックを行います。

// Web Share API の例
if (navigator.share) {
  // 使える
  await navigator.share({ ... });
} else {
  // フォールバック
  copyToClipboard(url);
}

CSS では @supports を使います。

/* デフォルト */
.card { display: block; }

/* サポートされる場合だけ適用 */
@supports (display: grid) {
  .card { display: grid; }
}

JavaScript 無効環境のテスト

Chrome DevTools での確認

  1. DevTools を開く
  2. Command Palette(Cmd+Shift+P / Ctrl+Shift+P
  3. “Disable JavaScript” と入力して選択
  4. ページをリロード

noscript タグ

<noscript>
  <style>
    .js-only { display: none; }
    .no-js-only { display: block; }
  </style>
  <p class="no-js-only">JavaScript を有効にするとより良い体験が得られます</p>
</noscript>

CSS での Progressive Enhancement

CSS にも同じ考え方を適用できます。

/* 基本スタイル */
.card {
  padding: 1rem;
  background: white;
}

/* モダンブラウザでの拡張 */
@supports (backdrop-filter: blur(10px)) {
  .card {
    backdrop-filter: blur(10px);
    background: rgba(255, 255, 255, 0.8);
  }
}

/* さらにモダンな拡張 */
@supports (color: color-mix(in oklch, red, blue)) {
  .card {
    background: color-mix(in oklch, white 80%, transparent);
  }
}

モダンフレームワークとの相性

「SPA は Progressive Enhancement に反する」という意見もありますが、現代のフレームワークは工夫されています。

Next.js / Astro の SSR + Hydration

  1. サーバーで HTML を生成(HTML のみで動作)
  2. クライアントで JavaScript がハイドレーション(拡張)

→ JavaScript 無効でも基本機能が動く

Remix / SvelteKit の Progressive Forms

// Remix の例
<Form method="post">
  <input name="email" />
  <button>送信</button>
</Form>

Form コンポーネントは、JavaScript が有効なら非同期送信、無効なら通常送信として動作します。

Progressive Enhancement の利点まとめ

項目従来のアプローチProgressive Enhancement
初回表示速度遅い速い
SEO依存強い
アクセシビリティ実装次第標準で良い
エラー耐性低い高い
実装の複雑さ高いシンプル

まとめ

Progressive Enhancement は、古い考え方に見えるかもしれませんが、現代の Web 開発でこそ価値があります

実践のポイント:

  1. セマンティック HTML から始める
  2. CSS でプレゼンテーションを分離
  3. JavaScript は拡張として追加
  4. Feature Detection で段階的にアップグレード
  5. フォーム、リンク、ボタンはネイティブ要素を使う

まずは小さなコンポーネントから、「この機能は JS なしでも動くか?」を問いかけてみてください。堅牢さと保守性が劇的に向上します。

参考リンク

#設計 #HTML #CSS #JavaScript #アクセシビリティ
シェア: