mcpField
U
user-personalization

user-personalization

github.com/mgiffordデザイン・UI57138 回インストール6 週間前に更新
リポジトリ

user-personalization について

パーソナライズ設定を実装・評価するチーム向けのアクセシビリティスキルです。システム設定を尊重し、カスタマイズと WCAG 適合の作業を区別する指針を示します。

主な機能

  1. 01システムとブラウザーの設定を尊重
  2. 02アクセシブルな設定コントロールの設計
  3. 03設定値の安全な検証と保存
  4. 04意味を保ったモーション削減
  5. 05テーマとプリセットのアクセシビリティ評価

活用シーン

  1. 01テーマや文字サイズ設定の評価
  2. 02システム設定に従うモーション設定の実装
  3. 03設定の保存とリセット動作の確認

インストール

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

$npx skills add https://github.com/mgifford/accessibility-skills --skill user-personalization

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

同梱ファイルSYNC.md

詳細

ユーザー設定とアクセシビリティのスキル

正式な参照元:mgifford/ACCESSIBILITY.md の examples/USER_PERSONALIZATION_ACCESSIBILITY_BEST_PRACTICES.md このスキルは、そのファイルに基づいています。判断に迷った場合は、例を正式な参照元としてください。

ユーザー設定コントロールを実装する場合や、既存のパーソナライズ機能をレビューする場合は、以下のルールを適用してください。 プロジェクトにパーソナライズ機能がある場合に限り、このスキルを読み込んでください。


中核となる原則

パーソナライズは、アクセシブルな基盤を強化するものであり、問題を修復するためのレイヤーでも、WCAG 準拠の代替でもありません。まず OS、ブラウザー、AT の設定を尊重してください。サイト独自のコントロールを追加するのは、既存のツールではユーザーが確実に得られない、意味のある選択肢を提供する場合に限ります。設定エディターの有無にかかわらず、ページはブラウザーのズーム、テキストサイズの変更、テキスト間隔の上書き、強制カラーモード、キーボード操作に対応しなければなりません。

設定の優先順位モデル:(1)アクセシブルなデフォルトから開始する。(2)サイト設定が「システム」/「デフォルト」の場合は、認識されたシステム/ブラウザーの設定に従う。(3)明示的に選択されたサイト設定で上書きするのは、対応する表示上の要素に限る。(4)「デフォルトにリセット」するとサイトによる上書きが解除され、システム設定に再び従うようにする。メディアクエリに一致したことを、何らかの状態の診断として解釈しないでください。ダークモード、モーションの軽減、高コントラストを選ぶ理由は人それぞれです。


重大度の尺度(このスキル)

レベル意味
Criticalオーバーレイを準拠の代替として使用している。ユーザーの AT を妨げている
SeriousOS レベルの設定(モーションの軽減、カラースキーム)が尊重されていない
Moderateパーソナライズウィジェットがキーボードで操作できない。状態が通知されない
Minor設定が保存されない。prefers-reduced-data が考慮されていない

重大:アクセシビリティオーバーレイを準拠の代替として使用しない

