A collection of UI elements for XAF Blazor applications.
A collection of UI elements for XAF Blazor applications.
A dark, command-deck look built on the DevExpress Fluent design system

appsettings.jsonInstall-Package 'Llamachant.ExpressApp.Module.Blazor.UI'
Startup.cs
services.AddXaf(Configuration, builder => {
builder.UseApplication<ExpressAppBlazorApplication>();
builder.Modules
.AddLlamachantFrameworkBlazorUIModule(options => {
options.Themes.Add(LlamachantUIThemes.SciFi); // register the SciFi theme (accent picker on by default)
});
});
All theme options live on LlamachantFrameworkBlazorUIModuleOptions (the delegate above).
| Option | Type | Default | Purpose |
|---|---|---|---|
Themes |
LlamachantBlazorUIThemeCollection |
empty | Themes to register, one switcher group per theme: o.Themes.Add(LlamachantUIThemes.SciFi, t => {...}). Each theme is configured independently. |
EnableRecordHeader |
bool |
false |
See RecordHeader. |
Per-theme options (the t => delegate of Themes.Add):
| Option | Type | Default | Purpose |
|---|---|---|---|
GroupCaption |
string |
theme default | The theme-switcher group caption |
Presets |
IList<LlamachantBlazorUIThemePreset> |
shipped variants | The variants in the group |
DefaultTheme |
string |
null |
Startup variant |
AllowThemeColorPicker |
bool |
true |
Injects this theme's runtime accent script (llamachant-accent.js) — the custom-colour picker, the colour-driven presets' ?accent= derivation, the body.lct-sf / lct-sf-<variant> classes, and the data-lct-sf-suppress hook. Set false to manage the script yourself (also disables this theme's colour-driven presets) |
Themes.Add(theme, configure) gives you a preset list pre-populated with that theme's shipped presets. Mutate the list to rename, recolour, reorder, remove, or add entries — and add more themes later by calling Add again:
.AddLlamachantFrameworkBlazorUIModule(options => {
options.Themes.Add(LlamachantUIThemes.SciFi, t => {
t.GroupCaption = "Llamachant Themes"; // theme-switcher group caption
t.DefaultTheme = "Aqua"; // startup variant (optional)
t.Presets[0].Caption = "Ocean"; // rename Aqua
t.Presets[0].Color = "#00bcd4"; // recolour its swatch
t.Presets.RemoveAt(3); // drop Green
t.Presets.Add(new LlamachantBlazorUIThemePreset("Brand", "#ff6a00")); // colour-driven
t.Presets.Add(new LlamachantBlazorUIThemePreset("Steely", "#6a90af",
"llamachant-sf.variant-steel.min.css")); // pinned to a baked variant file
});
});
Preset kinds (LlamachantBlazorUIThemePreset):
new("Brand", "#ff6a00"): the hex drives the whole theme at runtime via the accent engine (no per-colour CSS file). Requires the theme's AllowThemeColorPicker = true (the default).new("Steely", "#6a90af", "llamachant-sf.variant-steel.min.css"): pins a specific shipped stylesheet. Shipped variants: llamachant-sf.variant-{aqua|blue|steel|green|custom}.min.css.Call the service-collection extension directly (e.g. when you only want a theme's presets):
services.AddLlamachantFrameworkBlazorUITheme(LlamachantUIThemes.SciFi, t => {
t.GroupCaption = "Llamachant SF";
t.DefaultTheme = "Blue";
});
This merges with any groups already defined in appsettings.json's ThemeSwitcher section (it appends a group; it does not replace existing ones).
When a theme's AllowThemeColorPicker is true (the default), a "Custom Color" picker is injected into the theme-switcher flyout under that theme's group. Picking a colour tints whichever theme is active and stores it in the lct-sf-accent cookie (survives reloads). Selecting any theme swatch clears the tint. Tag an app stylesheet meant for other themes with data-lct-sf-suppress and it is disabled while a Llamachant theme is active.
A dark, command-deck look built on the DevExpress Fluent design system

