mcpField
B
best-practices

best-practices

github.com/addyosmaniテスト・品質3.0k29.2k 回インストール9 週間前に更新
リポジトリ

best-practices について

セキュリティ、ブラウザー互換性、コード品質を確認する開発者向けWeb標準スキルです。ページ表示に基づくLighthouseの証拠と、依存関係、ヘッダー、設定、ソースの調査を組み合わせます。

主な機能

  1. 01証拠に基づくLighthouse監査手順
  2. 02セキュリティとヘッダーの確認
  3. 03ブラウザー互換性のガイダンス
  4. 04非推奨APIの確認
  5. 05コード品質とエラー処理の確認

活用シーン

  1. 01デプロイ前のサイトレビュー
  2. 02ブラウザーのコンソールやネットワーク障害の調査
  3. 03セキュリティヘッダーと依存関係の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/addyosmani/web-quality-skills --skill best-practices

インストール後、Claude Code で/best-practicesを入力して読み込まれたか確認します。

同梱ファイルreferences/SECURITY.md

詳細

ベストプラクティス

Lighthouse のベストプラクティス監査に基づく、最新のウェブ開発標準です。セキュリティ、ブラウザー互換性、コード品質のパターンを扱います。

根拠に基づく監査ワークフロー

レンダリング済みのページを利用できる場合:

  1. その機能を利用できる場合は、Lighthouse のベストプラクティス監査をライブで実行します。Chrome DevTools MCP を使用する場合は、lighthouse_audit を使用します。通常のページ読み込みにはナビゲーションモードを、現在の状態を保持する必要がある場合はスナップショットモードを使用します。
  2. 一覧に表示されたコンソールとネットワークの失敗を調べ、調査結果の裏付けになる場合に限って個別の詳細を取得します。
  3. 実行時の根拠を、依存関係、ヘッダー、設定、ソースの調査で補足します。Lighthouse は完全なセキュリティ評価ではありません。
  4. 該当するコードを修正し、同じ監査を再実行します。セキュリティ上の指摘とスタイル上の好みは分けて扱います。

ライブツールを利用できない場合は、Lighthouse CLI と、対象を絞った依存関係およびヘッダーの確認を使用します。Lighthouse のスコアが高いことを、アプリケーションが安全である証拠として決して報告しないでください。

セキュリティ

セキュリティが対象となる場合、またはライブ監査で関連する失敗が見つかった場合は、セキュリティリファレンスを参照してください。HTTPS/HSTS、CSP と Trusted Types、サブリソース完全性、ヘッダー、依存関係、サニタイズ、Cookie を扱います。

最低限、次の点を守ってください。

  • 混在コンテンツを含めずに HTTPS を使用します。 関連するすべてのサブドメインが HTTPS に対応していることを確認してから、HSTS を追加します。
  • 厳格な CSP は多層防御として扱います。 nonce またはハッシュを優先し、強制適用の前にレポートのみのモードでテストします。
  • 信頼できない HTML をサニタイズし、DOM XSS のシンクを保護します。 マークアップが不要な場合は、テキスト API を優先します。
  • サードパーティーコードのバージョンを固定し、レビューします。 配信方式が対応している場合は SRI を使用し、依存関係にパッチを適用した状態に保ちます。
  • 実行時にレスポンスヘッダーを確認します。 ソースの設定だけでは、デプロイ済みのページが実際に送信する内容は証明できません。

ブラウザー互換性

Doctype 宣言

<HTML>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">


<!DOCTYPE html>
<html lang="en">
文字エンコーディング

<html>
<head>
  <title>Page</title>
  <meta charset="UTF-8">
</head>


<html>
<head>
  <meta charset="UTF-8">
  <title>Page</title>
</head>
Viewport メタタグ

<head>
  <title>Page</title>
</head>


<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Page</title>
</head>
機能検出
// ❌ Browser detection (brittle)
if (navigator.userAgent.includes('Chrome')) {
  // Chrome-specific code
}

// ✅ Feature detection
if ('IntersectionObserver' in window) {
  // Use IntersectionObserver
} else {
  // Fallback
}

// ✅ Using @supports in CSS
@supports (display: grid) {
  .container {
    display: grid;
  }
}