問題を自動修正すると謳うサードパーティ製の「アクセシビリティオーバーレイ」ウィジェットを、準拠の代替として使用してはなりません。オーバーレイを準拠の根拠として用いることは重大な問題です。根本的な構造上の問題(ラベルの欠落、不適切な ARIA、タブ順序)を修正できず、ユーザー自身の AT(スクリーンリーダー、ブラウザーのズーム、OS のハイコントラスト)を積極的に妨げ、誤った準拠の安心感を与えるうえ、障害者コミュニティから広く拒絶されています(https://overlayfactsheet.com/ を参照)。

設定エディターはオーバーレイとは異なります。設定エディターでは、ユーザーがサイト側で意図的に設計・テストした表示方法から選択できます。パーソナライズによって、アクセシブルでないサイトを準拠していると主張したり、実行時に意味情報を汎用的に書き換えたり、AT やユーザースタイルを妨げたり、未解決の不具合を隠したり、ソーステンプレートの修正に取って代わったりしてはなりません。ロックされたレガシーシステムに対する実行時の修正は、文書化・テストされた一時的なリスク軽減策にはなり得ますが、準拠を意味するものではなく、根本的な修正作業を止める理由にもなりません。


必須要件:WCAG の要件と拡張機能を分ける

WCAG が通常求めているのは、ユーザー設定にコンテンツを適応させることです。一般に、サイトがブラウザーの設定をカスタムパネルで再現することまでは求めていません。

要件作成者が実際に行うべきこと
1.4.4 テキストのサイズ変更(AA)ユーザーエージェントによるサイズ変更を妨げず、200%でもコンテンツや操作部品を使える状態に保ちます。サイトの文字サイズ調整機能は一つの手法であり、すべてのサイトに必須ではありません。
1.4.12 テキスト間隔(AA)コンテンツや機能を損なわずに、行間、段落間隔、文字間隔、単語間隔をユーザーが上書きできるようにします。サイトが間隔調整機能を提供する必要はありません。
1.4.10 リフロー(AA)要件で定められた狭い幅でも、コンテンツや機能を維持します。
コントラスト/非テキストのコントラスト作成者が提供するテーマやプリセットは、それぞれ独立して該当する達成基準を満たす必要があります。
モーションの低減モーションが障壁となる可能性がある場合は prefers-reduced-motion を尊重します。サイトの設定機能で、追加の上書きを提供できます。
読書モード、フォントの選択、表示密度調査で必要性が実証された場合に有用な拡張機能ですが、自動的に WCAG の要件となるわけではありません。

設定パネルを追加しただけで、アクセシブルでないコンテンツが適合するようになると主張してはいけません。


重大:ユーザー設定のメディア機能を尊重する(段階的な拡張)

サポートされていないクエリは無視されるため、それらがなくてもデフォルトの表示がアクセシブルである必要があります。

カラースキーム
:root {
  color-scheme: light dark;
  --page-background: #ffffff;
  --text-color: #1f2937;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --page-background: #111827;
    --text-color: #f3f4f6;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --page-background: #111827;
  --text-color: #f3f4f6;
}

data-theme がない場合は「システム」を意味し、選択を明示すると属性が設定されます。テーマセレクターの完全なパターンについては、skills/light-dark-mode/SKILL.md を参照してください。

モーションの低減

すべてのアニメーションやトランジションを 0.01ms !important に強制する一律のルールは適用しないでください。 このパターンは、transitionend/animationend の発火に依存する状態変更やスクリプトのイベント処理を壊す可能性があります。代わりに、重要でない特定のモーションを削除するか、別の表現に置き換えてください。

.panel { transition: translate 200ms ease-out, opacity 200ms ease-out; }

@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion]) .panel { transition: none; }
  :root:not([data-motion]) { scroll-behavior: auto; }
}
:root[data-motion="reduce"] .panel { transition: none; }

モーションが重要な状態を伝える場合は、即時の状態変更に置き換えてください。情報を取り除いてはいけません。

コントラスト設定

prefers-contrast は more、less、custom を表すことがあります。すべての一致をハイコントラストの要求として扱わないでください。

@media (prefers-contrast: more) {
  :root { --border-width: 0.1875rem; --muted-text-color: currentColor; }
}
@media (prefers-contrast: less) {
  .decorative-texture { background-image: none; }
}
@media (prefers-contrast) {
  .decorative-gradient, .decorative-shadow { background-image: none; box-shadow: none; }
}

修飾子のない prefers-contrast クエリは、一般的に視覚的な複雑さを軽減する場合には適切です。しかし、特定のハイコントラストパレットを強制する用途には適していません。ユーザーがコントラストを下げることやカスタムパレットを要求している可能性があるためです。

