| name | avalonia-styling |
| description | Use when applying styles, control themes, pseudoclasses, style selectors, theme variants, or custom fonts in Avalonia. Covers Style vs ControlTheme distinction, selector syntax, nested styles, light/dark theme variants, style sharing via avares://, StyleKeyOverride, and style precedence rules. |
Avalonia Styling
Overview
Three mechanisms for visual styling in Avalonia:
| Mechanism | Analogous to | Use for |
|---|
Style | CSS | App-specific visual rules, typography, spacing, state |
ControlTheme | WPF Style + ControlTemplate | Restyling entire control appearance (lookless) |
ContainerQuery | CSS container queries | Responsive styles based on container size |
Styles cascade up the logical tree. ControlThemes live in Resources, not Styles.
Writing Styles
Selector + Setters. Place in Window.Styles, UserControl.Styles, or App.axaml for global.
<Window.Styles>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="24"/>
<Setter Property="FontWeight" Value="Bold"/>
<Style Selector="^:pointerover">
<Setter Property="Foreground" Value="Red"/>
</Style>
</Style>
</Window.Styles>
Multiple setters, one selector:
<Style Selector="Button.primary">
<Setter Property="Background" Value="#0078D4"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Padding" Value="16,8"/>
</Style>
Style Selector Syntax
| Selector | Syntax | Matches |
|---|
| Type | Button | All Buttons |
| Class | .myClass | Controls with that style class |
| Type + Class | Button.primary | Buttons with class primary |
| Name | #myName | Control with x:Name="myName" |
| Direct child | StackPanel > Button | Button as direct child of StackPanel |
| Descendant | StackPanel Button | Any Button inside StackPanel |
| Pseudoclass | :pointerover | State-based match |
| Property value | [IsChecked=True] | Property equals value |
| Template part | /template/ ContentPresenter | Element inside ControlTemplate |
| Nesting | ^ | Parent selector (nested styles only) |
| Or | Button, TextBlock | Either type |
| Not | :not(.primary) | Negation |
| Is (type check) | :is(Button) | Includes subtypes |
Pseudoclasses
Built-in: :pointerover, :pressed, :disabled, :focus, :focus-within, :focus-visible, :checked, :unchecked, :indeterminate, :selected, :expanded, :collapsed, :empty, :nth-child(n), :nth-last-child(n), :first-child, :last-child
Custom pseudoclass in code-behind or ViewModel:
PseudoClasses.Set(":loading", isLoading);
Then target in XAML:
<Style Selector="local|MyControl:loading">
<Setter Property="Opacity" Value="0.5"/>
</Style>
Style Classes
Declare on control:
<Button Classes="primary large">Click</Button>
Add/remove in code:
myButton.Classes.Add("active");
myButton.Classes.Remove("active");
myButton.Classes.Set("active", condition);
Conditional class binding:
<Button Classes.active="{Binding IsActive}"/>
Multiple classes:
<Button Classes.primary="{Binding IsPrimary}" Classes.large="{Binding IsLarge}"/>
Control Themes
ControlThemes replace the entire visual tree of a control. Must go in Resources, not Styles.
<Application.Resources>
<ControlTheme x:Key="{x:Type Button}" TargetType="Button">
<Setter Property="Background" Value="#E0E0E0"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover /template/ Border">
<Setter Property="Background" Value="#C0C0C0"/>
</Style>
<Style Selector="^:pressed /template/ Border">
<Setter Property="Background" Value="#A0A0A0"/>
</Style>
</ControlTheme>
</Application.Resources>
Extend existing theme with BasedOn:
<ControlTheme x:Key="OutlineButton" TargetType="Button"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Background" Value="Transparent"/>
</ControlTheme>
Apply a named ControlTheme:
<Button Theme="{StaticResource OutlineButton}">Outlined</Button>
Theme Variants (Light / Dark)
Set on Window or Application:
<Window RequestedThemeVariant="Dark">
Application.Current!.RequestedThemeVariant = ThemeVariant.Dark;
Theme-variant resources:
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="SurfaceBrush" Color="#FFFFFF"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="SurfaceBrush" Color="#1E1E1E"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
Consume with DynamicResource (not StaticResource):
<Border Background="{DynamicResource SurfaceBrush}"/>
Built-in Themes
<Application.Styles>
<FluentTheme/>
<SimpleTheme/>
</Application.Styles>
NuGet packages:
Avalonia.Themes.Fluent — Microsoft Fluent design
Avalonia.Themes.Simple — minimal, no external dependencies
Sharing Styles
Include an external .axaml file:
<Window.Styles>
<StyleInclude Source="avares://MyApp/Styles/Buttons.axaml"/>
</Window.Styles>
Or at app level:
<Application.Styles>
<FluentTheme/>
<StyleInclude Source="avares://MyApp/Styles/Global.axaml"/>
</Application.Styles>
Merge ResourceDictionary:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://MyApp/Resources/Colors.axaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
avares:// URI scheme references embedded resources from the assembly. The format is avares://AssemblyName/Path/To/File.axaml.
StyleKeyOverride
Causes a custom control subclass to pick up styles targeting its base type:
public class MyButton : Button
{
protected override Type StyleKeyOverride => typeof(Button);
}
Useful when subclassing a control only for behavior changes, not visual identity.
ContainerQuery (Responsive Styles)
<Style Selector="local|MyPanel">
<Style Selector="^ /template/ TextBlock">
<ContainerQuery MinWidth="400">
<Setter Property="FontSize" Value="18"/>
</ContainerQuery>
<ContainerQuery MaxWidth="399">
<Setter Property="FontSize" Value="12"/>
</ContainerQuery>
</Style>
</Style>
Style Precedence (Highest to Lowest)
- Inline property set (e.g.,
<Button Background="Red"/>)
- Animations
- Template bindings (
{TemplateBinding})
- Local style (closest scope — Window or UserControl)
- Inherited styles (further up logical tree — App.axaml)
Community Themes & Icon Libraries
Themes
| Theme | NuGet / Repo | Style |
|---|
| Material.Avalonia | Material.Avalonia | Google Material Design theme |
| Semi.Avalonia | Semi.Avalonia | Semi Design (modern, clean) |
| SukiUI | SukiUI | Flat design, highly customizable |
| ShadUI | GitHub: accntech/shad-ui | shadcn/ui inspired, modern |
| FluentAvalonia | FluentAvalonia | WinUI3 Fluent controls + theme |
| Ursa.Avalonia | Irihi.Ursa | Full UI library with rich controls |
| AntDesign.Avalonia | GitHub: MicroSugarDeveloperOrg/AntDesign.Avalonia | Ant Design theme |
| Neumorphism.Avalonia | GitHub: flarive/Neumorphism.Avalonia | Neumorphism soft-UI design |
| Classic.Avalonia | GitHub: BAndysc/Classic.Avalonia | Windows 9x retro theme |
| Pipboy.Avalonia | GitHub: NeverMorewd/Pipboy.Avalonia | Fallout 4 Pip-Boy inspired theme |
| Huskui.Avalonia | GitHub: d3ara1n/Huskui.Avalonia | Modern, ParkUI/Radix-inspired |
| Citrus.Avalonia | Citrus.Avalonia | Modern styles for built-in controls |
| WPFDarkTheme | GitHub: AngryCarrot789/WPFDarkTheme | Compact soft dark theme |
Icon Libraries
| Library | NuGet | Icons |
|---|
| Icons.Avalonia | Projektanker.Icons.Avalonia | FontAwesome, Material, and more |
| IconPacks.Avalonia | MahApps.Metro.IconPacks.Avalonia | 21,000+ icons from multiple packs |
| Material.Icons.Avalonia | Material.Icons.Avalonia | 6000+ Material Design Icons |
| HeroIcons.Avalonia | GitHub: russkyc/heroicons-avalonia | Heroicons set |
| Lucide.Avalonia | Lucide.Avalonia | Lucide icon library |
CSS-like Styling
- Nlnet.Avalonia.Css — write Avalonia styles in CSS syntax (
Nlnet.Avalonia.Css)
Common Mistakes
- Using WPF
Triggers — does not exist in Avalonia. Use pseudoclasses + nested styles instead.
- Forgetting
^ in nested selectors — <Style Selector=":pointerover"> inside another Style does not scope to the parent; must be <Style Selector="^:pointerover">.
- Putting ControlTheme in
Window.Styles — ControlThemes must go in Window.Resources or Application.Resources, not Styles.
- Using
StaticResource for theme-variant resources — theme variant resources must be referenced with DynamicResource to react to variant changes at runtime.
- Confusing Styles and ControlThemes — Styles add/override individual property values; ControlThemes replace the entire visual template. If you only need color or font changes, use a Style.
/template/ selector outside ControlTheme — the /template/ segment only works when targeting elements inside a ControlTemplate, typically from within a ControlTheme state style.