@supports not (display: grid) {
  .container {
    display: flex;
  }
}
ポリフィル(必要な場合)

サポート対象ブラウザーの一覧に合わせて、ビルド時にポリフィルをバンドルする(Babel/SWC + core-js、または @vitejs/plugin-legacy)ことを優先してください。これにより実行時のチェックが不要になり、最新のブラウザーにポリフィルのバイト列を送信せずに済みます。

実行時にポリフィルを読み込む必要がある場合は、スクリプト要素を追加してください。document.write は絶対に使用しないでください(パーサーをブロックし、非同期/遅延コンテキストでは動作しません)。

<script>
  if (!('fetch' in window)) {
    const s = document.createElement('script');
    s.src = '/polyfills/fetch.js';
    s.defer = true;
    document.head.appendChild(s);
  }
</script>

自分で管理していないサードパーティー CDN からポリフィルを読み込まないでください。 polyfill.io サービスは、サプライチェーン攻撃により2024 年半ばに侵害され、約 10 万のサイトにマルウェアを配信するために使われました。セルフホストするか、審査済みのミラー(例:Cloudflare の cdnjs ポリフィルビルド)を使用し、サブリソース完全性でバージョンを固定してください。


非推奨 API

これらは避ける
// ❌ document.write (blocks parsing)
document.write('<script src="..."></script>');

// ✅ Dynamic script loading
const script = document.createElement('script');
script.src = '...';
document.head.appendChild(script);

// ❌ Synchronous XHR (blocks main thread)
const xhr = new XMLHttpRequest();
xhr.open('GET', url, false); // false = synchronous

// ✅ Async fetch
const response = await fetch(url);

// ❌ Application Cache (deprecated)
<html manifest="cache.manifest">

// ✅ Service Workers
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}
イベントリスナーの passive オプション
// ❌ Non-passive touch/wheel (may block scrolling)
element.addEventListener('touchstart', handler);
element.addEventListener('wheel', handler);

// ✅ Passive listeners (allows smooth scrolling)
element.addEventListener('touchstart', handler, { passive: true });
element.addEventListener('wheel', handler, { passive: true });

// ✅ If you need preventDefault, be explicit
element.addEventListener('touchstart', handler, { passive: false });

コンソールとエラー

コンソールエラーをなくす
// ❌ Errors in production
console.log('Debug info'); // Remove in production
throw new Error('Unhandled'); // Catch all errors

// ✅ Proper error handling
try {
  riskyOperation();
} catch (error) {
  // Log to error tracking service
  errorTracker.captureException(error);
  // Show user-friendly message
  showErrorMessage('Something went wrong. Please try again.');
}
エラーバウンダリー(React)
class ErrorBoundary extends React.Component {
  state = { hasError: false };
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, info) {
    errorTracker.captureException(error, { extra: info });
  }
  
  render() {
    if (this.state.hasError) {
      return <FallbackUI />;
    }
    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <App />
</ErrorBoundary>
グローバルエラーハンドラー
// Catch unhandled errors
window.addEventListener('error', (event) => {
  errorTracker.captureException(event.error);
});

// Catch unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
  errorTracker.captureException(event.reason);
});

ソースマップ

本番環境の設定
// ❌ Source maps exposed in production
// webpack.config.js
module.exports = {
  devtool: 'source-map', // Exposes source code
};

// ✅ Hidden source maps (uploaded to error tracker)
module.exports = {
  devtool: 'hidden-source-map',
};

// ✅ Or no source maps in production
module.exports = {
  devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',
};

エラートラッカーにアップロードする際は、本番用マップから sourcesContent を削除してください。デフォルトでは、バンドラーが元のソース全体を .map ファイルに埋め込みます。マップを入手した人は(設定を誤ったアップロード手順などを通じて入手した場合も)、圧縮されていないコードを取得できます。バンドラーで sourcesContent を除外するよう設定するか、アップロード時に除外する Sentry/Bugsnag CLI フラグを使用してください。

Vite では、//# sourceMappingURL= コメントがバンドルに出力されないよう、'true' より sourcemap: 'hidden' を優先してください。


パフォーマンスのベストプラクティス

ブロッキングパターンを避ける
// ❌ Blocking script
<script src="heavy-library.js"></script>

