avalonia-styling
Skill50 starsChanged 8 days agoMITView on GitHub
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.
In this repository23 other files it gives its agents.
- avalonia-app-developmentSkill
- avalonia-controlsSkill
- avalonia-custom-controlsSkill
- avalonia-data-bindingSkill
- avalonia-data-templatesSkill
- avalonia-deploymentSkill
- avalonia-eventsSkill
- avalonia-graphics-animationSkill
- avalonia-input-interactionSkill
- avalonia-interop-renderingSkill
- avalonia-layoutSkill
- avalonia-mobileSkill
- avalonia-mvvmSkill
- avalonia-performanceSkill
- avalonia-pro-maxSkill
- avalonia-property-systemSkill
- avalonia-servicesSkill
- avalonia-testingSkill
- avalonia-threadingSkill
- avalonia-versionsSkill
- avalonia-wpf-migrationSkill
- avalonia-xamlSkill
- avaloniaSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 98
Add/remove in code:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
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"/>
<!-- Nested: ^ refers to parent selector (TextBlock.h1) -->
<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:
// In a custom control
PseudoClasses.Set(":loading", isLoading);The extension class was renamed PseudolassesExtensions → PseudoClassesExtensions (12.0+) (typo fix; only matters for explicit static calls, e.g. PseudoClassesExtensions.Set(classes, ":x", true)).
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); // toggleConditional 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>
<!-- State styles inside ControlTheme -->
<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
<!-- App.axaml -->
<Application.Styles>
<FluentTheme/>
<!-- or -->
<SimpleTheme/>
</Application.Styles>NuGet packages:
Avalonia.Themes.Fluent— Microsoft Fluent designAvalonia.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
{
// This control will be styled as Button, not MyButton
protected override Type StyleKeyOverride => typeof(Button);
}Useful when subclassing a control only for behavior changes, not visual identity.
IStyleable (with its explicit StyleKey implementation) was removed in 12 — use StyleKeyOverride / StyledElement:
// (11.x only, removed in 12.0) class MyButton : Button, IStyleable { Type IStyleable.StyleKey => typeof(Button); }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>Limitations in 12
- (12.0+)
Window.WindowStateis a direct property, not styleable —<Setter Property="WindowState" .../>no longer works. Set it in code/XAML attribute or bind it. - (12.0+) Style-triggered animations stop on hidden controls; add
Animation.PlaybackBehavior="Always"to keep running (see avalonia-graphics-animation). - Theme variants (
RequestedThemeVariant,ThemeDictionaries,ThemeVariant),ControlTheme,BasedOn,ContainerQuery: unchanged in 11.3/12.x.
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">. - Styling
Window.WindowState(12.0+) — direct property, not settable from a Style; set directly. - Putting ControlTheme in
Window.Styles— ControlThemes must go inWindow.ResourcesorApplication.Resources, notStyles. - Using
StaticResourcefor theme-variant resources — theme variant resources must be referenced withDynamicResourceto 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.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