appsettings.jsonInstall-Package 'Llamachant.ExpressApp.Module.Blazor.UI'
Startup.cs
services.AddXaf(Configuration, builder => {
builder.UseApplication<ExpressAppBlazorApplication>();
builder.Modules
.AddLlamachantFrameworkBlazorUIModule(options => {
options.Themes.Add(LlamachantUIThemes.SciFi); // register the SciFi theme (accent picker on by default)
});
});
All theme options live on LlamachantFrameworkBlazorUIModuleOptions (the delegate above).
| Option | Type | Default | Purpose |
|---|---|---|---|
Themes |
LlamachantBlazorUIThemeCollection |
empty | Themes to register, one switcher group per theme: o.Themes.Add(LlamachantUIThemes.SciFi, t => {...}). Each theme is configured independently. |
EnableRecordHeader |
bool |
false |
See RecordHeader. |
Per-theme options (the t => delegate of Themes.Add):
| Option | Type | Default | Purpose |
|---|---|---|---|
GroupCaption |
string |
theme default | The theme-switcher group caption |
Presets |
IList<LlamachantBlazorUIThemePreset> |
shipped variants | The variants in the group |
DefaultTheme |
string |
null |
Startup variant |
AllowThemeColorPicker |
bool |
true |
Injects this theme's runtime accent script (llamachant-accent.js) — the custom-colour picker, the colour-driven presets' ?accent= derivation, the body.lct-sf / lct-sf-<variant> classes, and the data-lct-sf-suppress hook. Set false to manage the script yourself (also disables this theme's colour-driven presets) |
Themes.Add(theme, configure) gives you a preset list pre-populated with that theme's shipped presets. Mutate the list to rename, recolour, reorder, remove, or add entries — and add more themes later by calling Add again:
.AddLlamachantFrameworkBlazorUIModule(options => {
options.Themes.Add(LlamachantUIThemes.SciFi, t => {
t.GroupCaption = "Llamachant Themes"; // theme-switcher group caption
t.DefaultTheme = "Aqua"; // startup variant (optional)
t.Presets[0].Caption = "Ocean"; // rename Aqua
t.Presets[0].Color = "#00bcd4"; // recolour its swatch
t.Presets.RemoveAt(3); // drop Green
t.Presets.Add(new LlamachantBlazorUIThemePreset("Brand", "#ff6a00")); // colour-driven
t.Presets.Add(new LlamachantBlazorUIThemePreset("Steely", "#6a90af",
"llamachant-sf.variant-steel.min.css")); // pinned to a baked variant file
});
});
Preset kinds (LlamachantBlazorUIThemePreset):
new("Brand", "#ff6a00"): the hex drives the whole theme at runtime via the accent engine (no per-colour CSS file). Requires the theme's AllowThemeColorPicker = true (the default).new("Steely", "#6a90af", "llamachant-sf.variant-steel.min.css"): pins a specific shipped stylesheet. Shipped variants: llamachant-sf.variant-{aqua|blue|steel|green|custom}.min.css.Call the service-collection extension directly (e.g. when you only want a theme's presets):
services.AddLlamachantFrameworkBlazorUITheme(LlamachantUIThemes.SciFi, t => {
t.GroupCaption = "Llamachant SF";
t.DefaultTheme = "Blue";
});
This merges with any groups already defined in appsettings.json's ThemeSwitcher section (it appends a group; it does not replace existing ones).
When a theme's AllowThemeColorPicker is true (the default), a "Custom Color" picker is injected into the theme-switcher flyout under that theme's group. Picking a colour tints whichever theme is active and stores it in the lct-sf-accent cookie (survives reloads). Selecting any theme swatch clears the tint. Tag an app stylesheet meant for other themes with data-lct-sf-suppress and it is disabled while a Llamachant theme is active.
Created with the help and inspiration of Jean Paul Teunisse - thank you!
A clean icon, title, and type name at the top of every detail view

Set EnableRecordHeader = true to add an avatar + title + type + subtitle header to the top of every persistent class's default Detail View. When disabled, none of its model extensions or generator updaters are registered.
.AddLlamachantFrameworkBlazorUIModule(options => options.EnableRecordHeader = true);
Header content is model-driven. Set it in the Model Editor, or in code with [ModelDefault]:
[ImageName("BO_Customer")] // avatar icon (from the model's ImageName)
[ModelDefault("HeaderTitle", "{CompanyName}")] // empty => the class's default property
[ModelDefault("HeaderSubtitle", "{Segment} · {City}")] // ObjectFormatter; empty entries collapse
public class Customer : BaseObject { … }
HeaderTitle format → the class's default property → ToString().Caption.ImageName (rendered as a full-colour image); when none is declared it shows initials derived from the title.ShowRecordHeader checkbox (default on) — untick it to hide the header on that view.HeaderTitle and HeaderSubtitle are ObjectFormatter formats (e.g. {CompanyName}), not Criteria Language.
| Application | Llamachant Framework | Values |
|---|---|---|
| BusinessObjects | ||
| Llamachant.ExpressApp | ||
| BusinessObject | HeaderTitle | ObjectFormatter |
| HeaderSubtitle | ObjectFormatter | |
| Views | ||
| Llamachant.ExpressApp | ||
| BusinessObject | ||
| BusinessObject_DetailView | ShowRecordHeader | True / False |
Supply a Razor template to replace the built-in avatar/title/subtitle markup. The template receives a RecordHeaderContext:
RecordHeaderContext member |
Description |
|---|---|
BusinessObject |
the current object |
Title, Subtitle, TypeCaption |
resolved text |
ImageName, ImageUrl |
class icon name and its resolved full-colour URL |
Initials |
avatar fallback initials |
DetailView |
the IModelDetailView |
Globally — author the fragment in any .razor and assign it in startup:
@* MyHeaderTemplates.razor *@
@code {
public static RenderFragment<RecordHeaderContext> Header = ctx =>
@<div class="record-header">
<div class="record-header-body">
<div class="record-header-name">@ctx.Title</div>
<div class="record-header-subtitle">@ctx.Subtitle</div>
</div>
</div>;
}
.AddLlamachantFrameworkBlazorUIModule(o => {
o.EnableRecordHeader = true;
o.RecordHeader.Template = MyHeaderTemplates.Header;
});
Per view — derive a controller from RecordHeaderTemplateController, scope it, and return the template for that view only:
public class CustomerHeaderController : RecordHeaderTemplateController
{
public CustomerHeaderController() => TargetObjectType = typeof(Customer); // or TargetViewId = "Customer_DetailView"
protected override RenderFragment<RecordHeaderContext> Template => MyHeaderTemplates.Header;
}
Resolution order is per-view template → global RecordHeader.Template → built-in layout. Templates are applied at activation; if you change one after the header has rendered, refresh the Detail View.
| Member | Where | Default | Notes |
|---|---|---|---|
Themes |
module options | empty | themes to register (one switcher group each) |
EnableRecordHeader |
module options | false |
add the RecordHeader |
RecordHeader.Template |
module options | null |
global header override |
AllowThemeColorPicker |
theme options (per theme) | true |
this theme's runtime accent script |
GroupCaption |
theme options (per theme) | "Llamachant UI" |
switcher group caption |
DefaultTheme |
theme options (per theme) | null |
startup variant |
HeaderTitle / HeaderSubtitle |
IModelClass (per class) |
empty | ObjectFormatter formats |
ShowRecordHeader |
IModelDetailView (per view) |
true |
show/hide the header |
Created with the help and inspiration of Jean Paul Teunisse - thank you!
A clean icon, title, and type name at the top of every detail view

