Kuwantima 1.7.0

dotnet add package Kuwantima --version 1.7.0
                    
NuGet\Install-Package Kuwantima -Version 1.7.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="Kuwantima" Version="1.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Kuwantima" Version="1.7.0" />
                    
Directory.Packages.props
<PackageReference Include="Kuwantima" />
                    
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 Kuwantima --version 1.7.0
                    
#r "nuget: Kuwantima, 1.7.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 Kuwantima@1.7.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=Kuwantima&version=1.7.0
                    
Install as a Cake Addin
#tool nuget:?package=Kuwantima&version=1.7.0
                    
Install as a Cake Tool

Kuwantima

A glass-glow design system for Avalonia UI. One StyleInclude, 16 styled controls, automatic light/dark theming.

Features

  • Glass morphism aesthetic with glow borders, frosted panels, and themed shadows
  • Light & Dark themes that swap automatically — MidnightBlue ink on light, AliceBlue frost on dark
  • Fluent-integrated — extends Avalonia's FluentTheme palette so styled and unstyled controls stay harmonious
  • Class-based — opt in per control with Classes="Kuwantima", no global override

Quick Start

Install via NuGet:

dotnet add package Kuwantima

Then in your App.axaml:

<Application.Styles>
    <StyleInclude Source="avares://Kuwantima/Theme/KuwantimaPrimaryTheme.axaml"/>
</Application.Styles>

That's it. Every Fluent control picks up the Kuwantima color palette. To apply full Kuwantima styling to individual controls, add the class:

<Button Classes="Kuwantima" Content="Click me"/>
<CheckBox Classes="Kuwantima" Content="Accept terms"/>
<TextBox Classes="Kuwantima" PlaceholderText="Search..."/>

Controls

Control Class Variants
Button Kuwantima Accent
CheckBox Kuwantima Classic
Color Palette Picker KuwantimaPalette (trigger), KuwantimaSwatch (swatch)
ComboBox Kuwantima :error
Expander Kuwantima
Border (Glass) KuwantimaGlass
GridSplitter Kuwantima Pill, Arrow (+ Horizontal/Vertical)
ListBox Kuwantima
MenuToggleButton KuwantimaMenu
ProgressBar Kuwantima
RadioButton Kuwantima Classic
Slider Kuwantima
TabControl Kuwantima
TextBox Kuwantima ReadOnly, :error
ToggleButton Kuwantima
ToolTip Kuwantima

:error isn't a class you add — unlike every other variant above, it's Avalonia's native :error pseudo-class, which fires automatically from DataValidationErrors/binding validation.

Variant Examples


<CheckBox Classes="Kuwantima Classic" Content="Remember me"/>


<Button Classes="Kuwantima Accent" Content="Save"/>


<GridSplitter Classes="Kuwantima Pill"/>


<GridSplitter Classes="Kuwantima Arrow"/>

Theming

Kuwantima's color story is built on three layers:

Layer Light Dark Role
Cool anchor MidnightBlue #191970 AliceBlue #F0F8FF Tints all Fluent tokens (text, chrome, backgrounds)
Warm accent Orange #FF8C00 Orange #FFA500 Checked/selected borders — contrasts against cool blue
System accent Fluent Blue #0078D4 Fluent Blue #0078D4 Filled accent backgrounds (buttons, selections)

Overriding Brushes

Custom brushes are defined in KuwantimaThemeResources.axaml inside ThemeDictionaries. To override, redefine the key in your own resource dictionary after the StyleInclude:

<Application.Styles>
    <StyleInclude Source="avares://Kuwantima/Theme/KuwantimaPrimaryTheme.axaml"/>
</Application.Styles>

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary x:Key="Dark">
                <SolidColorBrush x:Key="KuwantimaGlassGlowBorder" Color="Purple" Opacity="0.5"/>
            </ResourceDictionary>
        </ResourceDictionary.ThemeDictionaries>
    </ResourceDictionary>
</Application.Resources>

Available Theme Resources