透明度の低減と強制カラー
@media (prefers-reduced-transparency: reduce) {
  .dialog-backdrop { background: Canvas; opacity: 1; }
}
@media (forced-colors: active) {
  .button {
    color: ButtonText;
    background: ButtonFace;
    border: 0.125rem solid ButtonText;
  }
  .button:focus-visible { outline: 0.1875rem solid Highlight; }
}

デフォルトの forced-color-adjust: auto を維持してください。forced-color-adjust: none は、独自の対応をテスト済みの、対象を限定した要素にのみ使用してください。これは一般的なフォーカスインジケーターの修正方法ではありません。Media Queries Level 5 は引き続き W3C の作業草案です。新しい機能は進化中のものとして扱い、テスト済みのデフォルトに置き換わるものと見なさないでください。


中程度:アクセシブルな設定エディターを使う

選択肢が少ない場合は、インラインの <details> による開示を優先してください。これにより、モーダルのフォーカス管理が不要になります。

<details class="display-preferences">
  <summary>Display preferences</summary>
  <form id="display-preferences-form">
    <fieldset>
      <legend>Color theme</legend>
      <label><input type="radio" name="theme" value="system" checked> System</label>
      <label><input type="radio" name="theme" value="light"> Light</label>
      <label><input type="radio" name="theme" value="dark"> Dark</label>
    </fieldset>
    <label for="text-size">Text size</label>
    <select id="text-size" name="textSize">
      <option value="default">Default</option>
      <option value="large">Large</option>
    </select>
    <button type="button" id="reset-display-preferences">Reset to defaults</button>
    <p id="display-preferences-status" role="status"></p>
  </form>
</details>

ネイティブのラジオボタンとセレクトボックスは選択中の値をすでに公開するため、テストで必要性が確認されない限り、視覚的な変更のたびにライブリージョンで通知を追加しないでください。保存時、リセット時、未保存時に簡潔なステータスメッセージを表示すれば十分です。設定への入口には、「アクセシビリティ」ではなく、「表示設定」や「読書設定」のように目的を示すラベルを付けてください。一つのアイコンであらゆる設定を伝えることはできないため、アイコンがあってもテキストラベルを表示してください。

モーダルが本当に必要な場合は、アクセシブルな名前、初期フォーカス、モーダル内に収まる Tab の移動順序、Escape キーによる閉じる操作、視認できる閉じるボタン、起動元へのフォーカスの復帰を含む完全なパターンを実装してください。


中程度:生の保存文字列ではなく CSS 変数で表示を適用する

:root { --content-measure: 80ch; --reading-line-height: 1.5; }
:root[data-text-size="large"] { font-size: 125%; }
:root[data-reading-layout="comfortable"] { --content-measure: 70ch; --reading-line-height: 1.7; }
.reading-content { max-inline-size: var(--content-measure); }

ブラウザーのズームやユーザーのフォント設定が引き続き機能するよう、相対サイズを使ってください。ルート要素のパーセンテージ指定は、ユーザーのデフォルト値を置き換えるのではなく、その値を基準に拡大縮小します。フォントサイズ、テキストサイズ、言語、間隔が変わったときに問題となる固定高さのテキストコンテナーや、テキストを切り取る指定は避けてください。

重大:値を安全に検証、適用、保存する

プライベートブラウジング、クロスオリジンの iframe、ストレージが制限された環境では localStorage を利用できません。アクセスは必ず try/catch で囲んでください。 適用する前に、保存された値をすべて許可リストと照合して検証してください。保存された文字列を CSS やクラス名に直接挿入しないでください。

const preferenceKey = "site.displayPreferences.v1";
const defaults = Object.freeze({ theme: "system", textSize: "default", motion: "system" });
const allowedValues = Object.freeze({
  theme: ["system", "light", "dark"],
  textSize: ["default", "large", "x-large"],
  motion: ["system", "reduce"]
});

function sanitizePreferences(candidate = {}) {
  const source = candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : {};
  return Object.fromEntries(
    Object.entries(defaults).map(([name, defaultValue]) => {
      const value = source[name];
      return [name, allowedValues[name].includes(value) ? value : defaultValue];
    })
  );
}

