Primary APIs:
Style, Styles, Setter, ControlThemeSelectors (Is, OfType, Class, Name, Child, Descendant, Not, NthChild, PropertyEquals)ThemeVariant, ThemeVariantScopeResourceDictionary (ThemeDictionaries, MergedDictionaries, TryGetResource)DynamicResourceExtension, StyleInclude, ResourceIncludeReference docs:
04-styles-themes-resources.md10-templated-controls-and-control-themes.md17-resources-assets-theme-variants-and-xmlns.md28-custom-themes-xaml-and-code-only.md| CSS selector idiom | Avalonia selector idiom |
|---|---|
.card |
Border.card or *.card-style targeting by type/class |
button.primary |
Button.primary |
#header |
#Header via Name selector |
.list > .item |
parent/child composition in selector chain |
.grid .cell |
descendant selector chain |
:hover |
:pointerover |
:focus |
:focus / :focus-visible-style patterns via pseudo-class support |
:disabled |
:disabled |
:nth-child(2n+1) |
NthChild(step: 2, offset: 1) in typed selector APIs |
For complex runtime style construction, prefer typed selectors in C#:
using Avalonia.Controls;
using Avalonia.Styling;
var style = new Style(x => x.OfType<Button>().Class("primary").Not(y => y.Class("danger")));
style.Setters.Add(new Setter(
Avalonia.Controls.Primitives.TemplatedControl.FontWeightProperty,
Avalonia.Media.FontWeight.SemiBold));
HTML/CSS:
.sidebar > .item:hover { background: #1b2638; }
.sidebar .item.active { border-left: 3px solid var(--accent); }
.panel :is(button, a).danger { color: #d13f4a; }
Avalonia:
<Style Selector="StackPanel.sidebar > Border.item:pointerover">
<Setter Property="Background" Value="#1B2638" />
</Style>
<Style Selector="StackPanel.sidebar Border.item.active">
<Setter Property="BorderBrush" Value="{DynamicResource AccentBrush}" />
<Setter Property="BorderThickness" Value="3,0,0,0" />
</Style>
<Style Selector="Border.panel Button.danger">
<Setter Property="Foreground" Value="#D13F4A" />
</Style>
<Style Selector="Border.panel HyperlinkButton.danger">
<Setter Property="Foreground" Value="#D13F4A" />
</Style>
Typed C# selector equivalent for Sidebar > Item (state-specific pseudo-classes are usually clearer in XAML selector strings):
var itemStyle = new Style(x => x
.OfType<StackPanel>().Class("sidebar")
.Child()
.OfType<Border>().Class("item"));
Important differences from browser CSS:
Styles still matters when selectors overlap.SetValue, inline XAML property assignment) usually override style-set values.Practical rule: use explicit classes and small style scopes instead of very deep selector chains.
CSS:
:root {
--accent: #2f6fed;
--surface: #10151e;
}
.card { background: var(--surface); border-color: var(--accent); }
Avalonia:
<Application.Resources>
<Color x:Key="AccentColor">#2F6FED</Color>
<SolidColorBrush x:Key="AccentBrush" Color="{DynamicResource AccentColor}" />
<SolidColorBrush x:Key="SurfaceBrush" Color="#10151E" />
</Application.Resources>
<Style Selector="Border.card">
<Setter Property="Background" Value="{DynamicResource SurfaceBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentBrush}" />
</Style>
Theme-aware token split:
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="SurfaceBrush" Color="#F8FAFD" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="SurfaceBrush" Color="#10151E" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
Map utility/variant classes from CSS frameworks to Avalonia classes:
Button.primaryButton.secondaryButton.destructiveButton:disabledButton:pointerover<Style Selector="Button.primary">
<Setter Property="Background" Value="{DynamicResource AccentBrush}" />
<Setter Property="Foreground" Value="White" />
</Style>
<Style Selector="Button.primary:pointerover">
<Setter Property="Opacity" Value="0.92" />
</Style>
<Style Selector="Button.primary:disabled">
<Setter Property="Opacity" Value="0.45" />
</Style>
Use ControlTheme when variant changes include template structure (not only colors/metrics).
HTML/CSS:
<button class="btn btn-primary">Save</button>
<button class="btn btn-danger">Delete</button>
.btn { padding:.6rem 1rem; border-radius:.6rem; }
.btn-primary { background:#2f6fed; color:white; }
.btn-danger { background:#d13f4a; color:white; }
Avalonia:
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Classes="btn primary" Content="Save" />
<Button Classes="btn danger" Content="Delete" />
</StackPanel>
<Style Selector="Button.btn">
<Setter Property="Padding" Value="14,9" />
<Setter Property="CornerRadius" Value="10" />
</Style>
<Style Selector="Button.btn.primary">
<Setter Property="Background" Value="#2F6FED" />
<Setter Property="Foreground" Value="White" />
</Style>
<Style Selector="Button.btn.danger">
<Setter Property="Background" Value="#D13F4A" />
<Setter Property="Foreground" Value="White" />
</Style>
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
var buttonRow = new StackPanel
{
Orientation = Avalonia.Layout.Orientation.Horizontal,
Spacing = 8,
Children =
{
new Button
{
Content = "Save",
Padding = new Thickness(14, 9),
CornerRadius = new CornerRadius(10),
Background = new SolidColorBrush(Color.Parse("#2F6FED")),
Foreground = Brushes.White
},
new Button
{
Content = "Delete",
Padding = new Thickness(14, 9),
CornerRadius = new CornerRadius(10),
Background = new SolidColorBrush(Color.Parse("#D13F4A")),
Foreground = Brushes.White
}
}
};
DynamicResource, not literal values.ControlTheme instead of only setter-based styles.