Key Purpose
KuwantimaGlassBackground Glass panel fill
KuwantimaGlassGlowBorder Glass control border at rest
KuwantimaGlassGlowBorderHover Glass control border on hover
KuwantimaGlassGlow Outer glow shadow at rest (BoxShadows)
KuwantimaGlassGlowHover Outer glow shadow on hover (BoxShadows)
KuwantimaControlHoverBrush Background tint on pointer-over
KuwantimaAccentGlowPressed Warm outer glow on accent press (BoxShadows)
KuwantimaAccentOrangeBrush Warm border for checked/selected state
KuwantimaDarkBorderBrush Subtle separator (dark theme only)
KuwantimaValidationErrorBrush Border edge on native :error (TextBox, ComboBox)
KuwantimaSuccessTextBrush Positive-outcome label text
KuwantimaWarningTextBrush Negative-outcome label text
KuwantimaTooltipBackground Tooltip backdrop
KuwantimaTooltipForeground Tooltip text color
KuwantimaSplitterBrush GridSplitter line at rest
KuwantimaSplitterHoverBrush GridSplitter line on hover
KuwantimaScrimBackground Wash behind a blocking overlay (a probe, save, or other long-running operation)
KuwantimaScrimForeground Text/ink on the scrim
KuwantimaPaletteBlue ColorPalettePicker swatch: blue (theme-invariant)
KuwantimaPaletteOrange ColorPalettePicker swatch: orange (theme-invariant)
KuwantimaPalettePurple ColorPalettePicker swatch: purple (theme-invariant)
KuwantimaPaletteGreen ColorPalettePicker swatch: green (theme-invariant)
KuwantimaPaletteRose ColorPalettePicker swatch: rose (theme-invariant)
SystemFillColorSuccessBrush Green status indicator
SystemFillColorAttentionBrush Blue status indicator
SystemFillColorCautionBrush Yellow status indicator

Fluent keys Kuwantima overrides

These are Avalonia Fluent's own keys, re-pointed so text stays readable on Kuwantima's surfaces. Override them yourself only if you also re-check contrast against the backgrounds they land on.

Key Kuwantima value Why
AccentButtonForeground White Ink on accent fills, checkmarks and radio dots. The accent ramp darkens on interaction so one light ink clears WCAG AA on every state (4.53 / 7.32 / 10.50).
SystemControlForegroundBaseMediumBrush #55557F / #C8D4E8 Fluent's value failed AA on the glass panel and on hovered controls.
TextControlPlaceholderForeground #55557F / #C8D4E8 Same tone. A hovered empty TextBox puts placeholder text on the hover tint, which Fluent's value did not survive.

Icons

The same StyleInclude brings in 14 icon geometries. They are ordinary StreamGeometry resources, so any control that takes a Geometry can use one:

<PathIcon Data="{StaticResource Icon.Home}" Width="18" Height="18"/>
Key Key Key
Icon.Home Icon.Search Icon.Refresh
Icon.Gear Icon.Clear.Circle Icon.Copy
Icon.Sliders Icon.Layers Icon.Sun
Icon.Expand Icon.Map Icon.Moon.ThirdEye.Smiling
Icon.Collapse Icon.Add

A key that does not exist renders nothing rather than failing loudly, so check a blank icon against this table before looking anywhere else.

Icon Attribution

Most of these are drawn from existing icon sets rather than original geometry, and retaining credit is a condition of both licenses:

  • Icon.Home, Icon.Gear, Icon.Sliders, Icon.Expand, Icon.Collapse, Icon.Search, Icon.Clear.Circle, Icon.Layers, Icon.Refresh, Icon.Map, Icon.Copy, Icon.Add — from Material Design Icons by Pictogrammers, licensed Apache License 2.0.
  • Icon.Sun — from Fluent System Icons by Microsoft, licensed MIT.
  • Icon.Moon.ThirdEye.Smiling is original.

No changes were made to the geometry beyond what's needed to use it as a StreamGeometry resource.

The sandbox's collapsible sidebar is built from shipped styles — there is no navigation control to install. A ToggleButton with Classes="KuwantimaMenu" is the nav item:

<ToggleButton Classes="KuwantimaMenu"
              Classes.Expanded="{Binding IsPaneOpen}"
              Tag="{StaticResource Icon.Home}"
              Content="Home"
              IsChecked="{Binding IsHomeSelected}"/>

Two properties drive it, and it is worth being precise about which:

  • Tag is the icon. The template binds it to the button's PathIcon, so any StreamGeometry works — one of the keys above, or your own.
  • Content is the label, and it is hidden unless the Expanded class is on. That is what makes the button collapse to an icon-only square when the pane closes.

