agentleFS
linuxdevellinuxdevel/Avalonia-skills/skills/avalonia/avalonia-styling/SKILL.md

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.

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
  1. Distinctive:

    Add/remove in code:

    line 98

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Avalonia Styling

Overview

Three mechanisms for visual styling in Avalonia:

MechanismAnalogous toUse for
StyleCSSApp-specific visual rules, typography, spacing, state
ControlThemeWPF Style + ControlTemplateRestyling entire control appearance (lookless)
ContainerQueryCSS container queriesResponsive 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

SelectorSyntaxMatches
TypeButtonAll Buttons
Class.myClassControls with that style class
Type + ClassButton.primaryButtons with class primary
Name#myNameControl with x:Name="myName"
Direct childStackPanel > ButtonButton as direct child of StackPanel
DescendantStackPanel ButtonAny Button inside StackPanel
Pseudoclass:pointeroverState-based match
Property value[IsChecked=True]Property equals value
Template part/template/ ContentPresenterElement inside ControlTemplate
Nesting^Parent selector (nested styles only)
OrButton, TextBlockEither 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); // toggle

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>
        <!-- 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 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
{
    // 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.WindowState is 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)

  1. Inline property set (e.g., <Button Background="Red"/>)
  2. Animations
  3. Template bindings ({TemplateBinding})
  4. Local style (closest scope — Window or UserControl)
  5. Inherited styles (further up logical tree — App.axaml)

Community Themes & Icon Libraries

Themes

ThemeNuGet / RepoStyle
Material.AvaloniaMaterial.AvaloniaGoogle Material Design theme
Semi.AvaloniaSemi.AvaloniaSemi Design (modern, clean)
SukiUISukiUIFlat design, highly customizable
ShadUIGitHub: accntech/shad-uishadcn/ui inspired, modern
FluentAvaloniaFluentAvaloniaWinUI3 Fluent controls + theme
Ursa.AvaloniaIrihi.UrsaFull UI library with rich controls
AntDesign.AvaloniaGitHub: MicroSugarDeveloperOrg/AntDesign.AvaloniaAnt Design theme
Neumorphism.AvaloniaGitHub: flarive/Neumorphism.AvaloniaNeumorphism soft-UI design
Classic.AvaloniaGitHub: BAndysc/Classic.AvaloniaWindows 9x retro theme
Pipboy.AvaloniaGitHub: NeverMorewd/Pipboy.AvaloniaFallout 4 Pip-Boy inspired theme
Huskui.AvaloniaGitHub: d3ara1n/Huskui.AvaloniaModern, ParkUI/Radix-inspired
Citrus.AvaloniaCitrus.AvaloniaModern styles for built-in controls
WPFDarkThemeGitHub: AngryCarrot789/WPFDarkThemeCompact soft dark theme

Icon Libraries

LibraryNuGetIcons
Icons.AvaloniaProjektanker.Icons.AvaloniaFontAwesome, Material, and more
IconPacks.AvaloniaMahApps.Metro.IconPacks.Avalonia21,000+ icons from multiple packs
Material.Icons.AvaloniaMaterial.Icons.Avalonia6000+ Material Design Icons
HeroIcons.AvaloniaGitHub: russkyc/heroicons-avaloniaHeroicons set
Lucide.AvaloniaLucide.AvaloniaLucide 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 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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.