ShadUI Documentation

repository·main·Indexed 19 days ago

https://github.com/accntech/shad-ui

An Avalonia-based cross-platform UI library for .NET developers inspired by shadcn/ui and Suki UI. It provides a modern set of UI components, including a specialized Window and DialogHost system with automatic visual tree ancestor resolution. The library includes accessibility-focused controls like Badge with WCAG contrast support and High Contrast Mode.

Tokens
3.6K
Snippets
11
Records
20
Agent score
67%

What's inside ShadUI

  1. How DialogHost and ShadUI.Window interact

    main

    In ShadUI, DialogHost is designed to be decoupled from ShadUI.Window. It can be hosted anywhere (a vanilla Avalonia.Controls.Window, a UserControl, or a Panel), but it gains special window-level features when it detects an Avalonia.Controls.Window ancestor in its visual tree.

    Key Behaviors

    • Inside a ShadUI.Window: If you add a DialogHost to the Hosts collection of a ShadUI.Window, the window automatically subscribes to the host's HasOpenDialog state. This enables features like snap-layout suppression (preventing Windows snap-layout flyouts from appearing when a dialog is open).
    • Inside other containers: When hosted in a UserControl or Panel, DialogHost still functions for dialog management, but window-specific UX like title-bar dragging and double-tap maximize will be no-ops.

    Integration Point

    The recommended way to integrate a DialogHost with a ShadUI.Window is via the Window.Hosts collection.

    <shadui:Window.Hosts>
        <shadui:DialogHost
            Manager="{Binding DialogManager}"
            x:Name="PART_DialogHost" />
    </shadui:Window.Hosts>
  2. Handle Badge content length and layout integration

    main

    The Badge control is built to handle diverse content scenarios gracefully:

    • Content Length: Supports single characters, numbers, words, phrases, and long sentences/descriptions. It handles empty or null content without breaking.
    • Text Wrapping: Manages text wrapping and width constraints effectively.
    • Layout Integration: Works seamlessly within common Avalonia layout containers, including:
      • StackPanel: Maintains proper spacing.
      • Grid: Supports standard positioning and alignment.
      • WrapPanel: Ideal for tag-like scenarios where multiple badges are used.
    • Complex Content: Can contain multi-element structures, such as a StackPanel containing both icons and text, or styled content using runs and formatting.
  3. Configure Badge color contrast and High Contrast Mode

    main

    The Badge control is designed to meet WCAG accessibility standards for color contrast. It supports both WCAG AA (4.5:1 ratio) and WCAG AAA (7:1 ratio) standards across its variants.

    When implementing custom themes or colors, ensure they maintain these ratios. The control also includes built-in support for High Contrast Mode, automatically adapting to system high contrast colors and ensuring contrast ratios remain valid even in disabled states.

  4. How ShadUI.Window aggregates DialogHost state

    main

    A ShadUI.Window tracks the open state of its dialogs by observing the Hosts collection. When a DialogHost is added to or removed from the Hosts collection, the window subscribes to or unsubscribes from that host's PropertyChanged event.

    The window's HasOpenDialog property is an aggregate value: it is true if any DialogHost in the Hosts collection has HasOpenDialog set to true, and false otherwise.

  5. How DialogHost resolves its owner window

    main

    In recent versions of ShadUI, DialogHost has been decoupled from ShadUI.Window. Instead of requiring an explicit Owner property, DialogHost automatically identifies its parent window by searching the visual tree for the nearest ancestor of type Avalonia.Controls.Window. This allows features like window dragging and maximizing to work seamlessly without manual property assignment.

    // DialogHost internally uses a method like this to find the window:
    private Avalonia.Controls.Window? ResolveOwnerWindow()
        => _ancestorWindow ??= this.FindAncestorOfType<Avalonia.Controls.Window>();
  6. Decouple DialogHost from ShadUI.Window

    main

    The DialogHost component is being decoupled from ShadUI.Window to allow it to function in any container. Previously, DialogHost had a compile-time dependency on ShadUI.Window and manually updated the Owner.HasOpenDialog property.

    In the new architecture:

    • DialogHost no longer references ShadUI.Window directly.
    • DialogHost caches an Avalonia.Controls.Window ancestor to handle window-specific features like drag/maximize/snap-layout suppression.
    • ShadUI.Window becomes the observer. It monitors its Hosts collection and aggregates the HasOpenDialog state from all DialogHost instances within it.
    • Breaking Change: The Owner property on DialogHost is being removed.
  7. Use Badge disabled states and keyboard navigation

    main

    When a Badge is set to a disabled state, it provides visual feedback through opacity reduction while maintaining content accessibility.

    For interactive or focusable badges, the following features are supported:

    • Keyboard Navigation: Supports the Focusable property and TabIndex for custom tab orders.
    • Focus Management: Integrates with standard Avalonia focus management.
    • Tooltips: Supports ToolTip integration for hover-based information display, which is also implemented to be accessibility-friendly.
  8. Migrate from DialogHost.Owner to decoupled hosting

    main

    As of the design update on 2026-05-07, the Owner property and OwnerProperty have been removed from DialogHost. DialogHost now automatically resolves its window ancestor via the visual tree.

    Breaking Changes

    • Removed: DialogHost.Owner property.
    • Removed: DialogHost.OwnerProperty.

    Migration Steps

    1. Remove Bindings: Search your XAML for any <shadui:DialogHost> elements that use the Owner attribute or binding and delete them.
    2. Use Hosts Collection: To ensure ShadUI.Window features (like snap-layout suppression) work correctly, ensure your DialogHost is placed within the <shadui:Window.Hosts> collection.

    Incorrect (Old Way):

    <shadui:DialogHost Owner="{Binding MyWindow}" ... />

    Correct (New Way):

    <shadui:Window.Hosts>
        <shadui:DialogHost Manager="{Binding DialogManager}" />
    </shadui:Window.Hosts>
  9. Expose ShadUI internals to the test assembly

    main

    To allow the ShadUI.Tests project to access internal properties like Window.HasOpenDialog and DialogHost.HasOpenDialog, you must add an InternalsVisibleTo entry to the main project file.

    Edit src/ShadUI/ShadUI.csproj and add the following ItemGroup after the first </PropertyGroup> block:

    <ItemGroup>
        <InternalsVisibleTo Include="ShadUI.Tests" />
    </ItemGroup>
  10. Configure DialogHost in XAML without the Owner property

    main

    When using DialogHost within a ShadUI.Window, you no longer need to bind the Owner property. The DialogHost will automatically detect the window via the visual tree. Remove the Owner attribute from your XAML declaration to avoid binding errors.

    <!-- Correct way to declare DialogHost in ShadUI.Window -->
    <shadui:Window.Hosts>
        <shadui:DialogHost
            Manager="{Binding DialogManager}"
            x:Name="PART_DialogHost" />
    </shadui:Window.Hosts>
  11. Configure ShadUI in App.xaml

    main

    After installing the package, you must include the ShadTheme in your App.xaml file to enable the ShadUI styling system. You will need to define a namespace mapping for ShadUI.

    <Application
        xmlns:themes="clr-namespace:ShadUI;assembly=ShadUI">
    
        <!-- other code -->
    
        <Application.Styles>
            <themes:ShadTheme />
            <!-- other styles -->
        </Application.Styles>
    </Application>