Skip to content

feat(avalonia): Phase 2 foundation and P0 components - #7

Merged
Shewart merged 9 commits into
mainfrom
feat/avalonia-implementation
Oct 7, 2026
Merged

Shewart merged 9 commits into
mainfrom
feat/avalonia-implementation

Conversation

@Shewart

@Shewart Shewart commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Summary

First Avalonia slice of ShellUI Native (Phase 2): the groundwork plus the P0 components, built the same way as MAUI (one C# file per component, developed in a demo app, copied into the CLI templates).

Foundation

  • examples/Avalonia.Demo (Avalonia 12.1.3, .NET 10, implicit usings off as in dotnet new avalonia.app), in the solution and in CI on Linux, Windows and macOS
  • shell: same theme tokens, palettes and radii as MAUI; both palettes published as theme dictionaries so a theme switch repaints without re-publishing; Token() binds a property to a token, ShellFocus.Ring draws shadcn's keyboard-only focus ring
  • icon: the same 110 Lucide icons, picked with Kind (every Avalonia control already has a Name); theme-toggle
  • generate-icons.py writes both Icon.cs files; sync-templates.py writes the [NativePlatform.Avalonia] template entries and fails if an Avalonia file uses a dependency the template doesn't declare

P0 components

  • button (+ button-variants), input, label (ShellLabel), checkbox, switch, card + header/content/footer, separator, badge, progress, alert, with the MAUI names, properties, variants and 40px sizing
  • Input clears the Fluent TextBox chrome so its own border is the only frame; Card's XAML children go to Items

CLI

  • init in an Avalonia project prints the Avalonia setup steps; list shows only components with a template for the project's platform
  • Projects without <RootNamespace> get a valid namespace from the project name (my-app → my_app), as dotnet new does

Fixes found while testing

  • Token bindings are replaced instead of stacked; stale bindings repainted Ghost/Link buttons and the Outline badge after a theme switch
  • The button spinner turns (frame timer)

Verification

  • dotnet build ShellUI.Native.slnx: 0 warnings; tests 585/585; each commit builds the Avalonia demo and passes the tests
  • Every Avalonia component installs alone into a fresh dotnet new avalonia.app and builds with 0 warnings; all of them together in my-app build and render
  • Demo checked in light and dark, with the keyboard focus ring and the loading state
  • Not verified: real pointer and keyboard input (driven from code), Linux (no WSL on the dev machine; CI builds on ubuntu)

Avalonia templates are not on NuGet yet; they ship with the next release.

Shewatipa Tseisi added 9 commits October 7, 2026 12:49
…toggle

examples/Avalonia.Demo (Avalonia 12.1.3, .NET 10) is where Avalonia components are developed, as MAUI.Demo is for MAUI. Its root namespace is AvaloniaDemo so it doesn't sit inside Avalonia's, and implicit usings are off as in 'dotnet new avalonia.app', so a component missing a using fails in the demo instead of in a user's app.

shell keeps the MAUI token names, palettes and radii and publishes both palettes as theme dictionaries, so a theme switch repaints without re-publishing; Token() binds a property to a resource. icon draws the same 110 Lucide icons with a custom Render and picks the icon with Kind, since every Avalonia control already has a Name. generate-icons.py now writes both Icon.cs files from the same icon list.
…ggle

sync-templates.py also copies examples/Avalonia.Demo into each template's [NativePlatform.Avalonia] entry. Dependencies still come from the MAUI file; the script fails if an Avalonia file uses one the template doesn't declare. Tests check every Avalonia template for the namespace placeholder, no MAUI types, no hard-coded colors, and Avalonia content in all of its dependencies, so an Avalonia install never pulls in MAUI-only code.
In an Avalonia project init prints where to call ShellTheme.EnsureInitialized and the XAML namespace, instead of the MAUI steps. list shows only components with a template for the project's platform, so Avalonia projects don't see MAUI-only components as available.
ARCHITECTURE describes the pattern in use (one C# file per component built in code, no .axaml) instead of the planned TemplatedControl one. DEVELOPMENT_PLAN records the foundation, the remaining P0/P1 work and the move to the ShellIcons packages once they are published.
Projects from 'dotnet new avalonia.app' don't set <RootNamespace>, so the CLI used the project name, and 'my-app' produced 'namespace my-app.Components.UI', which doesn't compile. Without the property the namespace is now the project name made safe the way 'dotnet new' does it: invalid characters become '_' and a leading digit gets a '_' prefix ('my-app' → 'my_app', '2fa.app' → '_2fa.app').
Token() bound a resource observable and dropped the subscription. A later Token() on the same property added a second binding, and ClearValue/SetValue didn't remove the first, so on the next theme change the stale binding repainted the property: Ghost and Link buttons and the Outline badge turned dark after switching to light. Token() now keeps one binding per control and property and disposes the previous one; ClearToken() removes it before a plain value is set.
button (+ button-variants), input, label, checkbox, switch, card with header/content/footer, separator, badge, progress and alert, with the MAUI names, properties, variants and 40px sizing.

Avalonia-specific choices:
- shell gains ShellFocus.Ring (shadcn's ring-2 ring-offset-2, keyboard focus only, replacing Fluent's focus adorner) and token colors at an opacity for glows and shadows.
- input clears the Fluent TextBox chrome by overriding its TextControl* resources, so its own Border is the only frame.
- card is a Border whose XAML children go to Items, because Panel.Render is sealed and Border takes a single child.
- The button spinner advances on a frame timer; an Animation on the RotateTransform throws (the transform animator expects the control) and on the control it didn't turn.

sync-templates.py walks subfolders for Avalonia too (Variants/ButtonVariants.cs). The two registry tests that used button as a MAUI-only example now use WinUI and the button's two platforms.

Checked: every P0 component installs alone into a fresh 'dotnet new avalonia.app' and builds with 0 warnings; all of them in one 'my-app' build and render; the demo in light and dark, with the focus ring and the loading state.
COMPONENTS gains an Avalonia section: what is ported, setup after init, and the differences (Icon Kind, MaxLength 0 means no limit, Card.Items, HorizontalAlignment, keyboard-only focus ring).
@github-actions github-actions Bot added documentation Improvements or additions to documentation cli templates examples ci labels Oct 7, 2026
@Shewart
Shewart merged commit a82197c into main Oct 7, 2026
7 checks passed
@Shewart
Shewart deleted the feat/avalonia-implementation branch October 8, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci cli documentation Improvements or additions to documentation examples templates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant