SuperAPI AI Directory

avalonia-pro-max/design-system

Design & creation

View repository

Updated 11-03 16:15

About this skill

Design System for Avalonia

Goal

A design system in Avalonia is a set of named resources (colors, brushes, fonts, doubles, thicknesses) that:

  1. Live in ResourceDictionary files merged into App.axaml.
  2. Are referenced via {DynamicResource Name} in views — never hard-coded hex/numbers.
  3. Are split by theme variant (Light/Dark) so the entire UI re-themes instantly.
  4. Follow a 3-layer naming model: primitive → semantic → component.

3-Layer Token Model

Layer 1 — Primitives (raw values, never used in views)

<!-- Resources/Primitives.axaml -->
<ResourceDictionary xmlns="https://github.com/avaloniaui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <!-- Color ramp: 50..950 like Tailwind -->
  <Color x:Key="Slate50">#F8FAFC</Color>
  <Color x:Key="Slate100">#F1F5F9</Color>
  <Color x:Key="Slate200">#E2E8F0</Color>
  <Color x:Key="Slate500">#64748B</Color>
  <Color x:Key="Slate700">#334155</Color>
  <Color x:Key="Slate900">#0F172A</Color>
  <Color x:Key="Slate950">#020617</Color>

  <Color x:Key="Blue500">#3B82F6</Color>
  <Color x:Key="Blue600">#2563EB</Color>
  <Color x:Key="Blue700">#1D4ED8</Color>

  <Color x:Key="Red500">#EF4444</Color>
  <Color x:Key="Green500">#22C55E</Color>
  <Color x:Key="Amber500">#F59E0B</Color>
</ResourceDictionary>

Layer 2 — Semantic tokens (theme-variant aware)

<!-- Resources/Semantic.axaml -->
<ResourceDictionary xmlns="https://github.com/avaloniaui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <ResourceDictionary.MergedDictionaries>
    <ResourceInclude Source="avares://MyApp/Resources/Primitives.axaml"/>
  </ResourceDictionary.MergedDictionaries>

  <ResourceDictionary.ThemeDictionaries>

    <ResourceDictionary x:Key="Light">
      <!-- Surfaces -->
      <SolidColorBrush x:Key="SurfaceBackgroundBrush" Color="{StaticResource Slate50}"/>
      <SolidColorBrush x:Key="SurfaceBrush"           Color="#FFFFFF"/>
      <SolidColorBrush x:Key="SurfaceElevatedBrush"   Color="#FFFFFF"/>
      <SolidColorBrush x:Key="SurfaceMutedBrush"      Color="{StaticResource Slate100}"/>

      <!-- Text -->
      <SolidColorBrush x:Key="TextPrimaryBrush"   Color="{StaticResource Slate900}"/>
      <SolidColorBrush x:Key="TextSecondaryBrush" Color="{StaticResource Slate700}"/>
      <SolidColorBrush x:Key="TextMutedBrush"     Color="{StaticResource Slate500}"/>
      <SolidColorBrush x:Key="TextOnAccentBrush"  Color="#FFFFFF"/>

      <!-- Borders / dividers -->
      <SolidColorBrush x:Key="BorderBrush"         Color="{StaticResource Slate200}"/>
      <SolidColorBrush x:Key="BorderStrongBrush"   Color="{StaticResource Slate500}"/>

      <!-- Brand / status -->
      <SolidColorBrush x:Key="AccentBrush"  Color="{StaticResource Blue600}"/>
      <SolidColorBrush x:Key="DangerBrush"  Color="{StaticResource Red500}"/>
      <SolidColorBrush x:Key="SuccessBrush" Color="{StaticResource Green500}"/>
      <SolidColorBrush x:Key="WarningBrush" Color="{StaticResource Amber500}"/>
    </ResourceDictionary>

    <ResourceDictionary x:Key="Dark">
      <SolidColorBrush x:Key="SurfaceBackgroundBrush" Color="{StaticResource Slate950}"/>
      <SolidColorBrush x:Key="SurfaceBrush"           Color="{StaticResource Slate900}"/>
      <SolidColorBrush x:Key="SurfaceElevatedBrush"   Color="#1E293B"/>
      <SolidColorBrush x:Key="SurfaceMutedBrush"      Color="#1E293B"/>

      <SolidColorBrush x:Key="TextPrimaryBrush"   Color="{StaticResource Slate50}"/>
      <SolidColorBrush x:Key="TextSecondaryBrush" Color="{StaticResource Slate200}"/>
      <SolidColorBrush x:Key="TextMutedBrush"     Color="{StaticResource Slate500}"/>
      <SolidColorBrush x:Key="TextOnAccentBrush"  Color="#FFFFFF"/>

      <SolidColorBrush x:Key="BorderBrush"       Color="#1E293B"/>
      <SolidColorBrush x:Key="BorderStrongBrush" Color="{StaticResource Slate700}"/>

      <SolidColorBrush x:Key="AccentBrush"  Color="{StaticResource Blue500}"/>
      <SolidColorBrush x:Key="DangerBrush"  Color="#F87171"/>
      <SolidColorBrush x:Key="SuccessBrush" Color="#4ADE80"/>
      <SolidColorBrush x:Key="WarningBrush" Color="#FBBF24"/>
    </ResourceDictionary>

  </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

