AKCSS is Akbura's typed styling language. It complements Avalonia styles with C# expressions, reactive dependencies, typed property binding, and component-local style files.
Your first reactive style​
Create a companion style file next to the component. A component named Counter.akbura automatically discovers Counter.akcss in the same directory.
Counter.akcss:
.style {
@if(Background == Brushes.Red) {
Padding: 15;
}
Padding: 10;
}
Counter.akbura:
using Avalonia.Controls;
using Avalonia.Media;
state IBrush bg = Brushes.Aqua;
<Button class="style"
Background={bg}
Click={() => bg = bg == Brushes.Aqua ? Brushes.Red : Brushes.Aqua}>
Current bg is {bg == Brushes.Aqua ? "Aqua" : "Red"}
</Button>
Clicking the button changes bg, which updates the button's Background. The .style rule observes the Background expression and runs its cascade again:
- with an aqua background,
Paddingis10; - with a red background, the declaration inside
@ifsetsPaddingto15; - changing back to aqua removes the conditional value and exposes the
Padding: 10fallback again.
Conditional priority​
AKCSS maps declarations to Avalonia binding priorities:
| Declaration | Avalonia priority |
|---|---|
| A normal style declaration | BindingPriority.Style |
A declaration inside an active @if |
BindingPriority.StyleTrigger |
StyleTrigger has a higher priority than Style. This is why the conditional Padding: 15 wins even though Padding: 10 appears later in the file. When the condition becomes false, AKCSS clears the trigger value and the normal style value becomes visible again.
How reactive dependencies are observed​
AKCSS collects property references from every bound expression in a style, including conditions and property values. For each dependency it uses the best observation mechanism available:
- Avalonia property - subscribes directly to the registered
AvaloniaPropertyobservable. INotifyPropertyChangedobject - subscribes toPropertyChangedon the object that owns the referenced property.- Non-observable property - applies the value, but does not create an automatic subscription for that dependency.
When any observed dependency changes, AKCSS resets the values written by the style and evaluates the ordered cascade again.
Expressions are reactive too​
Observation is not limited to @if. Any property reference inside an expression can become a dependency:
.sameSize {
Width: Height;
}
Here Height is observed. Whenever the target's height changes, .sameSize is evaluated again and writes the new value to Width.
The same rule applies to larger expressions:
.card {
Width: Height * 2;
@if(IsEnabled && Opacity > 0.5) {
Padding: 20;
}
}
Each resolvable property reference is tracked independently.
Selectors​
AKCSS supports untyped, typed, type-only, nested-type, and globally qualified selectors:
.selector { }
Button.selector { }
(MyNamespace.Button) { }
(MyNamespace.Button.Nested).selector { }
(global::MyNs.Button) { }
(global::MyNs.Button).selector { }
| Form | Meaning |
|---|---|
.selector |
A named style that can contribute compatible properties to its target. |
Button.selector |
A named style restricted to Button and compatible derived controls. |
(MyNamespace.Button) |
A type-only selector. |
(MyNamespace.Button.Nested).selector |
A named style for a nested target type. |
(global::MyNs.Button) |
A type-only selector resolved from the global namespace. |
(global::MyNs.Button).selector |
A globally qualified typed class selector. |
Common Avalonia control names such as Button can be used directly. Parenthesized selectors are useful for fully qualified, generic, or nested C# type names.
Using namespaces​
A standalone .akcss file uses @using for C# namespaces:
@using MyApplication.Controls;
@using MyApplication.Models;
(DashboardCard).highlighted {
Opacity: 1;
}
AKCSS also provides these implicit namespaces:
Avalonia
Avalonia.Layout
Avalonia.Media
Akbura
In addition, short Avalonia control names are resolved from Avalonia.Controls, so selectors such as Button.primary do not require a fully qualified name.
Use global:: when a local namespace or type name would otherwise be ambiguous.
Loading and importing styles​
Companion styles​
AKCSS first searches the component's local styles. For Counter.akbura, the companion file is Counter.akcss in the same directory:
Pages/
Counter.akbura
Counter.akcss
No import is required for the companion file. Inline @akcss blocks are local to the component as well.
Importing an AKCSS module​
Import shared styles in an .akbura component with a normal using whose logical name ends in .akcss:
Counter.akbura:
using Akbura.Styles.akcss;
<Button class="myclass">Hello</Button>
The compiler searches local styles before imported modules. Imported modules are considered in using order.
One AKCSS file can import another with @using:
@using Akbura.Styles.akcss;
An import name is the module's logical name, normally derived from the project's root namespace and file path.
AKCSS and Avalonia styles together​
AKCSS is not a replacement for Avalonia's standard styling system. Both can be used on the same control:
<Border Classes="avalonia-card selected" class="akcssCard" />
Classescontains standard Avalonia style classes.classcontains AKCSS styles resolved by the Akbura compiler.
Use Avalonia styles for the existing Avalonia styling ecosystem and AKCSS where typed properties, C# expressions, reactive rules, or component-local styling are useful.