Files
pos-system/microservices/.agent/skills/maui-branding-expert/guidelines/resource-architecture.md
Ho Ngoc Hai 76d75c753b Migrate
2026-05-23 18:37:02 +07:00

12 KiB

Resource Dictionary Architecture / Kiến Trúc Resource Dictionary

Overview / Tổng Quan

Resource Dictionary là nền tảng của Design System trong .NET MAUI. Việc tổ chức đúng cách giúp:

  • Re-branding dễ dàng: Chỉ cần thay đổi 1 file
  • Maintainability: Code UI sạch, không lặp lại
  • Consistency: Đảm bảo UI đồng nhất toàn app

Directory Structure / Cấu Trúc Thư Mục

Resources/
├── Styles/
│   ├── Colors.xaml        # Bảng màu chính
│   ├── Typography.xaml    # Font families, sizes
│   ├── Spacing.xaml       # Margins, paddings
│   └── Theme.xaml         # Styles cho controls
├── Fonts/
│   ├── Inter-Regular.ttf
│   └── Inter-Bold.ttf
└── Images/
    ├── logo.svg
    └── icons/

1. Colors.xaml - Bảng Màu

Nguyên tắc đặt tên

  • Brand Colors: Brand* (Primary, Secondary, Accent)
  • Semantic Colors: Color* (Success, Warning, Error, Info)
  • Surface Colors: Surface* (Background, Card, Overlay)
  • Text Colors: Text* (Primary, Secondary, Disabled)

