AngelSix.ThemeEngine 1.2.0

dotnet add package AngelSix.ThemeEngine --version 1.2.0
                    
NuGet\Install-Package AngelSix.ThemeEngine -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AngelSix.ThemeEngine" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AngelSix.ThemeEngine" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="AngelSix.ThemeEngine" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AngelSix.ThemeEngine --version 1.2.0
                    
#r "nuget: AngelSix.ThemeEngine, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AngelSix.ThemeEngine@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AngelSix.ThemeEngine&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=AngelSix.ThemeEngine&version=1.2.0
                    
Install as a Cake Tool

AngelSix.ThemeEngine

Strongly-typed Avalonia theming with a Roslyn source generator. Decorate a class with [Theme], install this single NuGet package, and the bundled generator emits one {theme:PropertyName} markup extension per public property — XAML binds theme values directly, with IntelliSense, no runtime code generation.

Install

dotnet add package AngelSix.ThemeEngine

That single line gives you both the runtime library (Theme, ThemeContext, ThemeAttribute) and the source generator (AngelSix.ThemeEngine.SourceGen) — the analyzer is shipped inside this package under analyzers/dotnet/cs/, so NuGet wires it automatically.

Define a theme

Decorate any class with [Theme]. The base class is up to you — ObservableObject if you want per-property change notifications, plain class if you only ever swap themes wholesale.

using AngelSix.ThemeEngine;
using Avalonia;
using Avalonia.Media;

[Theme]
public class DefaultTheme
{
    public string ThemeName => "Default";

    public Color ThemeColor1 => Color.Parse("#FFFFFFFF");
    public SolidColorBrush ThemeColor1Brush => new(ThemeColor1);

    public Thickness ButtonPadding => new(8, 5, 8, 6);
    public CornerRadius ControlCornerRadius => new(3);

    public Color AccentColor => Color.Parse("#0F766E");
    public SolidColorBrush AccentBrush => new(AccentColor);
}

The generator scans your compilation for every [Theme]-attributed class and emits one *Extension : MarkupExtension per public property.

Bootstrap a ThemeContext

Construct a ThemeContext (typically in your DI container) and pass it your theme instance. The constructor registers it as the resolver for all generated markup extensions.

using AngelSix.ThemeEngine;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddSingleton(new ThemeContext(new DefaultTheme()));

Not using a DI container — or need the theme to resolve in the Avalonia designer or a custom-control library? See Use outside dependency injection below.

Use outside dependency injection (design time & custom controls)

The generated {theme:...} extensions resolve through a single static hook, ThemeContext.Resolver. Constructing a ThemeContext sets it — so in a normal app the DI bootstrap above is all you need. But there are cases where no DI container ever runs, and the extensions have nothing to resolve against:

  • Design-time previews. The Avalonia previewer / designer instantiates your theme and XAML directly; it never executes your app's startup, so Resolver is never set and every {theme:...} binding fails (or the preview throws).
  • Custom-control libraries. A control or theme library that ships its own XAML can be loaded ad-hoc — by the previewer, by a consumer that doesn't use DI, or before the host app has bootstrapped its container. The library can't assume someone else has set up a ThemeContext.

For these cases, set up a fallback ThemeContext once, before any {theme:...} extension is evaluated. Guard on Resolver so you only create one when nothing else has — this keeps a real DI-provided context untouched at runtime while still giving the designer (or an undecorated host) a working theme to bind to:

if (ThemeContext.Resolver is null)
    _ = new ThemeContext(new DefaultTheme());

The natural place is the constructor of whatever loads your theme's XAML — for example a Styles-derived theme class, right before AvaloniaXamlLoader.Load:

public class MyTheme : Styles
{
    public MyTheme(IServiceProvider? sp = null)
    {
        // Fallback for design time / non-DI hosts. If a DI bootstrap has already
        // created a ThemeContext, Resolver is set and we leave it alone; otherwise
        // we register the default theme so {theme:...} extensions still resolve.
        if (ThemeContext.Resolver is null)
            _ = new ThemeContext(new DefaultTheme());

        AvaloniaXamlLoader.Load(sp, this);
    }
}

You don't keep a reference to the ThemeContext — its constructor registers itself as Resolver, which is the only thing the extensions need. At runtime your DI bootstrap wins: the ThemeContext constructor it runs unconditionally reassigns Resolver to the DI-provided context, so it overrides this fallback whether it runs before or after. At design time, or in a host with no DI, the fallback is the only thing that ran, so it takes over.

Use in XAML

<ResourceDictionary xmlns="https://github.com/avaloniaui"
                    xmlns:theme="using:AngelSix.ThemeEngine.Generated">

  <ControlTheme x:Key="{x:Type Button}" TargetType="Button">
    <Setter Property="Background" Value="{theme:ThemeColor1Brush}" />
    <Setter Property="Padding"    Value="{theme:ButtonPadding}" />
    <Setter Property="CornerRadius" Value="{theme:ControlCornerRadius}" />
  </ControlTheme>

</ResourceDictionary>

Each extension returns a Binding whose source is ThemeContext.Active, so swapping ThemeContext.Active = new DarkTheme() re-themes every bound value instantly.

Compose paddings & margins — {theme:Edges}

Asymmetric paddings and margins don't need their own tokens. The Edges markup extension (shipped in this package, reached through the same theme namespace) composes a Thickness from whichever spacing tokens you place on each side, so per-control insets reuse your scale instead of hard-coded literals:


<Setter Property="Padding" Value="{theme:Edges Horizontal={theme:SpacingXl}, Vertical={theme:SpacingLg}}" />


<Setter Property="Padding" Value="{theme:Edges Left={theme:SpacingXxl}, Top={theme:SpacingLg}, Right={theme:SpacingLg}, Bottom={theme:SpacingMd}}" />


<Border Margin="{theme:Edges All={theme:SpacingMd}}" />

Each edge accepts any {theme:...} token — they're live bindings, so Edges weaves the four into a MultiBinding and the result stays reactive to theme / BaseSize changes. Precedence per edge: an explicit edge (Left etc.) beats Horizontal/Vertical, which beat All; anything unset is 0.

Verification

The generator emits a marker type AngelSix.ThemeEngine.Generated.__ThemeEngineGenerated. The ThemeContext constructor reflects for it and throws an actionable error if the analyzer didn't run, so wiring problems surface immediately rather than as obscure XAML errors.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 0 6/5/2026
1.1.1 101 5/29/2026
1.1.0 105 5/17/2026
1.0.0 92 5/17/2026