So do not put your own icon-plus-label StackPanel in Content. The template already places both, and a panel there is invisible while collapsed and double-indented while expanded.

Classes.Expanded is what follows the pane's state, and checked state gets the accent fill and the warm orange border automatically, so the selected page reads at a glance:

<SplitView DisplayMode="CompactInline"
           CompactPaneLength="56"
           OpenPaneLength="220"
           IsPaneOpen="{Binding IsPaneOpen}">
    <SplitView.Pane>
        <StackPanel Spacing="6" Margin="8">
            
        </StackPanel>
    </SplitView.Pane>

    
</SplitView>

Wiring it to pages

The styles do not care how you choose pages, so this part is yours. But the shape below is worth copying, because the obvious alternative fails silently — see the note at the end.

Keep one list of pages, and let it drive the sidebar and the content area both. Each entry carries its label, its icon key, and a factory for the page itself:

using System;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.ComponentModel;

public sealed partial class NavPage : ObservableObject
{
    private readonly Func<Control> _build;
    private Control? _view;

    public NavPage(string title, string iconKey, Func<Control> build)
        => (Title, IconKey, _build) = (title, iconKey, build);

    public string Title { get; }

    /// <summary>Key of a geometry from the table above, e.g. "Icon.Home".</summary>
    public string IconKey { get; }

    /// <summary>Built on first visit, then kept for the lifetime of the app.</summary>
    public Control View => _view ??= _build();

    /// <summary>Drives the nav button's checked state.</summary>
    [ObservableProperty] private bool _isSelected;
}

IsSelected must raise PropertyChanged — hence ObservableObject and [ObservableProperty] above. A plain public bool IsSelected { get; set; } compiles, runs, and leaves the highlight stuck on the first page while the content area changes underneath it, with no exception and no binding error to go looking for.

The view model holds the list and the selection, and keeps IsSelected in step in one place:

using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

public partial class MainViewModel : ObservableObject
{
    public ObservableCollection<NavPage> Pages { get; } = new()
    {
        new NavPage("Home",     "Icon.Home", () => new HomePage()),
        new NavPage("Settings", "Icon.Gear", () => new SettingsPage()),
    };

    [ObservableProperty] private NavPage _selectedPage = null!;
    [ObservableProperty] private bool _isPaneOpen = true;

    public MainViewModel() => SelectedPage = Pages[0];

    partial void OnSelectedPageChanged(NavPage? oldValue, NavPage newValue)
    {
        if (oldValue is not null) oldValue.IsSelected = false;
        newValue.IsSelected = true;
    }

    [RelayCommand]
    private void NavigateTo(NavPage page) => SelectedPage = page;
}

Because the icon is a key rather than a geometry, one small converter turns it into the real resource at bind time. A DataTemplate cannot write {StaticResource {Binding IconKey}} — a resource key has to be known when the markup is parsed — so the lookup happens here, which also keeps Geometry out of your view model:

using System;
using System.Globalization;
using Avalonia;
using Avalonia.Data.Converters;

public class ResourceKeyConverter : IValueConverter
{
    public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
    {
        if (value is not string key || Application.Current is not { } app)
            return null;

        return app.TryGetResource(key, app.ActualThemeVariant, out var resource) ? resource : null;
    }

    public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
        => throw new NotSupportedException($"{nameof(ResourceKeyConverter)} is one-way.");
}

Pass the theme variant explicitly, as above. TryGetResource without one misses theme-scoped resources, and returning null on an unknown key leaves a nav button iconless rather than taking the window down.

The sidebar then becomes one ItemsControl over the list, and the content one ContentControl:

<Window.Resources>
    <conv:ResourceKeyConverter x:Key="ResourceKey"/>
</Window.Resources>

<SplitView.Pane>
    <ItemsControl ItemsSource="{Binding Pages}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Spacing="6" Margin="8"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate x:DataType="vm:NavPage">
                <ToggleButton Classes="KuwantimaMenu"
                              Classes.Expanded="{Binding $parent[Window].((vm:MainViewModel)DataContext).IsPaneOpen}"
                              Tag="{Binding IconKey, Converter={StaticResource ResourceKey}}"
                              Content="{Binding Title}"
                              IsChecked="{Binding IsSelected, Mode=OneWay}"
                              Command="{Binding $parent[Window].((vm:MainViewModel)DataContext).NavigateToCommand}"
                              CommandParameter="{Binding}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</SplitView.Pane>

