Skip to main content

Overview

Flowery.Uno controls are built with accessibility in mind, providing keyboard navigation, screen reader support, and ARIA properties out of the box. This guide covers accessibility features and best practices for building inclusive applications.

Built-In Accessibility

AutomationProperties Support

All Flowery.Uno controls support standard WinUI AutomationProperties for screen readers:
<daisy:DaisyButton Content="Save Document" 
                   AutomationProperties.Name="Save Document"
                   AutomationProperties.HelpText="Saves the current document to disk" />

AccessibleText Attached Property

Flowery.Uno provides a simplified AccessibleText attached property:
xmlns:services="using:Flowery.Services"

<daisy:DaisyButton Content="💾" 
                   services:DaisyAccessibility.AccessibleText="Save Document" />
This automatically sets AutomationProperties.Name for screen readers.

Keyboard Navigation

Tab Order

All interactive controls support TabIndex for custom tab order:
<StackPanel>
    <daisy:DaisyInput PlaceholderText="First Name" TabIndex="1" />
    <daisy:DaisyInput PlaceholderText="Last Name" TabIndex="2" />
    <daisy:DaisyInput PlaceholderText="Email" TabIndex="3" />
    <daisy:DaisyButton Content="Submit" TabIndex="4" />
</StackPanel>
Controls with TabIndex="0" are included in natural tab order. Negative values exclude elements from tab navigation.

Keyboard Shortcuts

Use AccessKey for keyboard shortcuts:
<daisy:DaisyButton Content="_Save" 
                   AccessKey="S"
                   Click="SaveButton_Click" />
<daisy:DaisyButton Content="_Cancel" 
                   AccessKey="C"
                   Click="CancelButton_Click" />
Users can activate these with Alt + S and Alt + C on Windows.

Enter and Space Key Handling

Flowery.Uno controls support Enter/Space key activation:
private void OnKeyDown(object sender, KeyRoutedEventArgs e)
{
    // Flowery helper handles Enter/Space activation
    DaisyAccessibility.TryHandleEnterOrSpace(e, () =>
    {
        // Perform action
        PerformAction();
    });
}

Screen Reader Support

Automatic Name Resolution

Controls automatically derive accessible names from their content:
<!-- AutomationProperties.Name = "Submit Form" -->
<daisy:DaisyButton Content="Submit Form" />

<!-- AutomationProperties.Name = "Enable Notifications" -->
<daisy:DaisyToggle Header="Enable Notifications" />

<!-- AutomationProperties.Name = "User Email" -->
<daisy:DaisyInput PlaceholderText="User Email" />

Custom Automation Names

Provide explicit names when content is not descriptive:
<!-- Icon-only button needs accessible name -->
<daisy:DaisyButton IconSymbol="Save"
                   AutomationProperties.Name="Save Document" />

<!-- Complex content needs clear name -->
<daisy:DaisyCard AutomationProperties.Name="User Profile Card">
    <StackPanel>
        <Image Source="avatar.png" />
        <TextBlock Text="John Doe" />
        <TextBlock Text="Software Engineer" />
    </StackPanel>
</daisy:DaisyCard>

Live Regions

Use AutomationProperties.LiveSetting for dynamic content:
<daisy:DaisyAlert Variant="Success" 
                  AutomationProperties.LiveSetting="Polite">
    <TextBlock Text="Document saved successfully" />
</daisy:DaisyAlert>

<daisy:DaisyAlert Variant="Error" 
                  AutomationProperties.LiveSetting="Assertive">
    <TextBlock Text="Failed to save document" />
</daisy:DaisyAlert>
Live Setting Values:
  • Off – No announcements (default)
  • Polite – Announces when screen reader is idle
  • Assertive – Interrupts screen reader immediately

Control-Specific Accessibility

Buttons

<daisy:DaisyButton Content="Delete" 
                   Variant="Error"
                   AutomationProperties.Name="Delete Item"
                   AutomationProperties.HelpText="Permanently deletes the selected item" />

Inputs

<StackPanel>
    <TextBlock Text="Email Address" 
               x:Name="EmailLabel" />
    <daisy:DaisyInput PlaceholderText="[email protected]"
                      AutomationProperties.LabeledBy="{x:Bind EmailLabel}" />
</StackPanel>

Toggles

<daisy:DaisyToggle Header="Dark Mode"
                   IsOn="{x:Bind ViewModel.IsDarkMode, Mode=TwoWay}"
                   AutomationProperties.HelpText="Switches between light and dark themes" />
Screen readers announce: “Dark Mode, toggle switch, on/off”.

Checkboxes

