Goal
AutomationProperties, custom AutomationPeers) so assistive technologies understand your UI.Why this matters
Prerequisites
Key namespaces
AutomationProperties.csAutomationPeer.csControlAutomationPeer.csTextInputMethodClient.csTextInputOptions.csFontManagerOptions.csFlowDirection.cs<StackPanel Spacing="8" KeyboardNavigation.TabNavigation="Cycle">
<TextBlock Text="_User name" RecognizesAccessKey="True"/>
<TextBox x:Name="UserName" TabIndex="0"/>
<TextBlock Text="_Password" RecognizesAccessKey="True"/>
<PasswordBox x:Name="Password" TabIndex="1"/>
<CheckBox TabIndex="2" Content="_Remember me"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button TabIndex="3">
<AccessText Text="_Sign in"/>
</Button>
<Button TabIndex="4">
<AccessText Text="_Cancel"/>
</Button>
</StackPanel>
</StackPanel>
KeyboardNavigation.TabNavigation="Cycle" keeps focus within the container, ideal for dialogs.AccessText or RecognizesAccessKey="True" to expose mnemonic keys.IsTabStop="False" or Focusable="False".KeyboardNavigation (source: KeyboardNavigation.cs) provides:
DirectionalNavigation="Cycle" for arrow-key traversal in menus/panels.TabNavigation modes (Continue, Once, Local, Cycle, None).Control.IsTabStop per element when you need to skip items like labels or icons.Attach AutomationProperties to expose names, help text, and relationships:
<StackPanel Spacing="10">
<TextBlock x:Name="EmailLabel" Text="Email"/>
<TextBox Text="{Binding Email}"
AutomationProperties.LabeledBy="{Binding #EmailLabel}"
AutomationProperties.AutomationId="EmailInput"/>
<TextBlock x:Name="StatusLabel" Text="Status"/>
<TextBlock Text="{Binding Status}"
AutomationProperties.LabeledBy="{Binding #StatusLabel}"
AutomationProperties.LiveSetting="Polite"/>
</StackPanel>
AutomationProperties.Name provides a fallback label when there is no visible text.AutomationProperties.HelpText supplies extra instructions for screen readers.AutomationProperties.LiveSetting (Polite, Assertive) controls how urgent announcements are.AutomationProperties.ControlType lets you override the role in edge cases (use sparingly).AutomationProperties map to automation peers. The base ControlAutomationPeer inspects properties and pseudo-classes to expose state.
Create peers when you author custom controls so assistive technology can identify them correctly.
public class ProgressBadge : TemplatedControl
{
public static readonly StyledProperty<string?> TextProperty =
AvaloniaProperty.Register<ProgressBadge, string?>(nameof(Text));
public string? Text
{
get => GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
protected override AutomationPeer? OnCreateAutomationPeer()
=> new ProgressBadgeAutomationPeer(this);
}
public sealed class ProgressBadgeAutomationPeer : ControlAutomationPeer
{
public ProgressBadgeAutomationPeer(ProgressBadge owner) : base(owner) { }
protected override string? GetNameCore() => (Owner as ProgressBadge)?.Text;
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Text;
protected override AutomationLiveSetting GetLiveSettingCore() => AutomationLiveSetting.Polite;
}
PatternInterfaces (e.g., IRangeValueProvider, IValueProvider) when your control supports specific automation patterns.AutomationProperties.AccessibilityView to control whether a control appears in the content vs. control view.Avalonia supports theme variants (Light, Dark, HighContrast). Bind colors to resources instead of hard-coding values.
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="AccentBrush" Color="#2563EB"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="AccentBrush" Color="#00FF00"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
Switch variants for testing:
Application.Current!.RequestedThemeVariant = ThemeVariant.HighContrast;
Provide clear focus visuals using pseudo-classes (:focus, :pointerover) and ensure contrast ratios meet WCAG (4.5:1 for body text). For Windows, respect system accent colors by reading RequestedThemeVariant and SystemBarColor (Chapter 7).
IME support matters for CJK languages and handwriting. TextInputMethodClient is the bridge between your control and platform IME surfaces. Text controls in Avalonia already implement it; custom text editors should derive from TextInputMethodClient (or reuse TextPresenter).
public sealed class CodeEditorTextInputClient : TextInputMethodClient
{
private readonly CodeEditor _editor;
public CodeEditorTextInputClient(CodeEditor editor) => _editor = editor;
public override Visual TextViewVisual => _editor.TextLayer;
public override bool SupportsPreedit => true;
public override bool SupportsSurroundingText => true;
public override string SurroundingText => _editor.Document.GetText();
public override Rect CursorRectangle => _editor.GetCaretRect();
public override TextSelection Selection
{
get => new(_editor.SelectionStart, _editor.SelectionEnd);
set => _editor.SetSelection(value.Start, value.End);
}
public void UpdateCursor()
{
RaiseCursorRectangleChanged();
RaiseSelectionChanged();
RaiseSurroundingTextChanged();
}
}
Configure text options with the attached TextInputOptions properties:
<TextBox Text="{Binding PhoneNumber}"
InputMethod.TextInputOptions.ContentType="TelephoneNumber"
InputMethod.TextInputOptions.ReturnKeyType="Done"
InputMethod.TextInputOptions.IsCorrectionEnabled="False"/>
ReturnKeyType changes the soft keyboard button (e.g., “Go”, “Send”).ContentType hints at expected input, enabling numeric keyboards or email layouts.IsContentPredictionEnabled/IsSpellCheckEnabled toggle autocorrect.When you detect IME-specific behaviour, test on Windows (IMM32), macOS, Linux (IBus/Fcitx), Android, and iOS — each backend surfaces slightly different capabilities.
Use RESX resources or a localization service that surfaces culture-specific strings.
public sealed class Loc : INotifyPropertyChanged
{
private CultureInfo _culture = CultureInfo.CurrentUICulture;
public string this[string key] => Resources.ResourceManager.GetString(key, _culture) ?? key;
public void SetCulture(CultureInfo culture)
{
if (_culture.Equals(culture))
return;
_culture = culture;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
}
public event PropertyChangedEventHandler? PropertyChanged;
}
Register in App.axaml and bind:
<Application.Resources>
<local:Loc x:Key="Loc"/>
</Application.Resources>
<TextBlock Text="{Binding [Ready], Source={StaticResource Loc}}"/>
Switch culture at runtime:
var culture = new CultureInfo("fr-FR");
CultureInfo.CurrentCulture = CultureInfo.CurrentUICulture = culture;
((Loc)Application.Current!.Resources["Loc"]).SetCulture(culture);
StringFormat or string.Format with the current culture for dates, numbers, and currency.FlowDirection="RightToLeft" for RTL languages and override back to LeftToRight for controls that must remain LTR (e.g., numeric fields).ScaleTransform or LayoutTransform).Ensure glyph coverage with FontManagerOptions:
AppBuilder.Configure<App>()
.UsePlatformDetect()
.With(new FontManagerOptions
{
DefaultFamilyName = "Noto Sans",
FontFallbacks = new[]
{
new FontFallback { Family = "Noto Sans Arabic" },
new FontFallback { Family = "Noto Sans CJK SC" }
}
})
.StartWithClassicDesktopLifetime(args);
FontFamily="avares://MyApp/Assets/Fonts/Brand.ttf#Brand".TextRenderingMode for clarity vs. smoothness.Tips for a repeatable test loop:
Accessibility Insights (Windows), Color Oracle, or browser dev tools to verify contrast ratios.Avalonia.Headless UI tests (Chapter 21) with assertions on AutomationId and localized content.Document gaps (e.g., missing peers, insufficient contrast) and track them like any other defect.
AutomationProperties.Name, HelpText, and AutomationId; inspect the automation tree with DevTools and NVDA.AutomationPeer for a progress pill control, exposing live updates and value patterns, then verify announcements in a screen reader.TextInputOptions for phone number input on Windows, Android, and iOS. Test with an IME (Japanese/Chinese) to ensure composition events render correctly.FlowDirection, and confirm mirrored layouts do not break focus order.FontManagerOptions with script-specific fallbacks and validate that Arabic, Cyrillic, and CJK text render without tofu glyphs.KeyboardNavigation.csAutomationProperties.cs, ControlAutomationPeer.csTextInputMethodClient.cs, TextInputOptions.csCultureInfoExtensions, RuntimePlatformServicesFontManagerOptions.csFlowDirection.csAutomationProperties.LabeledBy and AutomationId improve automated testing and screen reader output?AutomationPeer, and which patterns do you need to expose for value-based controls?TextInputOptions settings influence IME behaviour and soft keyboard layouts across platforms?What's next