Layer 3 — Component-specific (optional)

<SolidColorBrush x:Key="ButtonPrimaryBackgroundBrush" Color="{DynamicResource AccentBrush}"/>
<SolidColorBrush x:Key="CardBackgroundBrush"          Color="{DynamicResource SurfaceElevatedBrush}"/>

Rule: Views use Layer 2/3 only. Never reference Slate900 directly from a view.


Typography Scale

Choose ONE scale and stick to it. Modular scale 1.25 (Major Third) is a safe default.

<!-- Resources/Typography.axaml -->
<ResourceDictionary xmlns="https://github.com/avaloniaui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="using:System">

  <!-- Font families -->
  <!-- Static font files (Regular/Medium/SemiBold/Bold .ttf/.otf) in Assets/Fonts; family name after '#' -->
  <FontFamily x:Key="FontSans">avares://MyApp/Assets/Fonts#Inter</FontFamily>
  <FontFamily x:Key="FontMono">avares://MyApp/Assets/Fonts#JetBrains Mono</FontFamily>

  <!-- Sizes (modular scale 1.25) -->
  <sys:Double x:Key="FontSizeXs">12</sys:Double>
  <sys:Double x:Key="FontSizeSm">13</sys:Double>
  <sys:Double x:Key="FontSizeBase">14</sys:Double>   <!-- desktop body -->
  <sys:Double x:Key="FontSizeMd">16</sys:Double>
  <sys:Double x:Key="FontSizeLg">18</sys:Double>
  <sys:Double x:Key="FontSizeXl">22</sys:Double>
  <sys:Double x:Key="FontSize2xl">28</sys:Double>
  <sys:Double x:Key="FontSize3xl">36</sys:Double>
  <sys:Double x:Key="FontSize4xl">48</sys:Double>

  <!-- Line heights (multipliers) -->
  <sys:Double x:Key="LineHeightTight">1.2</sys:Double>
  <sys:Double x:Key="LineHeightNormal">1.5</sys:Double>
  <sys:Double x:Key="LineHeightRelaxed">1.65</sys:Double>
</ResourceDictionary>

Type ramp Styles:

<Style Selector="TextBlock.h1">
  <Setter Property="FontFamily" Value="{StaticResource FontSans}"/>
  <Setter Property="FontSize"   Value="{StaticResource FontSize3xl}"/>
  <Setter Property="FontWeight" Value="SemiBold"/>
  <Setter Property="LineHeight" Value="44"/>
  <Setter Property="Foreground" Value="{DynamicResource TextPrimaryBrush}"/>
</Style>

<Style Selector="TextBlock.body">
  <Setter Property="FontSize"   Value="{StaticResource FontSizeBase}"/>
  <Setter Property="LineHeight" Value="21"/>
  <Setter Property="Foreground" Value="{DynamicResource TextPrimaryBrush}"/>
</Style>

<Style Selector="TextBlock.muted">
  <Setter Property="Foreground" Value="{DynamicResource TextMutedBrush}"/>
</Style>

Usage: <TextBlock Classes="h1">Dashboard</TextBlock>