<ContentControl Content="{Binding SelectedPage.View}"/>

Adding a page is now one line in Pages. The trade-off: navigating away detaches a page from the visual tree, so transient control state such as scroll position resets when you come back. The page object itself is kept, so anything held in your view model persists.

If your app uses a Dependency Injection Container

NavPage takes a factory — Func<Control> — rather than a finished page, which is what makes pages build lazily. That factory is also the seam for a Dependency Injection Container: a library, such as Microsoft.Extensions.DependencyInjection, that constructs your objects for you and supplies whatever those objects need. If you use one, resolve the page there instead of calling new:

new NavPage("Settings", "Icon.Gear", () => provider.GetRequiredService<SettingsPage>()),

Nothing else changes. If you are not using a container, the () => new SettingsPage() above is complete and correct — this is an extension point, not a requirement.

Why one list

The tempting alternative is a nav button per page in the markup, plus an int SelectedPageIndex and one bool IsThisPageVisible property per page. Kuwantima's own sandbox was written that way, and it had two failure modes that produce no exception and no binding error:

  • A per-page notification list that must be kept in step by hand. Miss an entry and the nav button highlights correctly while the page never appears.
  • CommandParameter="3" as a magic number that has to agree with an index in the view model. Off by one and you silently get the wrong page.

With a single list there is nothing left to keep in sync. The one notification that still matters — NavPage.IsSelected — is raised for you by [ObservableProperty] and set in exactly one place, OnSelectedPageChanged. The Kuwantima.Sandbox project is a complete worked example, and the code above is the code it runs.

Color Palette Picker

Button.KuwantimaPalette is a 48×48 square trigger — the same footprint as the KuwantimaMenu nav button, so it sits naturally next to one — that opens a Flyout onto a grid of ToggleButton.KuwantimaSwatch color swatches. It's the first Kuwantima control built on a Flyout rather than a custom Popup.

<Button Classes="KuwantimaPalette">
    <Button.Flyout>
        <Flyout FlyoutPresenterClasses="KuwantimaPalette" Placement="Bottom">
            <Grid ColumnDefinitions="Auto,Auto,Auto" RowDefinitions="Auto,Auto" RowSpacing="8" ColumnSpacing="8">
                <ToggleButton Classes="KuwantimaSwatch" Grid.Row="0" Grid.Column="0" Background="{DynamicResource KuwantimaPaletteBlue}"/>
                <ToggleButton Classes="KuwantimaSwatch" Grid.Row="0" Grid.Column="1" Background="{DynamicResource KuwantimaPaletteOrange}"/>
                <ToggleButton Classes="KuwantimaSwatch" Grid.Row="0" Grid.Column="2" Background="{DynamicResource KuwantimaPalettePurple}"/>
                <ToggleButton Classes="KuwantimaSwatch" Grid.Row="1" Grid.Column="0" Background="{DynamicResource KuwantimaPaletteGreen}"/>
                <ToggleButton Classes="KuwantimaSwatch" Grid.Row="1" Grid.Column="1" Background="{DynamicResource KuwantimaPaletteRose}"/>
                
            </Grid>
        </Flyout>
    </Button.Flyout>
</Button>

Two things worth being precise about, the same way the nav section above is about Tag/Content:

  • FlyoutPresenterClasses="KuwantimaPalette" is what styles the flyout itself — an opaque panel matching ComboBox's own dropdown, not the translucent glass background used elsewhere. A flyout floats over arbitrary content, so translucency there would let whatever's behind it show through the swatches. FlyoutPresenterClasses is the only way to reach the auto-generated presenter with a selector; it never appears in markup you write.
  • A swatch's color is set per instance, on Background — it is not baked into the style. This is what makes the swatch grid open-ended rather than fixed at 5: adding a 6th color is one more ToggleButton line, not a style or layout change.

Overriding the 5 default colors