function readPreferences() {
  try {
    const stored = localStorage.getItem(preferenceKey);
    return stored ? sanitizePreferences(JSON.parse(stored)) : { ...defaults };
  } catch {
    return { ...defaults };
  }
}

function setOptionalAttribute(name, value, defaultValue) {
  if (value === defaultValue) document.documentElement.removeAttribute(name);
  else document.documentElement.setAttribute(name, value);
}

function applyPreferences(preferences) {
  setOptionalAttribute("data-theme", preferences.theme, "system");
  setOptionalAttribute("data-text-size", preferences.textSize, "default");
  setOptionalAttribute("data-motion", preferences.motion, "system");
}

ストレージキーをバージョン管理し(.v1)、スキーマが変更されたときは、古いデータを意図的に移行するか破棄してください。廃止された値を解釈しようとしないでください。保存については正確に説明してください。アカウント同期で実際に実現されるのでなければ、「どこでも引き継げる」ではなく「このデバイスに保存」と伝えてください。ストレージへのアクセス拒否やデータ破損には安全に対処し、必ず機能するリセット手段を用意してください。


重大:プライバシープロファイルを作らずに設定を保存する

選択した表示を再現するために必要な値のみを保存し、推測した診断名や障害情報は決して保存しないでください。利用可能だからといって設定値を分析用データとして送信しないでください。公開プロフィールに設定を公開しないでください。アカウント同期は意図的かつ透明性をもって行い、同期された設定をアカウントデータとして保護してください。プロジェクトのプライバシー手順に従って、データの保持期間や Cookie の使用について文書化してください。ユーザー設定に応じて変化するメディア機能は、ブラウザーのフィンガープリントに利用される可能性があります。可能な場合は、JavaScript を通じて値を読み取ったり送信したりするのではなく、CSS による対応を優先してください。


中程度:意味を損なわずに動きや気を散らす要素を減らす

動きの低減は、視覚的な変化をすべてなくすことと同じではありません。ビューポート内を移動させる代わりに即時に状態を変化させること、透明度のみの変化に問題がなければクロスフェードを使うこと、装飾的なアニメーションを一時停止すること、カルーセルに手動操作を用意すること、スムーズスクロールを使わないこと、コントロールの位置を安定させることを優先してください。動き続ける、点滅する、スクロールする、または自動更新されるコンテンツが WCAG 2.2.2 の条件に該当する場合は、一時停止/停止/非表示の操作、または更新頻度を制御する手段を用意してください。これらの操作を、同じ動きの多いコンポーネントの中に隠さないでください。サイト全体の「動きを減らす」設定は prefers-reduced-motion を補完できます。「システム」は、OS の設定が変わったときにも引き続き反映される必要があります。


中程度:フォントの選択と読みやすさの調整

ディスレクシアやその他の障害に対して、あるフォントが普遍的に最適だと示す根拠はありません。障害を理由に、どのフォントが必要かをユーザーに伝えないでください。ユーザーが実際に見た結果をもとに選べるようにしてください。フォントを選択できるようにする場合は、「サンセリフ/セリフ/等幅」のように中立的に説明してください。あらゆる言語や文字を十分にカバーし、堅牢なフォールバックを用意してください。ユーザーエージェントの最小フォントサイズ設定を維持し、「デフォルト」と「リセット」を用意してください。

読書向けのレイアウトでは、行の長さを制限したり、間隔を広げたり、装飾的な背景をなくしたり、情報密度を下げたり、主要コンテンツと補足コンテンツの順序を入れ替えたりできます。ただし、説明、警告、エラーメッセージ、タスクの必須情報を、ユーザーに知らせずに削除してはいけません。設定によって任意の情報を非表示にする場合は、何が非表示になったかをユーザーに伝え、すぐに元に戻せる方法を用意してください。W3C COGA のガイダンスと WAI-Adapt は補足リソースであり、WCAG 2.2 の適合要件に追加されるものではありません。