Set EnableRecordHeader = true to add an avatar + title + type + subtitle header to the top of every persistent class's default Detail View. When disabled, none of its model extensions or generator updaters are registered.
.AddLlamachantFrameworkBlazorUIModule(options => options.EnableRecordHeader = true);
Header content is model-driven. Set it in the Model Editor, or in code with [ModelDefault]:
[ImageName("BO_Customer")] // avatar icon (from the model's ImageName)
[ModelDefault("HeaderTitle", "{CompanyName}")] // empty => the class's default property
[ModelDefault("HeaderSubtitle", "{Segment} · {City}")] // ObjectFormatter; empty entries collapse
public class Customer : BaseObject { … }
HeaderTitle format → the class's default property → ToString().Caption.ImageName (rendered as a full-colour image); when none is declared it shows initials derived from the title.ShowRecordHeader checkbox (default on) — untick it to hide the header on that view.HeaderTitle and HeaderSubtitle are ObjectFormatter formats (e.g. {CompanyName}), not Criteria Language.
| Application | Llamachant Framework | Values |
|---|---|---|
| BusinessObjects | ||
| Llamachant.ExpressApp | ||
| BusinessObject | HeaderTitle | ObjectFormatter |
| HeaderSubtitle | ObjectFormatter | |
| Views | ||
| Llamachant.ExpressApp | ||
| BusinessObject | ||
| BusinessObject_DetailView | ShowRecordHeader | True / False |
Supply a Razor template to replace the built-in avatar/title/subtitle markup. The template receives a RecordHeaderContext:
RecordHeaderContext member |
Description |
|---|---|
BusinessObject |
the current object |
Title, Subtitle, TypeCaption |
resolved text |
ImageName, ImageUrl |
class icon name and its resolved full-colour URL |
Initials |
avatar fallback initials |
DetailView |
the IModelDetailView |
Globally — author the fragment in any .razor and assign it in startup:
@* MyHeaderTemplates.razor *@
@code {
public static RenderFragment<RecordHeaderContext> Header = ctx =>
@<div class="record-header">
<div class="record-header-body">
<div class="record-header-name">@ctx.Title</div>
<div class="record-header-subtitle">@ctx.Subtitle</div>
</div>
</div>;
}
.AddLlamachantFrameworkBlazorUIModule(o => {
o.EnableRecordHeader = true;
o.RecordHeader.Template = MyHeaderTemplates.Header;
});
Per view — derive a controller from RecordHeaderTemplateController, scope it, and return the template for that view only:
public class CustomerHeaderController : RecordHeaderTemplateController
{
public CustomerHeaderController() => TargetObjectType = typeof(Customer); // or TargetViewId = "Customer_DetailView"
protected override RenderFragment<RecordHeaderContext> Template => MyHeaderTemplates.Header;
}
Resolution order is per-view template → global RecordHeader.Template → built-in layout. Templates are applied at activation; if you change one after the header has rendered, refresh the Detail View.
| Member | Where | Default | Notes |
|---|---|---|---|
Themes |
module options | empty | themes to register (one switcher group each) |
EnableRecordHeader |
module options | false |
add the RecordHeader |
RecordHeader.Template |
module options | null |
global header override |
AllowThemeColorPicker |
theme options (per theme) | true |
this theme's runtime accent script |
GroupCaption |
theme options (per theme) | "Llamachant UI" |
switcher group caption |
DefaultTheme |
theme options (per theme) | null |
startup variant |
HeaderTitle / HeaderSubtitle |
IModelClass (per class) |
empty | ObjectFormatter formats |
ShowRecordHeader |
IModelDetailView (per view) |
true |
show/hide the header |