<daisy:DaisyCheckBox Content="I agree to the terms and conditions"
                     IsChecked="{x:Bind ViewModel.AgreeToTerms, Mode=TwoWay}"
                     AutomationProperties.HelpText="You must agree to continue" />

Radio Buttons

<StackPanel AutomationProperties.Name="Payment Method">
    <daisy:DaisyRadio Content="Credit Card" 
                      GroupName="PaymentMethod" 
                      IsChecked="True" />
    <daisy:DaisyRadio Content="PayPal" 
                      GroupName="PaymentMethod" />
    <daisy:DaisyRadio Content="Bank Transfer" 
                      GroupName="PaymentMethod" />
</StackPanel>

Progress Indicators

<daisy:DaisyProgress Value="{x:Bind ViewModel.UploadProgress, Mode=OneWay}"
                     Maximum="100"
                     AutomationProperties.Name="Upload Progress"
                     AutomationProperties.LiveSetting="Polite" />

Modals and Dialogs

Flowery.Uno dialogs automatically manage focus:
// DaisyModal automatically:
// - Traps focus within dialog
// - Returns focus on close
// - Announces dialog title to screen readers
var result = await DaisyModal.ShowAsync(
    title: "Confirm Delete",
    message: "Are you sure you want to delete this item?",
    primaryButtonText: "Delete",
    secondaryButtonText: "Cancel"
);

Focus Management

Focus Visible

All interactive controls show focus indicators:
<!-- Focus ring automatically appears on keyboard navigation -->
<daisy:DaisyButton Content="Focused Button" />

Programmatic Focus

Set focus explicitly when needed:
// Set focus to first input on page load
private void Page_Loaded(object sender, RoutedEventArgs e)
{
    FirstNameInput.Focus(FocusState.Programmatic);
}

// Use helper for pointer-based focus
DaisyAccessibility.FocusOnPointer(MyButton);

Focus Scope

Use XYFocusKeyboardNavigation for custom focus regions:
<Grid XYFocusKeyboardNavigation="Enabled">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    
    <!-- Toolbar: Separate focus scope -->
    <StackPanel Grid.Row="0" 
                Orientation="Horizontal"
                XYFocusKeyboardNavigation="Enabled">
        <daisy:DaisyButton Content="New" />
        <daisy:DaisyButton Content="Open" />
        <daisy:DaisyButton Content="Save" />
    </StackPanel>
    
    <!-- Content: Separate focus scope -->
    <ScrollViewer Grid.Row="1" 
                  XYFocusKeyboardNavigation="Enabled">
        <!-- Main content -->
    </ScrollViewer>
</Grid>

Color Contrast

Theme Compliance

Flowery.Uno themes are designed to meet WCAG AA contrast requirements (4.5:1 for normal text).

Verifying Contrast

Test your custom colors:
<!-- Good contrast: #FFFFFF on #1E293B = 12.6:1 -->
<Border Background="#1E293B">
    <TextBlock Foreground="#FFFFFF" Text="High Contrast Text" />
</Border>

<!-- Poor contrast: #A0AEC0 on #CBD5E0 = 1.8:1 ❌ -->
<Border Background="#CBD5E0">
    <TextBlock Foreground="#A0AEC0" Text="Low Contrast Text" />
</Border>
Use Contrast Checkers: Tools like WebAIM Contrast Checker help verify WCAG compliance.

Touch Targets

Minimum Touch Target Size

Flowery.Uno enforces 44px minimum touch targets on mobile platforms:
<!-- On mobile, automatically enforces 44px height -->
<daisy:DaisyButton Content="Tap Me" Size="Small" />

Spacing Between Targets

Provide adequate spacing between interactive elements:
<StackPanel Spacing="16">
    <daisy:DaisyButton Content="Action 1" Size="Large" />
    <daisy:DaisyButton Content="Action 2" Size="Large" />
    <daisy:DaisyButton Content="Action 3" Size="Large" />
</StackPanel>
WCAG Guideline: Touch targets should be at least 44×44 CSS pixels with adequate spacing (typically 8px minimum).

ARIA Properties

Common ARIA Properties

PropertyPurposeExample
AutomationProperties.NameAccessible label”Submit Form”
AutomationProperties.HelpTextAdditional context”Saves changes to the server”
AutomationProperties.LabeledByAssociates labelReference to label element
AutomationProperties.DescribedByDetailed descriptionReference to description element
AutomationProperties.LiveSettingAnnounces changesPolite or Assertive

Form Labels