KuwantimaPaletteBlue/Orange/Purple/Green/Rose are ordinary theme resources, overridable exactly like any other Kuwantima brush — redefine the key after the StyleInclude. They're deliberately identical in both Light and Dark dictionaries (a swatch is a literal paint choice, not UI ink, so it shouldn't shift with the app's theme), so override both if you want to keep that property:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary x:Key="Light">
                <SolidColorBrush x:Key="KuwantimaPaletteBlue" Color="#2563EB"/>
            </ResourceDictionary>
            <ResourceDictionary x:Key="Dark">
                <SolidColorBrush x:Key="KuwantimaPaletteBlue" Color="#2563EB"/>
            </ResourceDictionary>
        </ResourceDictionary.ThemeDictionaries>
    </ResourceDictionary>
</Application.Resources>

If you replace one of these, re-check it the way KuwantimaPaletteOrange and KuwantimaPaletteGreen had to be re-picked in this repo: as a live Fluent accent, not just a fill color — see below.

Adding more colors

No resource involved at all — just add another swatch with whatever brush or literal color you want:

<ToggleButton Classes="KuwantimaSwatch" Grid.Row="1" Grid.Column="2" Background="#009688"/>

Wiring it to actually do something

The styles don't care what happens when a swatch is picked — same as the nav pattern above, that part is yours. Mutual exclusion is the same shape as the nav ToggleButtons: IsChecked="{Binding SelectedColor, Mode=OneWay, Converter={x:Static ObjectConverters.Equal}, ConverterParameter=...}" plus a Command that updates SelectedColor.

Kuwantima.Sandbox goes one step further and uses the selection to re-tint the app's live Fluent accent — every SystemAccentColor*-derived surface in the app, not just the swatch itself — by reaching Application.Current's FluentTheme and setting .Palettes[variant].Accent (see MainWindowViewModel.cs). That's a real worked example, but it's Sandbox application code, not something the package provides: Kuwantima ships zero C#, so "does picking a color change anything" is entirely up to your app, the same way page navigation is. If you replace one of the 5 defaults, or add your own, re-check it against white text as a live accent the way the two colors above had to be — a color that reads fine as a small swatch can still fail contrast once it's driving SystemAccentColorDark1/Dark2 behind white ink.

Sandbox

The Kuwantima.Sandbox project is a live gallery of every control and variant. Run it to preview the full design system:

dotnet run --project Kuwantima.Sandbox

Seven demo pages: Buttons, Inputs, Toggles, Feedback, Containers, Theme Preview (side-by-side light/dark), and Documents (styled README + license dialog).

Requirements

  • .NET 10.0
  • Avalonia 12.0+
  • Avalonia.Themes.Fluent 12.0+

License

MIT License. See LICENSE for details.

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.7.0 46 10/3/2026
1.6.0 89 9/26/2026
1.5.0 87 9/25/2026
1.4.2 89 9/23/2026
1.4.1 82 9/23/2026
1.4.0 87 9/23/2026
1.3.0 85 9/23/2026
1.2.0 136 7/14/2026
1.1.0 138 4/14/2026
1.0.0 128 3/12/2026

v1.7.0 - New: validation error styling for TextBox and ComboBox, plus an Icon.Add glyph.

A new KuwantimaValidationErrorBrush resource styles Avalonia's native :error pseudo-class (the state a binding enters under DataValidationErrors / INotifyDataErrorInfo / ObservableValidator validation) with a border color change on TextBox.Kuwantima and ComboBox.Kuwantima. Fluent supplies no baseline treatment for this state at all - verified by a headless probe before writing any style - so this is a from-scratch addition, not an override of an existing Fluent key.

The new brush reuses KuwantimaWarningTextBrush's existing hex under a new border-role key rather than introducing a new color into the palette - see README's Available Theme Resources table and CLAUDE.md's Color Philosophy section for the full rationale.

Demoed on the Inputs sandbox page's new "Validation" section, wired to the real DataValidationErrors.Errors attached property (not a forced pseudo-class), so it exercises the same mechanism a consuming app's validation will drive.

Also: Icon.Add (a plus-glyph from Material Design Icons, matching the existing MDI set), filling a gap found while building a consuming app's onboarding wizard. 14 icons now ship, up from 13.

Additive and non-breaking: two new resource keys, one new selector per file on two existing controls, nothing existing renamed or removed. Control count stays 16.