Fonts, variable fonts, letter spacing, text rendering

  • Ship static font files (one .ttf/.otf per weight). Variable-font axis control is not a released Avalonia feature: as of the last check, the fvar/avar parsing and FontVariations property PRs were still open on GitHub (AvaloniaUI/Avalonia #22179, #22185), so do not rely on wght/wdth axes or FontVariations. Weight selection works by matching static files via FontWeight.
  • (12.0+) Only TrueType (.ttf) and OpenType (.otf) fonts are supported; legacy Type 1 (.pfb/.pfm) fonts no longer load.
  • Letter spacing: TextElement.LetterSpacing is an inherited attached property on all templated controls (12.0+), so you can set it once on a parent control. In 11.x it is TextBlock.LetterSpacing only (not inherited). XAML on a TextBlock is source-compatible across versions. Use small positive values (e.g. 0.5) for uppercase labels, slightly negative for large headings.
  • Text rendering options (12.0+): TextOptions.TextRenderingMode, TextOptions.TextHintingMode and TextOptions.BaselinePixelAlignment live on TextOptions. In 11.x the mode is RenderOptions.TextRenderingMode (11.x) — do not mix.
  • (12.0+) If you call UseSkia() explicitly in AppBuilder you must also call UseHarfBuzz() (package Avalonia.HarfBuzz) or startup throws "No text shaping system configured". UsePlatformDetect() handles it.

Spacing Scale (4 / 8 rhythm)

<sys:Double x:Key="Space0">0</sys:Double>
<sys:Double x:Key="Space1">4</sys:Double>
<sys:Double x:Key="Space2">8</sys:Double>
<sys:Double x:Key="Space3">12</sys:Double>
<sys:Double x:Key="Space4">16</sys:Double>
<sys:Double x:Key="Space5">20</sys:Double>
<sys:Double x:Key="Space6">24</sys:Double>
<sys:Double x:Key="Space8">32</sys:Double>
<sys:Double x:Key="Space10">40</sys:Double>
<sys:Double x:Key="Space12">48</sys:Double>
<sys:Double x:Key="Space16">64</sys:Double>

For Margin/Padding use Thickness:

<Thickness x:Key="PaddingCard">20</Thickness>
<Thickness x:Key="PaddingCardCompact">12</Thickness>
<Thickness x:Key="PaddingPage">24,16</Thickness>

Padding="{StaticResource PaddingCard}".


Radius & Elevation

<CornerRadius x:Key="RadiusSm">4</CornerRadius>
<CornerRadius x:Key="RadiusMd">8</CornerRadius>
<CornerRadius x:Key="RadiusLg">12</CornerRadius>
<CornerRadius x:Key="RadiusXl">16</CornerRadius>
<CornerRadius x:Key="RadiusFull">9999</CornerRadius>

<!-- Elevation via BoxShadow -->
<BoxShadows x:Key="ShadowSm">0 1 2 0 #19000000</BoxShadows>
<BoxShadows x:Key="ShadowMd">0 4 8 -2 #26000000, 0 2 4 -2 #1A000000</BoxShadows>
<BoxShadows x:Key="ShadowLg">0 12 24 -8 #33000000, 0 4 8 -4 #1F000000</BoxShadows>

Apply to Border:

<Border Background="{DynamicResource SurfaceBrush}"
        CornerRadius="{StaticResource RadiusLg}"
        BoxShadow="{StaticResource ShadowMd}"
        Padding="{StaticResource PaddingCard}">
  <!-- card content -->
</Border>

For dark mode, halve shadow opacity in the dark theme dictionary (or use a subtle 1px border instead).


Wire Everything Up

<!-- App.axaml -->
<Application xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Class="MyApp.App"
             RequestedThemeVariant="Default">

  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceInclude Source="avares://MyApp/Resources/Primitives.axaml"/>
        <ResourceInclude Source="avares://MyApp/Resources/Semantic.axaml"/>
        <ResourceInclude Source="avares://MyApp/Resources/Typography.axaml"/>
        <ResourceInclude Source="avares://MyApp/Resources/Spacing.axaml"/>
        <ResourceInclude Source="avares://MyApp/Resources/Radius.axaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>

  <Application.Styles>
    <FluentTheme/>
    <StyleInclude Source="avares://MyApp/Styles/Typography.axaml"/>
    <StyleInclude Source="avares://MyApp/Styles/Components.axaml"/>
  </Application.Styles>
</Application>

Theme Switching at Runtime

Application.Current!.RequestedThemeVariant =
    Application.Current.ActualThemeVariant == ThemeVariant.Dark
        ? ThemeVariant.Light
        : ThemeVariant.Dark;

Persist to settings; restore on launch in OnFrameworkInitializationCompleted.


Palette Selection by Product Type

Product type Accent suggestion Tone
Productivity / SaaS dashboard Blue 600 / Indigo 600 Cool, professional
Developer tool / IDE Slate + accent (Cyan 500 or Violet 500) Neutral, dark-mode first
Finance / banking Green 600 or Navy 800 Trust, conservative
Health / wellness Teal 500 / Emerald 500 Calm, fresh
Creative / media Bold gradient or Magenta 500 Vibrant
Gaming / entertainment Purple 600 + Neon accent Energetic
Enterprise B2B Slate + Brand color from logo Restrained
Education / kids Saturated multi-color (Amber/Sky/Pink) Friendly

Always validate accent on white and the dark surface for ≥4.5:1 contrast on text overlays.


Common Mistakes

  • Hex literals in views — kills theming. Always DynamicResource.
  • Missing dark-mode variant — app looks broken if user's OS is dark.
  • Using StaticResource for theme-variant brushes — won't switch at runtime.
  • More than ~3 accent colors — visual noise; pick one accent + one optional secondary.
  • Inventing one-off spacing values (Margin="13,7") — breaks rhythm.
  • Body text below 13px on desktop — readability suffers.
  • Pure black #000 text on pure white — too harsh; use Slate 900 / Slate 50.
  • Same shadow opacity in dark mode — looks muddy. Reduce or replace with a 1px border.