dotnet-accessibility について
Blazor、MAUI、WinUIなどでUIを開発する人向けの.NETアクセシビリティスキルです。共通の実践方法に加え、フレームワーク別APIとテスト方法を扱います。
主な機能
- 01セマンティックマークアップと読み上げメタデータ
- 02キーボード操作とフォーカス管理
- 03WCAGの色コントラスト指針
- 04Blazor、MAUI、WinUI固有のAPI
- 05アクセシビリティテストツールとチェックリスト
活用シーン
- 01.NET UIのキーボード操作とフォーカス挙動の確認
- 02フレームワークのコントロールへの読み上げ対応追加
- 03テストでコントラストとアクセシビリティを検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-accessibilityインストール後、Claude Code で/dotnet-accessibilityを入力して読み込まれたか確認します。
詳細
dotnet-accessibility
.NET UI フレームワーク向けのクロスプラットフォームなアクセシビリティパターン:セマンティックマークアップ、キーボードナビゲーション、フォーカス管理、色のコントラスト、スクリーンリーダーとの連携。Blazor(HTML ARIA)、MAUI(SemanticProperties)、WinUI(AutomationProperties / UI Automation)について詳しく解説します。WPF、Uno Platform、TUI フレームワークについては、相互参照を交えた簡潔なガイダンスを提供します。
対象範囲: このスキルは、横断的なアクセシビリティの原則と、各フレームワークのアクセシビリティ API を扱います。フレームワーク固有の開発パターン(プロジェクトのセットアップ、MVVM、ルーティング、デプロイ)は、それぞれのフレームワークのスキルが扱います。
対象外: フレームワークのプロジェクトセットアップについては、個々のフレームワークのスキルを参照してください。法令遵守に関する助言は対象外です。このスキルでは WCAG 規格を参照しますが、法的なガイダンスは提供しません。UI フレームワークの選定については、[skill:dotnet-ui-chooser] を参照してください。
相互参照:Blazor のホスティングとレンダーモードについては [skill:dotnet-blazor-patterns]、Blazor コンポーネントのライフサイクルについては [skill:dotnet-blazor-components]、MAUI のパターンについては [skill:dotnet-maui-development]、WinUI 3 のパターンについては [skill:dotnet-winui]、.NET 8 以降の WPF については [skill:dotnet-wpf-modern]、Uno Platform のパターンについては [skill:dotnet-uno-platform]、Terminal.Gui については [skill:dotnet-terminal-gui]、Spectre.Console については [skill:dotnet-spectre-console]、フレームワークの選定については [skill:dotnet-ui-chooser] を参照してください。
クロスプラットフォームの原則
これらの原則は、すべての .NET UI フレームワークに適用されます。フレームワーク固有の実装については、以降のセクションで説明します。
セマンティックマークアップ
すべてのインタラクティブ要素と情報要素に、意味のある名前と説明を設定します。スクリーンリーダーは、UI の構造を伝える際に視覚的な外観ではなく、セマンティックメタデータに依存します。
- すべてのインタラクティブコントロールには、アクセシブルな名前(テキストラベル、ARIA ラベル、またはオートメーションプロパティ)が必要です
- 画像やアイコンには、その用途を説明するテキストによる代替情報が必要です
- 装飾要素はアクセシビリティツリーから非表示にします
- 関連するコントロールを論理的にグループ化し、スクリーンリーダーが文脈とともに読み上げるようにします
キーボードナビゲーション
すべての機能をキーボードのみで操作できるようにする必要があります。マウス、ポインター、タッチを使えないユーザーは、キーボード操作に全面的に依存します。
- 視覚的な読み取り順に沿った、論理的なタブ順序を維持します
- すべてのインタラクティブ要素に、視認できるフォーカスインジケーターを表示します
- 標準的なキーボード操作パターンに対応します:ナビゲーションには Tab/Shift+Tab、アクティブ化には Enter/Space、閉じる操作には Escape、複合コントロール内の操作には矢印キーを使用します
- キーボードトラップを避けます。ユーザーがすべてのコントロールから移動できるようにする必要があります
フォーカス管理
プログラムによるフォーカス管理により、スクリーンリーダーがコンテキストの変化を正しく読み上げられるようにします。
- 新たに表示されたコンテンツ(ダイアログ、展開されたパネル、インライン通知)にフォーカスを移します
- オーバーレイを閉じる際は、起動元の要素にフォーカスを戻します
- バックグラウンド更新中に、予期せずフォーカスを奪わないようにします
- ページやダイアログの読み込み時には、主要なアクションに初期フォーカスを設定します
色のコントラスト
テキストとインタラクティブ要素が WCAG のコントラスト比を満たすようにします。
| 要素の種類 | 最低比率(WCAG AA) | 強化比率(WCAG AAA) |
|---|---|---|
| 通常のテキスト(< 18pt) | 4.5:1 | 7:1 |
| 大きなテキスト(>= 18pt または 14pt 太字) | 3:1 | 4.5:1 |
| UI コンポーネントとグラフィックオブジェクト | 3:1 | 3:1 |
- 情報を伝える際に色だけに頼らないでください(補足としてアイコン、パターン、またはテキストラベルを使用します)
- ハイコントラストテーマとシステムカラーの上書きに対応します
- 色覚シミュレーションツールを使ってテストします
Blazor のアクセシビリティ(詳細)
Blazor は HTML をレンダリングするため、標準的な Web アクセシビリティパターンが適用されます。ネイティブ HTML セマンティクスと ARIA 属性を使用して、アクセシブルな Blazor アプリを構築します。
セマンティック HTML と ARIA
@* Use semantic HTML elements for structure *@
<nav aria-label="Main navigation">
<ul>
<li><a href="/products">Products</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<main>
<h1>Product Catalog</h1>
@* Image with alt text *@
<img src="hero.png" alt="Product showcase displaying three featured items" />
@* Decorative image hidden from accessibility tree *@
<img src="divider.svg" alt="" role="presentation" />
@* Button with accessible name from content *@
<button @onclick="AddToCart">Add to Cart</button>
@* Icon button requires aria-label *@
<button @onclick="ToggleFavorite" aria-label="Add to favorites">
<span class="icon-heart" aria-hidden="true"></span>
</button>
</main>
キーボードイベントの処理
<div role="listbox"
tabindex="0"
aria-label="Product list"
aria-activedescendant="@_activeId"
@onkeydown="HandleKeyDown"
@onkeydown:preventDefault>
@foreach (var product in Products)
{
<div id="@($"product-{product.Id}")"
role="option"
aria-selected="@(product.Id == SelectedId)"
@onclick="() => Select(product)">
@product.Name
</div>
}
</div>
@code {
private string _activeId = "";
private void HandleKeyDown(KeyboardEventArgs e)
{
switch (e.Key)
{
case "ArrowDown":
MoveSelection(1);
break;
case "ArrowUp":
MoveSelection(-1);
break;
case "Enter":
case " ":
ConfirmSelection();
break;
}
}
}
ライブリージョン
フォーカスを移動せずに、動的なコンテンツの変更をスクリーンリーダーに通知します。
@* Polite: announced after current speech finishes *@
<div aria-live="polite" aria-atomic="true">
@if (_statusMessage is not null)
{
<p>@_statusMessage</p>
}
</div>
@* Assertive: interrupts current speech (use sparingly) *@
<div aria-live="assertive" role="alert">
@if (_errorMessage is not null)
{
<p>@_errorMessage</p>
}
</div>
フォームのアクセシビリティ
<EditForm Model="@_model" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
<div>
<label for="product-name">Product name</label>
<InputText id="product-name"
@bind-Value="_model.Name"
aria-describedby="name-error"
aria-invalid="@(_nameInvalid ? "true" : null)" />
<ValidationMessage For="() => _model.Name" id="name-error" />
</div>
<div>
<label for="quantity">Quantity</label>
<InputNumber id="quantity"
@bind-Value="_model.Quantity"
aria-describedby="quantity-help"
min="1" max="100" />
<span id="quantity-help">Enter a value between 1 and 100</span>
</div>
<button type="submit">Submit Order</button>
</EditForm>
Blazor のホスティングモデルとレンダーモードの設定については、[skill:dotnet-blazor-patterns] を参照してください。コンポーネントのライフサイクルと EditForm のパターンについては、[skill:dotnet-blazor-components] を参照してください。
MAUI のアクセシビリティ(詳細)
MAUI では、推奨されるアクセシビリティ API として SemanticProperties 添付プロパティが用意されています。これらはネイティブプラットフォームのアクセシビリティ API(iOS/macOS の VoiceOver、Android の TalkBack、Windows の Narrator)に対応しています。
SemanticProperties
<Image Source="product.png"
SemanticProperties.Description="Product photo showing a blue widget" />
<Button Text="Add to Cart"
SemanticProperties.Hint="Adds the current product to your shopping cart" />
<Label Text="Order Summary"
SemanticProperties.HeadingLevel="Level1" />
<Label Text="Items"
SemanticProperties.HeadingLevel="Level2" />
主な API:
SemanticProperties.Description-- スクリーンリーダーが読み上げる短いテキスト(iOS のaccessibilityLabel、Android のcontentDescriptionに相当)SemanticProperties.Hint-- 用途に関する追加のコンテキスト(iOS のaccessibilityHintに相当)SemanticProperties.HeadingLevel-- 見出しを指定します(Level1 から Level9 まで)。Android と iOS がサポートする見出しレベルは 1 つのみですが、Windows は 9 つすべてをサポートします
プラットフォームに関する警告: Label に SemanticProperties.Description を設定しないでください。スクリーンリーダーでは Text プロパティより優先されるため、画面上のテキストと読み上げられるテキストが一致しなくなります。Android の Entry または Editor に SemanticProperties.Description を設定しないでください。Description は TalkBack の操作と競合するため、代わりに Placeholder または SemanticProperties.Hint を使用してください。
従来の AutomationProperties
AutomationProperties は以前の Xamarin.Forms API であり、MAUI では SemanticProperties に置き換えられています。新しいコードでは SemanticProperties を使用してください。
| 従来の API | 置き換え先 |
|---|---|
AutomationProperties.Name | SemanticProperties.Description |
AutomationProperties.HelpText | SemanticProperties.Hint |
AutomationProperties.LabeledBy | SemanticProperties.Description をラベルの Text にバインドします |
AutomationProperties.IsInAccessibleTree と AutomationProperties.ExcludedWithChildren は、アクセシビリティツリーに含めるかどうかを制御する際に引き続き役立ちます。
プログラムによるフォーカスと読み上げ
// Move screen reader focus to a specific element
myLabel.SetSemanticFocus();
// Announce text to the screen reader without moving focus
SemanticScreenReader.Default.Announce("Item added to cart successfully.");
アクセシブルなカスタムコントロール
カスタムコントロールを作成する場合は、アクセシビリティのメタデータが設定されていることを確認してください。
public class RatingControl : ContentView
{
private int _rating;
public int Rating
{
get => _rating;
set
{
_rating = value;
SemanticProperties.SetDescription(this,
$"Rating: {value} out of 5 stars");
SemanticScreenReader.Default.Announce(
$"Rating changed to {value} stars");
}
}
}
MAUI のプロジェクト構成、MVVM パターン、プラットフォームサービスについては、[skill:dotnet-maui-development] を参照してください。
WinUI のアクセシビリティ(詳細)
WinUI 3 / Windows App SDK は、Microsoft UI Automation フレームワークを基盤としています。組み込みコントロールは、既定でオートメーションをサポートします。カスタムコントロールにはオートメーションピアが必要です。
AutomationProperties
<Image Source="ms-appx:///Assets/product.png"
AutomationProperties.Name="Product photo showing a blue widget" />
<Button Content="Add to Cart"
AutomationProperties.HelpText="Adds the current product to your shopping cart" />
<TextBlock x:Name="QuantityLabel" Text="Quantity:" />
<NumberBox AutomationProperties.LabeledBy="{x:Bind QuantityLabel}"
Value="{x:Bind ViewModel.Quantity, Mode=TwoWay}" />
<Image Source="ms-appx:///Assets/divider.png"
AutomationProperties.AccessibilityView="Raw" />
カスタムオートメーションピア
カスタムコントロールでは、AutomationPeer を実装して、UI Automation クライアントにコントロールを公開します。
// Custom control
public sealed class StarRating : Control
{
public int Value
{
get => (int)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(int),
typeof(StarRating), new PropertyMetadata(0, OnValueChanged));
private static void OnValueChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (FrameworkElementAutomationPeer
.FromElement((StarRating)d) is StarRatingAutomationPeer peer)
{
peer.RaiseValueChanged((int)e.OldValue, (int)e.NewValue);
}
}
protected override AutomationPeer OnCreateAutomationPeer()
=> new StarRatingAutomationPeer(this);
}
// Automation peer (using Microsoft.UI.Xaml.Automation.Provider)
public sealed class StarRatingAutomationPeer
: FrameworkElementAutomationPeer, IRangeValueProvider
{
private StarRating Owner => (StarRating)base.Owner;
public StarRatingAutomationPeer(StarRating owner) : base(owner) { }
protected override string GetClassNameCore() => nameof(StarRating);
protected override string GetNameCore()
=> $"Rating: {Owner.Value} out of 5 stars";
protected override AutomationControlType GetAutomationControlTypeCore()
=> AutomationControlType.Slider;
// IRangeValueProvider
public double Value => Owner.Value;
public double Minimum => 0;
public double Maximum => 5;
public double SmallChange => 1;
public double LargeChange => 1;
public bool IsReadOnly => false;
public void SetValue(double value)
=> Owner.Value = (int)Math.Clamp(value, Minimum, Maximum);
public void RaiseValueChanged(int oldValue, int newValue)
{
RaisePropertyChangedEvent(
RangeValuePatternIdentifiers.ValueProperty,
(double)oldValue, (double)newValue);
}
}
WinUI のキーボードアクセシビリティ
WinUI の XAML コントロールには、キーボードサポートが組み込まれています。カスタムコントロールも同じパターンに従うようにしてください。
<TextBox Header="First name" TabIndex="1" />
<TextBox Header="Last name" TabIndex="2" />
<Button Content="Submit" TabIndex="3" />
<Button Content="Save" AccessKey="S" />
<Button Content="Delete" AccessKey="D" />
WinUI のプロジェクト設定、XAML のパターン、Windows との統合については、[skill:dotnet-winui] を参照してください。
WPF のアクセシビリティ(概要)
.NET 8 以降の WPF は、WinUI と同じ UI Automation フレームワークを使用します。名前空間の違いを除けば、API はほぼ同じです。
AutomationProperties.Name、AutomationProperties.HelpText、AutomationProperties.LabeledByは WinUI と同じように機能します- カスタムコントロールでは
OnCreateAutomationPeer()をオーバーライドし、FrameworkElementAutomationPeerのサブクラスを返します - WPF Fluent テーマ(.NET 9 以降)には、ハイコントラストのサポートが自動的に含まれます
- ライブリージョンでの読み上げには
AutomationProperties.LiveSettingを使用します
<Image Source="product.png"
AutomationProperties.Name="Product photo" />
<TextBlock x:Name="StatusLabel"
AutomationProperties.LiveSetting="Polite"
Text="{Binding StatusText}" />
.NET 8 以降の WPF 開発パターンについては、[skill:dotnet-wpf-modern] を参照してください。
Uno Platform のアクセシビリティ(概要)
Uno Platform は API サーフェスが WinUI と互換性があるため、UWP/WinUI の AutomationProperties パターンに準拠しています。
AutomationProperties.Name、AutomationProperties.HelpText、AutomationProperties.LabeledByはクロスプラットフォームで動作します- カスタムの
AutomationPeerの実装は WinUI のパターンに準拠します - WebAssembly では、Uno が
AutomationPropertiesを HTML ARIA 属性に自動的にマッピングします - プラットフォーム固有の動作は異なる場合があります。各ターゲット(Windows、iOS、Android、WASM)でテストしてください
Uno Platform の開発パターンについては、[skill:dotnet-uno-platform] を参照してください。ターゲットごとのデプロイとテストについては、[skill:dotnet-uno-targets] を参照してください。
TUI のアクセシビリティ(概要)
ターミナル UI フレームワークには、アクセシビリティ上の制約が本質的にあります。スクリーンリーダーのサポートは、ターミナルエミュレーターとオペレーティングシステムに依存します。
Terminal.Gui(v2):
- スクリーンリーダーは、ターミナルエミュレーターのアクセシビリティサポートを介してターミナルのテキストコンテンツを読み上げられます
- ARIA や AutomationProperties に相当する、プログラムによるアクセシビリティ API はありません
- 論理的なタブ順序は、ビューの
TabIndexプロパティに従います - ハイコントラストはアプリではなく、ターミナルのカラーテーマで管理します
Spectre.Console:
- 出力専用ライブラリです。スクリーンリーダーはターミナルのテキストバッファーを直接読み上げます
- アクセシビリティが重要な場合は、複雑な視覚要素(テーブル、ツリー)にプレーンテキストの代替表示を使用してください
AnsiConsole.Profile.Capabilitiesではターミナルの機能を検出できますが、スクリーンリーダーの有無は検出できません
正直な制約: TUI アプリからスクリーンリーダーの動作をプログラムで制御することはできません。ターミナルエミュレーターによって、アクセシビリティサポートのレベルは異なります。アクセシビリティが必須要件であるアプリケーションでは、代わりに GUI フレームワーク(Blazor、MAUI、WinUI)を検討してください。
Terminal.Gui のパターンについては、[skill:dotnet-terminal-gui] を参照してください。Spectre.Console のパターンについては、[skill:dotnet-spectre-console] を参照してください。
アクセシビリティテストツール
プラットフォームごとのテスト
| プラットフォーム | 主なツール | その他のツール |
|---|---|---|
| Windows | Accessibility Insights for Windows | ナレーター(Win+Ctrl+Enter)、Inspect.exe(Windows SDK) |
| Web(Blazor) | axe-core/axe DevTools | Lighthouse(Chrome)、WAVE、NVDA、VoiceOver(macOS) |
| Android | Accessibility Scanner | TalkBack、Android Studio Layout Inspector |
| iOS/macOS | Accessibility Inspector(Xcode) | VoiceOver(組み込み)、XCUITest のアクセシビリティアサーション |
自動テストの統合
// Blazor: integrate axe-core with Playwright for automated accessibility testing
// Requires: Deque.AxeCore.Playwright NuGet package
// Install: dotnet add package Deque.AxeCore.Playwright
var axeResults = await new Deque.AxeCore.Playwright.AxeBuilder(page)
.AnalyzeAsync();
// Check for violations
Assert.Empty(axeResults.Violations);
// WinUI/WPF: use Accessibility Insights for Windows CLI in CI pipelines
// Requires: AccessibilityInsights.CLI (available via Microsoft Store or direct download)
手動テストのチェックリスト
- キーボードのみでの操作 -- マウスを使わずにアプリ全体をタブ移動し、すべての機能にアクセスできることを確認します
- スクリーンリーダーでの確認 -- ナレーター/VoiceOver/TalkBack を有効にして、ワークフロー全体を操作します
- ハイコントラスト -- システムのハイコントラストテーマを有効にし、すべてのコンテンツが引き続き見えることを確認します
- ズーム/拡大縮小 -- テキストサイズを 200% に拡大し、レイアウトが崩れたりコンテンツが切れたりしないことを確認します
- 色のコントラスト -- すべてのテキストとインタラクティブ要素が WCAG AA の比率(テキストは 4.5:1、大きなテキストと UI コンポーネントは 3:1)を満たしていることを確認します
WCAG リファレンス
このスキルでは、グローバルなアクセシビリティ標準としてウェブコンテンツ・アクセシビリティ・ガイドライン(WCAG)を参照します。WCAG 2.1 が現在の基準であり、WCAG 2.2 ではモバイルおよび認知アクセシビリティに関する追加基準が設けられています。
4 つの原則(POUR):
- 知覚可能 -- すべてのユーザーが知覚できる方法で情報を提示する必要があります
- 操作可能 -- UI コンポーネントはすべてのユーザーが操作できる必要があります
- 理解可能 -- 情報と UI の操作は理解できる必要があります
- 堅牢 -- 支援技術で動作するのに十分な堅牢性をコンテンツに持たせる必要があります
適合レベル: A(最低限)、AA(ほとんどのアプリで推奨される目標)、AAA(強化)。法的要件や業界標準の大半は、WCAG 2.1 レベル AA を目標としています。
注: このスキルでは、技術的な実装ガイダンスを提供します。法域やアプリケーションの種類によって異なる、アクセシビリティ準拠要件に関する法的助言を行うものではありません。
エージェントが陥りやすい注意点
- MAUI の
LabelコントロールにSemanticProperties.Descriptionを設定しないでください。 スクリーンリーダーではTextプロパティが上書きされ、視覚的なコンテンツと読み上げられる内容が一致しなくなります。LabelはTextプロパティによってすでにアクセシブルです。 - Android の MAUI
Entry/EditorにSemanticProperties.Descriptionを設定しないでください。 代わりにPlaceholderまたはSemanticProperties.Hintを使用してください。これらのコントロールでは、Descriptionが TalkBack の操作と競合します。 - 新しい MAUI コードで
AutomationProperties.NameやAutomationProperties.HelpTextを使用しないでください。 代わりにSemanticProperties(MAUI ネイティブの API)を使用してください。アクセシビリティツリーへの包含を制御するAutomationProperties.IsInAccessibleTreeとExcludedWithChildrenは引き続き有効です。 - アイコンのみの Blazor ボタンで
aria-labelを省略しないでください。 表示テキストのないボタンは、aria-labelまたはaria-labelledbyが設定されていないと、スクリーンリーダーから認識できません。 - 通常のステータス更新に
aria-live="assertive"を使用しないでください。assertiveはスクリーンリーダーの読み上げを即座に中断します。重要度の低い更新にはaria-live="polite"を使用し、assertiveはエラーや緊急性の高いアラートに限定してください。 - TUI アプリがデフォルトでアクセシブルだと思い込まないでください。 ターミナルのスクリーンリーダー対応は、エミュレーターや OS によって大きく異なります。重要なアクセシビリティの場面では、必ず代替の出力形式を用意してください。
- コントラスト比を確認せずに色をハードコードしないでください。 ツール(Accessibility Insights、Lighthouse)を使って WCAG AA への準拠を確認してください。システムのハイコントラストテーマもテストする必要があります。
- 頻繁に使用する WinUI/WPF ボタンに
AccessKeyを設定し忘れないでください。 アクセスキー(Alt+キーのショートカット)は、キーボードに依存するユーザーにとって不可欠であり、簡単に追加できます。
前提条件
- .NET 8.0+(すべてのフレームワークでの基準)
- フレームワーク固有の SDK:MAUI ワークロード、Windows App SDK(WinUI)、Blazor プロジェクトテンプレート
- テストツール:Accessibility Insights(Windows)、axe-core(ウェブ)、Xcode Accessibility Inspector(macOS/iOS)
- 手動テスト用のスクリーンリーダー:Narrator(Windows)、VoiceOver(macOS/iOS)、TalkBack(Android)、NVDA(Windows、無料)