<StackPanel>
    <!-- Explicit label association -->
    <TextBlock x:Name="UsernameLabel" Text="Username" />
    <daisy:DaisyInput AutomationProperties.LabeledBy="{x:Bind UsernameLabel}" />
    
    <!-- Error message association -->
    <TextBlock x:Name="UsernameError" 
               Text="Username must be at least 3 characters" 
               Foreground="Red"
               Visibility="Collapsed" />
    <daisy:DaisyInput AutomationProperties.DescribedBy="{x:Bind UsernameError}" />
</StackPanel>

Best Practices

1

Provide text alternatives for non-text content

Use AutomationProperties.Name for icons, images, and visual-only controls.
2

Ensure keyboard accessibility

All interactive controls must be keyboard navigable with visible focus indicators.
3

Use semantic HTML-like structure

Group related controls with proper headings and regions.
4

Test with screen readers

Verify your app with Narrator (Windows), NVDA, or JAWS.
5

Maintain color contrast

Ensure 4.5:1 contrast for normal text, 3:1 for large text.
6

Support high contrast mode

Test your app in Windows High Contrast mode.

Testing Accessibility

Windows Narrator

Enable Narrator on Windows:
  1. Press Windows + Ctrl + Enter
  2. Navigate your app with Tab and Arrow keys
  3. Verify all controls are announced correctly

Keyboard-Only Navigation

  1. Unplug your mouse
  2. Navigate using only Tab, Shift+Tab, Arrow keys, Enter, and Space
  3. Verify all functionality is accessible

High Contrast Mode

  1. Open Windows Settings → Accessibility → Contrast themes
  2. Select a high contrast theme
  3. Verify your app remains usable

Common Patterns

Pattern: Accessible Form

<StackPanel Spacing="16">
    <!-- Name field with label -->
    <TextBlock x:Name="NameLabel" Text="Full Name" />
    <daisy:DaisyInput PlaceholderText="Enter your name"
                      AutomationProperties.LabeledBy="{x:Bind NameLabel}"
                      TabIndex="1" />
    
    <!-- Email field with validation -->
    <TextBlock x:Name="EmailLabel" Text="Email" />
    <daisy:DaisyInput PlaceholderText="[email protected]"
                      AutomationProperties.LabeledBy="{x:Bind EmailLabel}"
                      TabIndex="2" />
    <TextBlock x:Name="EmailError" 
               Text="Please enter a valid email"
               Foreground="Red"
               Visibility="{x:Bind ViewModel.ShowEmailError, Mode=OneWay}"
               AutomationProperties.LiveSetting="Polite" />
    
    <!-- Submit button -->
    <daisy:DaisyButton Content="Submit"
                       Variant="Primary"
                       TabIndex="3"
                       AutomationProperties.Name="Submit Form"
                       Click="SubmitButton_Click" />
</StackPanel>

Pattern: Accessible List

<ListView AutomationProperties.Name="Task List"
          SelectionMode="Single">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid AutomationProperties.Name="{x:Bind Title}">
                <daisy:DaisyCheckBox Content="{x:Bind Title}"
                                     IsChecked="{x:Bind IsCompleted, Mode=TwoWay}"
                                     AutomationProperties.HelpText="{x:Bind Description}" />
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Pattern: Accessible Modal

public async Task ShowDeleteConfirmationAsync()
{
    var result = await DaisyModal.ShowAsync(
        title: "Confirm Deletion",
        message: "Are you sure you want to delete this item? This action cannot be undone.",
        primaryButtonText: "Delete",
        secondaryButtonText: "Cancel"
    );
    
    if (result == ContentDialogResult.Primary)
    {
        await DeleteItemAsync();
        
        // Announce result to screen reader
        AnnounceToScreenReader("Item deleted successfully");
    }
}

private void AnnounceToScreenReader(string message)
{
    // Create invisible live region for announcement
    var announcement = new TextBlock
    {
        Text = message,
        Visibility = Visibility.Collapsed
    };
    AutomationProperties.SetLiveSetting(announcement, AutomationLiveSetting.Polite);
    
    // Add to visual tree temporarily
    RootPanel.Children.Add(announcement);
    
    // Remove after announcement
    _ = Task.Delay(2000).ContinueWith(_ =>
    {
        DispatcherQueue.TryEnqueue(() => RootPanel.Children.Remove(announcement));
    });
}

Resources

WCAG Guidelines

Web Content Accessibility Guidelines (WCAG) 2.1

WinUI Accessibility

Microsoft’s WinUI accessibility documentation

Contrast Checker

Verify color contrast ratios for WCAG compliance

Screen Reader Testing

Guide to testing with screen readers

Next Steps

Styling Guide

Learn about control customization

Responsive Design

Build adaptive UIs with size variants

Component Reference

Explore all available controls

Migration Guide

Upgrade from previous versions

Build docs developers (and LLMs) love