あらゆるテーマとプリセットで、それぞれアクセシビリティを確保する

作成者が用意するテーマはすべて、テキストのコントラスト、テキスト以外のコントラスト、フォーカスの視認性と遮蔽の有無、訪問済みリンクの区別、選択済み/チェック済み/展開済み/現在/エラーの各状態、グラフやアイコンの判読性、強制カラー表示での動作、印刷時の出力について、それぞれ独立して要件を満たす必要があります。定義とテストを完了したテーマでない限り、サイトのプリセットを「高コントラスト」と呼ばないでください。また、OS の強制カラーモードと同等であるかのように示さないでください。多くの場合、「より高いコントラスト」のほうが正確なラベルです。


プログレッシブエンハンスメントと障害への対応

拡張機能が適用されていないページでも、読みやすく操作可能な状態を保ってください。サイト内に設定コントロールがある場合でも CSS メディアクエリを使用し、カスタムウィジェットよりネイティブ HTML コントロールを優先してください。基本的なアクセシビリティのためにサードパーティスクリプトを必須にしないでください。リモートの設定サービスによって描画をブロックしないでください。ストレージが失敗した場合もシステム設定を維持し、部分的な障害の後もリセットや復旧手段を利用できる状態にしてください。


Minor: prefers-reduced-data

従量制接続で軽量なコンテンツを配信するための、新たに登場したメディアクエリです。 現在のサポートは限定的なため、@supports または機能検出で条件を設け、 現時点での要件ではなく、今後に向けたものとして扱ってください。

@media (prefers-reduced-data: reduce) {
  .decorative-hero-image { display: none; }
  video[autoplay] { display: none; }
}

テスト

  • デフォルト/失敗状態: JS の実行前と JS を無効にした状態でテストします。ローカルストレージをブロックし、現在のページでは設定が引き続き適用されることを確認します。不正な形式または古い保存済みの値を挿入し、無視されることを確認します。初回訪問、再訪問、リセット、サインアウトをテストします。ストレージ/ネットワークの障害によってコンテンツがブロックされないことを確認します
  • システム設定: ライト/ダークの配色と、ページを開いたまま配色を変更する場合をテストします(システムモードでは変更に追従する必要があります)。読み込み前と読み込み後のモーション低減をテストします。prefers-contrast の値(more/less/custom)をテストします。少なくとも1種類のライトと1種類のダークの強制カラー配色をテストします
  • サイトのコントロール: キーボード、タッチ、ポインター、音声入力ですべての設定を操作します。スクリーンリーダーでラベル/グループ/値/フォーカス/ステータスを確認します。設定を適用してもフォーカスが移動したり、予期せずページ遷移したりしないことを確認します。リセットによって表示とコントロールがデフォルトに戻ることを確認します
  • コンテンツ/レイアウト: テキストを200%に拡大します。400%ズームと、必要な狭い幅でテストします。Text Spacing の4つの上書き値をすべて適用します。コントロール、テーブル、図、メッセージを引き続き利用できることを確認します
  • 組み合わせ: まず個々の設定をテストし、その後、リスクの高い組み合わせ(特大テキスト+ゆったりとしたレイアウト、ダークテーマ+モーション低減、明示的なテーマ+強制カラー、パネルを開いた状態での400%ズーム、スキーマ更新後の保存済みの設定)をテストします。すべての組み合わせを網羅的にテストするのではなく、ペアワイズテスト/リスクベースのカバレッジを使用してください

自動チェックでは、コントラスト、ラベル、状態、ストレージの動作、ターゲットサイズの一部を検証できますが、設定が理解しやすいか、組み合わせによって認知的過負荷が生じるか、注入された修正が AT と競合するかは判断できません。ユーザビリティテストには、関連するパーソナライズ機能/AT を利用する人を含めてください。


よくある失敗