Complete Example

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    x:Class="MyApp.Resources.Styles.Colors">

    <!-- ==================== BRAND COLORS ==================== -->
    <!-- Primary brand color - used for main CTAs -->
    <Color x:Key="BrandPrimary">#2196F3</Color>
    <Color x:Key="BrandPrimaryDark">#1976D2</Color>
    <Color x:Key="BrandPrimaryLight">#64B5F6</Color>
    
    <!-- Secondary brand color - used for secondary actions -->
    <Color x:Key="BrandSecondary">#FF5722</Color>
    
    <!-- ==================== SEMANTIC COLORS ==================== -->
    <Color x:Key="ColorSuccess">#4CAF50</Color>
    <Color x:Key="ColorWarning">#FF9800</Color>
    <Color x:Key="ColorError">#F44336</Color>
    <Color x:Key="ColorInfo">#2196F3</Color>
    
    <!-- ==================== SURFACE COLORS (Theme-aware) ==================== -->
    <Color x:Key="SurfaceBackground">
        {AppThemeBinding Light=#FAFAFA, Dark=#121212}
    </Color>
    <Color x:Key="SurfaceCard">
        {AppThemeBinding Light=#FFFFFF, Dark=#1E1E1E}
    </Color>
    <Color x:Key="SurfaceOverlay">
        {AppThemeBinding Light=#00000033, Dark=#FFFFFF33}
    </Color>
    
    <!-- ==================== TEXT COLORS (Theme-aware) ==================== -->
    <Color x:Key="TextPrimary">
        {AppThemeBinding Light=#212121, Dark=#FFFFFF}
    </Color>
    <Color x:Key="TextSecondary">
        {AppThemeBinding Light=#757575, Dark=#B0B0B0}
    </Color>
    <Color x:Key="TextDisabled">
        {AppThemeBinding Light=#9E9E9E, Dark=#616161}
    </Color>
    <Color x:Key="TextOnPrimary">#FFFFFF</Color>
    
    <!-- ==================== BORDER COLORS ==================== -->
    <Color x:Key="BorderDefault">
        {AppThemeBinding Light=#E0E0E0, Dark=#424242}
    </Color>
    <Color x:Key="BorderFocused">{StaticResource BrandPrimary}</Color>

</ResourceDictionary>

2. Typography.xaml - Font & Text Styles

Font Registration (MauiProgram.cs)

builder.ConfigureFonts(fonts =>
{
    fonts.AddFont("Inter-Regular.ttf", "InterRegular");
    fonts.AddFont("Inter-Medium.ttf", "InterMedium");
    fonts.AddFont("Inter-Bold.ttf", "InterBold");
    fonts.AddFont("Inter-SemiBold.ttf", "InterSemiBold");
});

Typography Scale

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    x:Class="MyApp.Resources.Styles.Typography">

    <!-- ==================== FONT FAMILIES ==================== -->
    <OnPlatform x:Key="FontFamilyRegular" x:TypeArguments="x:String">
        <On Platform="Android, iOS" Value="InterRegular" />
        <On Platform="WinUI" Value="Assets/Fonts/Inter-Regular.ttf#Inter" />
    </OnPlatform>
    
    <OnPlatform x:Key="FontFamilyBold" x:TypeArguments="x:String">
        <On Platform="Android, iOS" Value="InterBold" />
        <On Platform="WinUI" Value="Assets/Fonts/Inter-Bold.ttf#Inter" />
    </OnPlatform>
    
    <!-- ==================== FONT SIZES (Scalable) ==================== -->
    <!-- Base size: 16 (Body) -->
    <x:Double x:Key="FontSizeCaption">12</x:Double>
    <x:Double x:Key="FontSizeBody">16</x:Double>
    <x:Double x:Key="FontSizeSubtitle">18</x:Double>
    <x:Double x:Key="FontSizeTitle">24</x:Double>
    <x:Double x:Key="FontSizeHeadline">32</x:Double>
    <x:Double x:Key="FontSizeDisplay">48</x:Double>
    
    <!-- ==================== TEXT STYLES ==================== -->
    <Style x:Key="TextCaption" TargetType="Label">
        <Setter Property="FontFamily" Value="{StaticResource FontFamilyRegular}" />
        <Setter Property="FontSize" Value="{StaticResource FontSizeCaption}" />
        <Setter Property="TextColor" Value="{StaticResource TextSecondary}" />
    </Style>
    
    <Style x:Key="TextBody" TargetType="Label">
        <Setter Property="FontFamily" Value="{StaticResource FontFamilyRegular}" />
        <Setter Property="FontSize" Value="{StaticResource FontSizeBody}" />
        <Setter Property="TextColor" Value="{StaticResource TextPrimary}" />
    </Style>
    
    <Style x:Key="TextTitle" TargetType="Label">
        <Setter Property="FontFamily" Value="{StaticResource FontFamilyBold}" />
        <Setter Property="FontSize" Value="{StaticResource FontSizeTitle}" />
        <Setter Property="TextColor" Value="{StaticResource TextPrimary}" />
    </Style>
    
    <Style x:Key="TextHeadline" TargetType="Label">
        <Setter Property="FontFamily" Value="{StaticResource FontFamilyBold}" />
        <Setter Property="FontSize" Value="{StaticResource FontSizeHeadline}" />
        <Setter Property="TextColor" Value="{StaticResource TextPrimary}" />
        <Setter Property="SemanticProperties.HeadingLevel" Value="Level1" />
    </Style>

</ResourceDictionary>

3. Theme.xaml - Control Styles

Implicit vs Explicit Styles

Type Syntax Use Case
Implicit <Style TargetType="Button"> Default cho tất cả Button
Explicit <Style x:Key="PrimaryButton" TargetType="Button"> Áp dụng có chọn lọc

Complete Example

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    x:Class="MyApp.Resources.Styles.Theme">

    <!-- ==================== BUTTON STYLES ==================== -->
    
    <!-- Primary Button (Filled) -->
    <Style x:Key="ButtonPrimary" TargetType="Button">
        <Setter Property="BackgroundColor" Value="{StaticResource BrandPrimary}" />
        <Setter Property="TextColor" Value="{StaticResource TextOnPrimary}" />
        <Setter Property="FontFamily" Value="{StaticResource FontFamilyBold}" />
        <Setter Property="FontSize" Value="{StaticResource FontSizeBody}" />
        <Setter Property="CornerRadius" Value="8" />
        <Setter Property="Padding" Value="24,12" />
        <Setter Property="VisualStateManager.VisualStateGroups">
            <VisualStateGroupList>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal" />
                    <VisualState x:Name="Pressed">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" 
                                    Value="{StaticResource BrandPrimaryDark}" />
                            <Setter Property="Scale" Value="0.98" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Disabled">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" 
                                    Value="{StaticResource TextDisabled}" />
                            <Setter Property="TextColor" 
                                    Value="{StaticResource SurfaceCard}" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateGroupList>
        </Setter>
    </Style>
    
    <!-- Secondary Button (Outlined) -->
    <Style x:Key="ButtonSecondary" TargetType="Button">
        <Setter Property="BackgroundColor" Value="Transparent" />
        <Setter Property="TextColor" Value="{StaticResource BrandPrimary}" />
        <Setter Property="BorderColor" Value="{StaticResource BrandPrimary}" />
        <Setter Property="BorderWidth" Value="2" />
        <Setter Property="FontFamily" Value="{StaticResource FontFamilyBold}" />
        <Setter Property="CornerRadius" Value="8" />
        <Setter Property="Padding" Value="24,12" />
    </Style>
    
    <!-- ==================== ENTRY STYLES ==================== -->
    
    <Style x:Key="EntryDefault" TargetType="Entry">
        <Setter Property="BackgroundColor" Value="{StaticResource SurfaceCard}" />
        <Setter Property="TextColor" Value="{StaticResource TextPrimary}" />
        <Setter Property="PlaceholderColor" Value="{StaticResource TextSecondary}" />
        <Setter Property="FontFamily" Value="{StaticResource FontFamilyRegular}" />
        <Setter Property="FontSize" Value="{StaticResource FontSizeBody}" />
    </Style>
    
    <!-- ==================== FRAME/CARD STYLES ==================== -->
    
    <Style x:Key="CardDefault" TargetType="Frame">
        <Setter Property="BackgroundColor" Value="{StaticResource SurfaceCard}" />
        <Setter Property="CornerRadius" Value="12" />
        <Setter Property="HasShadow" Value="True" />
        <Setter Property="Padding" Value="16" />
        <Setter Property="BorderColor" Value="Transparent" />
    </Style>

</ResourceDictionary>

4. Merged Dictionaries trong App.xaml

Thứ tự quan trọng

<!-- App.xaml -->
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyApp.App">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <!-- 1. Colors FIRST (base values) -->
                <ResourceDictionary Source="Resources/Styles/Colors.xaml" />
                
                <!-- 2. Typography (depends on nothing) -->
                <ResourceDictionary Source="Resources/Styles/Typography.xaml" />
                
                <!-- 3. Spacing (if any) -->
                <ResourceDictionary Source="Resources/Styles/Spacing.xaml" />
                
                <!-- 4. Theme LAST (depends on Colors, Typography) -->
                <ResourceDictionary Source="Resources/Styles/Theme.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Best Practices / Thực Hành Tốt Nhất

DO

<!-- Dùng StaticResource cho performance -->
<Button BackgroundColor="{StaticResource BrandPrimary}" />

<!-- Dùng AppThemeBinding cho theme-aware colors -->
<Color x:Key="Background">{AppThemeBinding Light=#FFF, Dark=#000}</Color>

<!-- Đặt tên có prefix rõ ràng -->
<Color x:Key="BrandPrimary" />
<Color x:Key="TextPrimary" />
<Style x:Key="ButtonPrimary" />

DON'T

<!-- KHÔNG hard-code màu -->
<Button BackgroundColor="#2196F3" />

<!-- KHÔNG đặt tên mơ hồ -->
<Color x:Key="Blue" />
<Color x:Key="MyColor" />

<!-- KHÔNG gộp tất cả vào App.xaml -->
<Application.Resources>
    <Color x:Key="Primary" />
    <Color x:Key="Secondary" />
    <!-- 500 lines more... -->
</Application.Resources>

Re-branding Workflow

Khi cần thay đổi thương hiệu:

  1. Chỉ sửa Colors.xaml - Thay đổi BrandPrimary, BrandSecondary
  2. Cập nhật fonts nếu cần trong Typography.xaml
  3. Rebuild app - Tất cả UI tự động cập nhật
<!-- Colors.xaml -->
- <Color x:Key="BrandPrimary">#2196F3</Color>
+ <Color x:Key="BrandPrimary">#FF5733</Color>