| name | frontend-ui-engineering-avalonia |
| description | Builds production-quality Avalonia UIs — MVVM with CommunityToolkit.Mvvm, compiled bindings (`x:DataType`, `x:CompileBindings`), `DataTemplate`s, theming via FluentTheme/SimpleTheme with `ThemeVariant`, keyboard navigation, `AutomationProperties` for accessibility, adaptive layouts across desktop/mobile/browser targets. Use when building or modifying Avalonia 11/12 user interfaces, implementing views and view-models, or fixing visual or UX issues in an Avalonia app. |
| version | 0.5.0 |
| source | rewritten from vendor/agent-skills/skills/frontend-ui-engineering/SKILL.md@44dac80 |
Frontend UI Engineering — Avalonia
Overview
Build production-quality Avalonia UIs that are accessible, performant, and visually polished. The goal is UI that looks like it was built by a design-aware .NET engineer at a top company — not like it was generated by an AI. That means real design-system adherence, proper keyboard navigation and screen-reader support, compiled XAML bindings for performance and type safety, thoughtful theming that respects light/dark/system variants, and no generic "AI aesthetic."
When to Use
- Building new Avalonia views or windows
- Modifying existing user-facing interfaces
- Implementing adaptive layouts for desktop + mobile + browser targets
- Adding interactivity or state management in view-models
- Fixing visual or UX issues (binding failures, theming bugs, focus traps)
- Migrating between Avalonia versions (11.x ↔ 12.x)
When NOT to use: backend-only changes in MyApp.Core / MyApp.Infrastructure, CLI tools, or code that doesn't render in an Avalonia view.
Related:
context-engineering for setting up CLAUDE.md + .editorconfig for an Avalonia project
integration-testing-dotnet for testing views with Avalonia.Headless.XUnit
performance-optimization-dotnet for virtualized lists, binding overhead, and startup-time tuning
- The companion avalonia-dev plugin in this marketplace (
/avalonia-review) for structural reviews of existing Avalonia solutions — design token extraction, theming, project layout, phased migration
Version Awareness
This skill supports Avalonia 11.x and 12.x. Where a technique differs between versions, the pattern is flagged.
Key differences to keep front-of-mind (see Avalonia's official migration guide for the full list):
- v12 enables compiled bindings (
x:CompileBindings) by default on UserControl / Window; v11 defaults to off — set it explicitly so your bindings are typed and validated at build time.
- v12 replaces
ReactiveObject-first defaults with first-class support for CommunityToolkit.Mvvm's source-generator patterns.
- v12's styling API tightens selector precedence; double-check custom styles after migrating.
- v12 page-based navigation APIs (
Navigation.PushAsync) are first-party; v11 patterns via ReactiveUI.Routing still work but are no longer the canonical choice.
When you detect the project's version (from the Avalonia NuGet reference in Directory.Packages.props / the UI .csproj), state it explicitly and apply version-appropriate patterns.
Side-by-side: the compiled-bindings opt-in
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:MyApp.ViewModels"
x:DataType="vm:TaskListViewModel"
x:CompileBindings="True">
</UserControl>
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:MyApp.ViewModels"
x:DataType="vm:TaskListViewModel">
</UserControl>
The visible XAML diff is one attribute on the root element. The cost of getting it wrong is version-specific: v11 without x:CompileBindings="True" silently degrades to runtime reflection; either version without x:DataType loses the build-time type check. Default posture: declare both on every UserControl / Window regardless of Avalonia version — it's correct in v11 and redundant-but-harmless in v12.
Component Architecture
File Structure
Colocate everything related to a view. Avalonia projects typically separate Views/ and ViewModels/ at the top level; for larger features, colocate by feature:
src/MyApp/
Views/
Tasks/
TaskListView.axaml # View markup
TaskListView.axaml.cs # Code-behind (minimal — one per view)
TaskItemTemplate.axaml # Small reusable DataTemplate if shared
ViewModels/
Tasks/
TaskListViewModel.cs # [ObservableObject] with [ObservableProperty] / [RelayCommand]
TaskItemViewModel.cs
Styles/
Tasks/
TaskList.axaml # Feature-specific styles
Resources/
Tokens/
Colors.axaml # Design-token resource dictionary
Spacing.axaml
Larger apps may prefer feature-folders:
src/MyApp/Features/Tasks/
TaskListView.axaml
TaskListView.axaml.cs
TaskListViewModel.cs
TaskItemViewModel.cs
Styles.axaml
Either layout is fine — pick one per project and stick to it. See the avalonia-dev plugin's project-structure reference for worked examples.
Keep Views and View-Models Focused
Each view does one thing. Each view-model exposes only what its view needs:
public sealed partial class TaskItemViewModel : ObservableObject
{
public TaskItemViewModel(TaskDto task, ITaskService service)
{
_service = service;
Id = task.Id;
Title = task.Title;
IsDone = task.Status == TaskStatus.Completed;
}
private readonly ITaskService _service;
public TaskId Id { get; }
[ObservableProperty] private string _title = string.Empty;
[ObservableProperty] private bool _isDone;
[RelayCommand]
private async Task ToggleAsync(CancellationToken cancellationToken)
{
IsDone = !IsDone;
await _service.SetStatusAsync(Id, IsDone ? TaskStatus.Completed : TaskStatus.Pending, cancellationToken);
}
}
<Border x:DataType="vm:TaskItemViewModel"
x:CompileBindings="True"
Padding="12"
Classes="task-item">
<StackPanel Orientation="Horizontal" Spacing="12">
<CheckBox IsChecked="{Binding IsDone}"
Command="{Binding ToggleCommand}"
AutomationProperties.Name="{Binding Title, StringFormat='Toggle task: {0}'}" />
<TextBlock Text="{Binding Title}"
Classes.done="{Binding IsDone}" />
</StackPanel>
</Border>
Prefer Composition Over Configuration
Build complex views by composing small controls, not by adding more properties:
<Card>
<Card.Header>
<TextBlock Text="Tasks" Classes="h2" />
</Card.Header>
<ListBox ItemsSource="{Binding Tasks}"
ItemTemplate="{StaticResource TaskItemTemplate}" />
</Card>
<Card Title="Tasks"
HeaderVariant="Large"
BodyPadding="Medium"
Items="{Binding Tasks}"
ItemTemplateKey="TaskItemTemplate" />
Separate Loading / Error / Empty from Presentation
Every list or async-loaded surface needs four states: loading, error, empty, populated. Surface them explicitly:
<Panel>
<ProgressBar IsIndeterminate="True"
IsVisible="{Binding IsLoading}"
VerticalAlignment="Center" />
<StackPanel IsVisible="{Binding HasError}" Spacing="8" Margin="24">
<TextBlock Text="Couldn't load tasks." Classes="h3" />
<TextBlock Text="{Binding ErrorMessage}" TextWrapping="Wrap" />
<Button Content="Retry" Command="{Binding LoadCommand}" />
</StackPanel>
<StackPanel IsVisible="{Binding IsEmpty}" Spacing="8" Margin="24" HorizontalAlignment="Center">
<PathIcon Data="{StaticResource EmptyTasksIcon}" Width="48" Height="48" />
<TextBlock Text="No tasks yet" Classes="h3" />
<TextBlock Text="Create one to get started." Classes="body-muted" />
<Button Content="Create task" Command="{Binding NewTaskCommand}" Classes="accent" />
</StackPanel>
<ListBox IsVisible="{Binding HasTasks}"
ItemsSource="{Binding Tasks}"
ItemTemplate="{StaticResource TaskItemTemplate}" />
</Panel>
View-model exposes the four states as discrete flags so the binding expressions stay trivially readable.
Binding Discipline
Compiled Bindings Are Not Optional
Turn on x:CompileBindings="True" (default in v12, opt-in on v11) and always specify x:DataType. Runtime-resolved bindings are the #1 source of "why is my view blank?" bugs — the failure is logged to the debug output and nowhere else.
<UserControl xmlns:vm="using:MyApp.ViewModels.Tasks"
x:DataType="vm:TaskListViewModel"
x:CompileBindings="True">
</UserControl>
For DataTemplates that bind to a different type, scope the data type explicitly:
<DataTemplate x:DataType="vm:TaskItemViewModel">
<Grid ColumnDefinitions="Auto,*,Auto">
<CheckBox IsChecked="{Binding IsDone}" />
<TextBlock Grid.Column="1" Text="{Binding Title}" />
<Button Grid.Column="2" Content="Delete" Command="{Binding DeleteCommand}" />
</Grid>
</DataTemplate>
Binding Diagnostics
When a binding is "wrong but silent," enable binding diagnostics in development:
Logger.Sink = new TraceLogSink(LogEventLevel.Warning, new[] { LogArea.Binding, LogArea.Property });
Any failed or unresolved binding writes to the debug console. Fix all of them before shipping.
State Management
Pick the simplest approach that works. Avalonia view-model state is typically held in the view-model itself; for cross-view state prefer DI-scoped services, not ambient statics:
ObservableObject + ObservableProperty → Single-view state (the common case)
Shared service injected via DI → State shared between view-models (registered as Singleton or Scoped)
Messenger (CommunityToolkit.Mvvm) → Loosely-coupled notifications between view-models (subscribe/publish)
IOptions<T> / IOptionsMonitor<T> → Configuration-style state bound from appsettings.json
ReactiveUI (if the project already uses it) → Reactive chains with observables; v11 legacy, still supported in v12
Avoid INotifyPropertyChanged boilerplate by standardising on CommunityToolkit.Mvvm source generators ([ObservableProperty], [RelayCommand], [NotifyCanExecuteChangedFor]). They produce the plumbing at compile time — no reflection, no runtime cost.
Design-System Adherence
Tokens, Not Raw Values
Bind to resources, not hard-coded values. A design token layer lives in Resources/Tokens/*.axaml and is merged into the application's resource dictionary:
<ResourceDictionary xmlns="https://github.com/avaloniaui">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<Color x:Key="SurfaceColor">#FFFFFF</Color>
<Color x:Key="TextPrimaryColor">#0F172A</Color>
<Color x:Key="AccentColor">#2563EB</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<Color x:Key="SurfaceColor">#0B1220</Color>
<Color x:Key="TextPrimaryColor">#F1F5F9</Color>
<Color x:Key="AccentColor">#60A5FA</Color>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<SolidColorBrush x:Key="SurfaceBrush" Color="{DynamicResource SurfaceColor}" />
<SolidColorBrush x:Key="TextPrimaryBrush" Color="{DynamicResource TextPrimaryColor}" />
<SolidColorBrush x:Key="AccentBrush" Color="{DynamicResource AccentColor}" />
</ResourceDictionary>
Use DynamicResource for anything theme-sensitive (colors, brushes) and StaticResource for things that don't change at runtime (spacing tokens, icon geometry). See the avalonia-dev plugin's design-tokens reference for a full token taxonomy (colors, typography, spacing, elevation).
Spacing
Use a spacing scale. 4 px / 8 px increments are common — SpacingXs=4, SpacingSm=8, SpacingMd=12, SpacingLg=16, SpacingXl=24. Don't invent Padding="13".
Typography
Respect the type hierarchy. One h1 per window / page; never use heading classes for non-heading content:
<TextBlock Text="{Binding Title}" Classes="h1" />
<TextBlock Text="{Binding Subtitle}" Classes="h2" />
<TextBlock Text="{Binding Body}" Classes="body" TextWrapping="Wrap" />
<TextBlock Text="{Binding Caption}" Classes="caption text-muted" />
Define the classes once in Styles/Typography.axaml. Don't set per-element FontSize="14" — that's how type hierarchies decay.
Avoid the AI Aesthetic
AI-generated Avalonia UIs have recognizable patterns. Avoid all of them:
| AI Default | Why It's a Problem | Production Quality |
|---|
| Purple/indigo everywhere | Models default to visually "safe" palettes so every app looks identical | Use the project's actual color palette via resource tokens |
| Maximum corner radius on everything | CornerRadius="16" on every Border flattens hierarchy and signals "friendly"-but-generic | Corner radius from a token scale (e.g. Xs/Sm/Md) that matches control size |
Over-nested StackPanel/Grid | Layouts that are 8 levels deep for a single row | Grid with ColumnDefinitions / DockPanel / StackPanel picked per role, not stacked by habit |
| Lorem-ipsum-style sample data | Placeholder content hides layout problems real content reveals (long titles wrap, numbers push columns) | Realistic fixtures in the design-time DataContext |
| Oversized padding everywhere | Padding="24" on every Border destroys hierarchy and wastes space on small windows | Padding from the spacing token scale, stepped by container role |
| Uniform stock card grids | Grid-of-cards layout regardless of content priority | Purpose-driven layouts — DockPanel for master/detail, Grid for true tabular data, ListBox with a real item template |
| Shadow-heavy design | Layered shadows and "glassmorphism" compete with content and slow rendering on low-end devices | Subtle or no shadows unless the design system specifies them |
Fluent theme with no customization | Works but looks like every other Avalonia starter | Customize the accent color / control templates / corner radii to match your product |
Design-Time Data
Populate a design-time DataContext so XAML previewers show real-looking content — long titles, mixed states, populated lists. Bad design-time data is how the AI aesthetic slips in: if your previewer only ever shows one short title, you won't notice when production overflows.
<Design.DataContext>
<vm:TaskListViewModel>
</vm:TaskListViewModel>
</Design.DataContext>
Theming
Use FluentTheme (or SimpleTheme) as the foundation and layer your overrides on top. Don't build a theme from scratch unless the product requires it.
<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://MyApp/Styles/Typography.axaml" />
<StyleInclude Source="avares://MyApp/Styles/Buttons.axaml" />
</Application.Styles>
<Application.Resources>
<ResourceInclude Source="avares://MyApp/Resources/Tokens/Colors.axaml" />
<ResourceInclude Source="avares://MyApp/Resources/Tokens/Spacing.axaml" />
</Application.Resources>
Respect ThemeVariant.Default — if the user's OS is in dark mode and you've defined Light + Dark dictionaries, Avalonia swaps them automatically.
Application.Current!.RequestedThemeVariant = ThemeVariant.Dark;
Accessibility
Every Avalonia control must meet these expectations. Windows Narrator, macOS VoiceOver, and Linux Orca all read AutomationProperties — set them.
Keyboard Navigation
<Button Content="Save" Command="{Binding SaveCommand}" />
<Border PointerPressed="OnBorderClicked"
Focusable="True" <!-- ✓ Must be set explicitly on non-control panels -->
KeyDown="OnBorderKeyDown">
<TextBlock Text="Click me" />
</Border>
Set TabIndex only when the default focus order is wrong, and set explicit focus when a view is activated:
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
TitleTextBox.Focus();
}
AutomationProperties
<Button Command="{Binding CloseCommand}"
AutomationProperties.Name="Close dialog">
<PathIcon Data="{StaticResource CloseIcon}" />
</Button>
<StackPanel Spacing="4">
<TextBlock x:Name="EmailLabel" Text="Email" />
<TextBox Text="{Binding Email}"
AutomationProperties.LabeledBy="{Binding #EmailLabel}" />
</StackPanel>
<TextBlock Text="{Binding StatusMessage}"
AutomationProperties.LiveSetting="Assertive"
AutomationProperties.Name="Status" />
Focus Management in Dialogs
public partial class ConfirmDialog : Window
{
public ConfirmDialog()
{
InitializeComponent();
Opened += (_, _) => ConfirmButton.Focus();
}
}
For modal dialogs, also trap focus — Avalonia's ShowDialog takes care of it for you when DialogResult is awaited.
Color Contrast
- Normal text vs surface: at least 4.5:1 (WCAG 2.1 AA)
- Large text (18pt+): at least 3:1
- Never use color as the sole signal. Pair red with an icon, an error message, or a pattern; pair "status" colors with text.
Verify with Real Tools
- Windows: Accessibility Insights for Windows — inspects the automation tree, flags missing
Name/Role, checks contrast
- macOS: VoiceOver rotor (
Ctrl+Option+U) against the running app
- Linux: Accerciser + Orca for spot-checks
Adaptive Layouts
Avalonia runs on Windows, macOS, Linux, iOS, Android, and the browser. A single XAML file can serve all of them if you design adaptively.
Use Responsive Units
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,*">
</Grid>
Class-Based Layout Switching
<UserControl Classes.compact="{Binding IsCompactLayout}">
<Panel>
<Grid IsVisible="{Binding !IsCompactLayout}" ColumnDefinitions="280,*">
<ListBox Grid.Column="0" ItemsSource="{Binding Tasks}" />
<ContentControl Grid.Column="1" Content="{Binding SelectedTask}" />
</Grid>
<ContentControl IsVisible="{Binding IsCompactLayout}"
Content="{Binding CurrentPane}" />
</Panel>
</UserControl>
Determine IsCompactLayout via this.GetObservable(Window.BoundsProperty) or a Window-level LayoutUpdated handler.
Mobile Targets
For the mobile head of the app, check OperatingSystem.IsAndroid() / OperatingSystem.IsIOS() in the composition root, and avoid relying on window sizing cues that work on desktop only.
Async UI Patterns
Stay Off the UI Thread for Work
[RelayCommand]
private async Task LoadAsync(CancellationToken cancellationToken)
{
IsLoading = true;
try
{
var tasks = await _service.ListAsync(cancellationToken).ConfigureAwait(true);
Tasks = tasks;
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
ErrorMessage = ex.Message;
}
finally
{
IsLoading = false;
}
}
Never block the dispatcher with .Result or .Wait(). In async code in a UI project, every I/O operation accepts a CancellationToken wired through to the view-model's lifecycle token — tie it to view disposal so navigating away cancels in-flight work.
Marshalling to the UI Thread
When you start background work off the UI thread, marshal back explicitly:
await Dispatcher.UIThread.InvokeAsync(() =>
{
Tasks = updated;
});
Testing
Avalonia UI tests run in-process via Avalonia.Headless.XUnit. See integration-testing-dotnet for the setup and representative tests. At minimum, every critical view should have a test that:
- Creates the view in a headless
Window
- Pumps the dispatcher (
Dispatcher.UIThread.RunJobs())
- Asserts that bindings produced the expected state
- Triggers a command and re-asserts
Performance
See performance-optimization-dotnet for the full treatment. The Avalonia-specific highlights:
- Virtualize long lists with
ListBox + VirtualizingStackPanel (the default). Never swap in a plain StackPanel for a list of >100 items.
- Compiled bindings (
x:CompileBindings="True", x:DataType="...") — faster binding and type-checked at build
- Avoid deep visual trees — use
DockPanel / Grid rather than nested StackPanels when the shape allows
- Prefer
DynamicResource only for genuinely theme-sensitive properties — every DynamicResource subscribes to a resource-change event
- Profile cold-start —
dotnet-trace with the StartupPerformance profile plus Application.Current.Startup instrumentation. An AOT-published build (dotnet publish -r <rid> -p:PublishAot=true on v12) can halve startup on desktop targets
Common Rationalizations
| Rationalization | Reality |
|---|
| "Binding errors don't matter if the view renders" | They mean a property isn't wired up. The view is showing stale or default data, and the debug log is screaming. Turn on diagnostics, fix every one. |
| "Compiled bindings are a premature optimization" | They're a correctness tool, not an optimization. Typos in binding paths become compile errors instead of silent runtime failures. |
| "Accessibility is a nice-to-have" | It's a legal requirement in many jurisdictions and a baseline quality standard. Screen readers find bugs your eyes don't — try narrating your view once. |
| "We'll theme it later" | Retrofitting theming onto a view that has Background="#FFFFFF" everywhere is more work than doing it right. Use DynamicResource from the start for colors and brushes. |
| "This is just an internal tool, design doesn't matter" | Internal tools get used daily by your team — poor design costs real time. Spend an hour on the token scale now; save hours of friction later. |
| "The Fluent theme is enough" | Fluent is a foundation, not a product. Pick an accent color, tune the corner radii, define your type scale — otherwise your app looks like every other Avalonia starter. |
Red Flags
- Views with more than ~300 lines of XAML (split into sub-views or
ContentControls)
- Hard-coded colors, spacings, or font sizes (should be tokens)
x:CompileBindings="False" or missing x:DataType on v11 views
StackPanel for a list of >100 items (use ListBox / VirtualizingStackPanel)
- Missing loading / error / empty states
- No
AutomationProperties.Name on icon-only buttons
.Result / .Wait() inside a RelayCommand body
- Binding warnings in the debug output ignored as "known"
- Color as the sole indicator of state (red/green without text or icons)
- Generic "AI look" — purple accents,
CornerRadius="16" everywhere, oversized padding, uniform card grids
Verification
After building or modifying an Avalonia view:
Source & Modifications
- Upstream: https://github.com/addyosmani/agent-skills/blob/44dac80216da709913fb410f632a65547866346f/skills/frontend-ui-engineering/SKILL.md
- Pinned commit:
44dac80216da709913fb410f632a65547866346f (synced 2026-04-19)
- Status:
rewritten
- Rationale: upstream targets React / TypeScript / Tailwind — every concrete mechanism (JSX, hooks, Tailwind classes, React re-render patterns, the
<picture> element for LCP) is inapplicable to Avalonia XAML. The cross-cutting principles (component focus, composition, design-system adherence, accessibility baseline, loading/error/empty states, avoid the AI aesthetic, responsive design) translate, but the body of the skill is retargeted to Avalonia 11 / 12 with CommunityToolkit.Mvvm, compiled XAML bindings, resource dictionaries, AutomationProperties, and theme-variant awareness.
- What changed (almost everything):
- New skill name (
frontend-ui-engineering-avalonia) — sibling slots can be added for Blazor / ASP.NET Core MVC / MAUI in a later wave
- New "Version Awareness" section calling out Avalonia 11 vs 12 differences (compiled bindings default, styling API, navigation API, CommunityToolkit.Mvvm first-class)
- File-structure example uses
Views/, ViewModels/, Resources/Tokens/, Styles/ — names real Avalonia project conventions
- Component example uses
[ObservableObject] / [ObservableProperty] / [RelayCommand] with a corresponding XAML snippet declaring x:DataType + x:CompileBindings
- "Prefer composition over configuration" example rewritten in XAML
- "Separate loading / error / empty / populated" example rewritten as an Avalonia
Panel with four IsVisible-gated child layouts
- New "Binding Discipline" section — compiled bindings + binding diagnostics — has no upstream equivalent
- State management list retargeted to
ObservableObject, DI-scoped services, Messenger, IOptions<T>, ReactiveUI for v11 legacy
- "Tokens, Not Raw Values" rewritten with
ResourceDictionary.ThemeDictionaries for Light/Dark variants and DynamicResource vs StaticResource guidance
- Spacing + typography + color-as-tokens kept conceptually; mechanisms swapped to XAML style classes and resource brushes
- "Avoid the AI Aesthetic" table re-written with Avalonia-specific smells (
CornerRadius="16", deep StackPanel nesting, unchanged Fluent theme, oversized padding, uniform card grids, shadow-heavy / "glassmorphism" looks, lorem-ipsum in Design.DataContext)
- New "Design-Time Data" section on
Design.DataContext for XAML previewer fidelity
- New "Theming" section (
FluentTheme + overrides + ThemeVariant) — has no upstream analog
- "Accessibility" rewritten for
AutomationProperties.Name/LabeledBy/LiveSetting, Narrator/VoiceOver/Orca, Accessibility Insights for Windows
- "Responsive Design" → "Adaptive Layouts" rewritten for class-based layout switching +
OperatingSystem.IsAndroid()/IsIOS() for mobile targets
- New "Async UI Patterns" section (
RelayCommand body, OperationCanceledException handling, Dispatcher.UIThread.InvokeAsync)
- Testing pointer goes to
integration-testing-dotnet (Avalonia.Headless.XUnit); performance pointer goes to performance-optimization-dotnet
- Common Rationalizations table fully rewritten for Avalonia concerns (binding errors, compiled bindings, Fluent theme defaults, retrofitting theming)
- Red Flags list fully rewritten for Avalonia concerns (long XAML, hard-coded values,
x:CompileBindings="False", non-virtualized lists, missing AutomationProperties, .Result in command bodies)
- Verification checklist rewritten to the 10-item Avalonia checklist (compiled bindings, zero binding warnings, Tab order, automation names, four states, both theme variants, token usage, accessibility-tool pass, Headless test, cold-start)
- Pointer added to the companion avalonia-dev plugin (
/avalonia-review) for structural reviews
- What was preserved conceptually (not verbatim): component-architecture framing, composition-over-configuration principle, loading/error/empty pattern, design-system-adherence framing, avoid-AI-aesthetic framing, four-tier focus on keyboard / ARIA-equivalent / focus management / color, responsive-by-default principle
- Downstream patches (applied after the initial sync; not tracked against upstream):
- 2026-04-19 (plugin v1.0.3) — Version Awareness section now includes side-by-side XAML examples contrasting Avalonia 11 (
x:CompileBindings="True" is opt-in) and Avalonia 12 (compiled bindings default-on, x:DataType still required for type checking). Added a "declare both on every root regardless of version" default posture so copy/paste between v11 and v12 projects is safe.
- License: MIT © 2025 Addy Osmani — see
../../LICENSES/agent-skills-MIT.txt