失敗修正
設定ウィジェットによってサイトが WCAG に適合すると主張する元のソースを修正し、ウィジェットは任意のパーソナライズ機能としてのみ説明します
WCAG 1.4.12 が間隔調整コントロールを必須としていると説明するユーザーによる上書きがコンテンツ/機能を失うことなく機能するかテストします。サイトがコントロールを提供する必要はありません
テキストサイズボタンをブラウザーのズームの代わりとして扱うズームを有効にしたまま、テキストの拡大にも個別に対応します
すべてのアニメーション/トランジションを 0.01ms !important に強制する状態の変化を損なわず、不要なモーションだけを削除するか置き換えます
prefers-contrast は常にハイコントラストを意味すると想定するmore、less、custom を正確に処理します
カスタムのフォーカスインジケーターに forced-color-adjust: none を適用するデフォルトで強制カラーを適用し、必要に応じてシステムカラーを使用します
サイトの「ハイコントラスト」テーマを強制カラーモードとして扱う作成者によるテーマとしてテストし、その効果を正確に表示します
すべてのページに A+/A-、フォント、色、音声、ナビゲーションのコントロールを追加する調査に基づき、保守およびテストが可能な設定のみを追加します
設定アイコンだけをラベルとして使う「表示設定」のような可視テキストを使用します
保存済みの値をクラスや CSS に直接挿入する適用する前に、小さな許可リストに照らして検証します
ローカルストレージが永続的で、どこでも利用できると想定する拒否/消去に対応し、デバイス/ブラウザーの適用範囲を正確に説明します
設定値から障害を推測したり、分析データを充実させたりする表示状態の保存は最小限にとどめます
読書モードで警告や手順を黙って削除する必須コンテンツを保持し、非表示にした任意の内容があれば明示します
個々のオプションだけをテストするリスクの高い組み合わせ、リセット、移行、失敗状態をテストします

完了条件チェックリスト

  • コンプライアンスの代替としてサードパーティ製のアクセシビリティオーバーレイを使用していない
  • 基本的な操作体験が設定エディターなしでもアクセシブルである
  • ブラウザーのズーム、テキストサイズの変更、ユーザースタイル、拡張機能、支援技術(AT)が妨げられていない
  • CSS メディアクエリーを実装している:prefers-reduced-motion(一律に 0.01ms を適用せず、対象を限定する)、prefers-color-scheme、prefers-contrast(強・弱・カスタム)、forced-colors
  • ウィジェットのラベルは「アクセシビリティ」ではなく、「設定」または「表示設定」としている
  • 明示的なサイト設定は対応する設定のみを上書きし、「システム/デフォルト」は OS の設定に従う
  • localStorage へのアクセスを try/catch で囲み、保存値を許可リストと照合して検証している
  • 読み込み時に設定を安全に復元し、ストレージキーにバージョンを付与している
  • リセット時にサイトの上書き設定を消去し、コントロールを更新する
  • パーソナライズ用コントロールが、ネイティブのセマンティクスを備え、キーボードで操作できる
  • 設定データを最小限にとどめ、障害の推測には決して使用しない
  • すべてのテーマ/プリセットが、個別にコントラストと状態に関する要件を満たしている
  • 個々の設定とリスクの高い組み合わせを手動でテストしている

主要な WCAG 基準

  • 1.4.3 コントラスト(最低限)(AA)
  • 1.4.4 テキストのサイズ変更(AA)
  • 1.4.10 リフロー(AA)
  • 1.4.11 非テキストのコントラスト(AA)
  • 1.4.12 テキストの間隔(AA)— このスキルが対象とする主要な基準
  • 1.3.6 目的の識別(AAA)— パーソナライズと認知アクセシビリティを結び付ける
  • 2.2.2 一時停止、停止、非表示(A)
  • 2.3.3 インタラクションによるアニメーション(AAA)
  • 2.4.11 フォーカスの非遮蔽(最低限)(AA)

参考資料

標準の見通し: WCAG 3.0 は策定中です。パーソナライズに関する要件は縮小ではなく、拡大する見込みです。 https://www.w3.org/TR/wcag-3.0/ を確認してください。

コメント