// ✅ Deferred script
<script defer src="heavy-library.js"></script>

// ❌ Blocking CSS import
@import url('other-styles.css');

// ✅ Link tags (parallel loading)
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="other-styles.css">
効率的なイベントハンドラー
// ❌ Handler on every element
items.forEach(item => {
  item.addEventListener('click', handleClick);
});

// ✅ Event delegation
container.addEventListener('click', (e) => {
  if (e.target.matches('.item')) {
    handleClick(e);
  }
});
メモリ管理
// ❌ Memory leak (never removed)
const handler = () => { /* ... */ };
window.addEventListener('resize', handler);

// ✅ Cleanup when done
const handler = () => { /* ... */ };
window.addEventListener('resize', handler);

// Later, when component unmounts:
window.removeEventListener('resize', handler);

// ✅ Using AbortController
const controller = new AbortController();
window.addEventListener('resize', handler, { signal: controller.signal });

// Cleanup:
controller.abort();

コード品質

有効な HTML

<div id="header">
<div id="header"> 

<ul>
  <div>Item</div> 
</ul>

<a href="/"><button>Click</button></a> 


<header id="site-header">
</header>

<ul>
  <li>Item</li>
</ul>

<a href="/" class="button">Click</a>
セマンティック HTML

<div class="header">
  <div class="nav">
    <div class="nav-item">Home</div>
  </div>
</div>
<div class="main">
  <div class="article">
    <div class="title">Headline</div>
  </div>
</div>


<header>
  <nav>
    <a href="/">Home</a>
  </nav>
</header>
<main>
  <article>
    <h1>Headline</h1>
  </article>
</main>
画像のアスペクト比

<img src="photo.jpg" width="300" height="100">



<img src="photo.jpg" width="300" height="225">



<img src="photo.jpg" style="width: 300px; height: 200px; object-fit: cover;">

権限とプライバシー

権限を適切にリクエストする
// ❌ Request on page load (bad UX, often denied)
navigator.geolocation.getCurrentPosition(success, error);

// ✅ Request in context, after user action
findNearbyButton.addEventListener('click', async () => {
  // Explain why you need it
  if (await showPermissionExplanation()) {
    navigator.geolocation.getCurrentPosition(success, error);
  }
});
権限ポリシー

<meta http-equiv="Permissions-Policy" 
      content="geolocation=(), camera=(), microphone=()">


<meta http-equiv="Permissions-Policy" 
      content="geolocation=(self 'https://maps.example.com')">

監査チェックリスト

セキュリティ(重要)
  • HTTPS が有効で、混在コンテンツがない
  • 脆弱な依存関係がない(npm audit)
  • CSP ヘッダーが構成されている(frame-ancestors、base-uri、form-action を含む)
  • require-trusted-types-for 'script' が適用されている(または展開中はレポート専用)
  • サードパーティの <script>/<link rel="stylesheet"> が SRI ハッシュで固定されている
  • セキュリティヘッダーが存在する(HSTS、X-Content-Type-Options、Referrer-Policy)
  • ソースマップが公開されていない(アップロードされたものからは sourcesContent を除去済み)
互換性
  • 有効な HTML5 doctype
  • head 内で最初に文字コードを宣言
  • viewport meta タグが存在する
  • 非推奨の API を使用していない
  • scroll/touch に passive イベントリスナーを使用
コード品質
  • コンソールエラーがない
  • 有効な HTML(重複した ID がない)
  • セマンティックな HTML 要素を使用
  • 適切なエラーハンドリング
  • コンポーネント内のメモリクリーンアップ
UX
  • 邪魔なインタースティシャルがない
  • 権限リクエストは文脈に沿って行う
  • 明確なエラーメッセージ
  • 適切な画像のアスペクト比

ツール

ツール目的
npm audit依存関係の脆弱性
SecurityHeaders.comヘッダー分析
W3C ValidatorHTML 検証
ライブ Lighthouse 監査(Chrome DevTools MCP: lighthouse_audit)エージェント向けのレンダリング済みベストプラクティスチェック
Lighthouse CLIベストプラクティス監査のフォールバック
Observatoryセキュリティスキャン

参考資料

コメント