MaterialColorUtilities 0.1.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package MaterialColorUtilities --version 0.1.1
NuGet\Install-Package MaterialColorUtilities -Version 0.1.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MaterialColorUtilities" Version="0.1.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add MaterialColorUtilities --version 0.1.1
#r "nuget: MaterialColorUtilities, 0.1.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install MaterialColorUtilities as a Cake Addin
#addin nuget:?package=MaterialColorUtilities&version=0.1.1

// Install MaterialColorUtilities as a Cake Tool
#tool nuget:?package=MaterialColorUtilities&version=0.1.1

Material Color Utilities for .NET

C# implementation of Google's Material Color Utilities

Includes all of the algorithms you might need for adding Material You colors to your .NET app:

  • .NET MAUI: add beautiful Material You dynamic theming to your .NET MAUI app in just a single line of code
  • HCT: a new color space designed by Google for perfect contrasts, properties: Hue, Chroma and Tone
  • Palettes: a TonalPalette allows easy access to the tones of a color, a CorePalette contains the 6 key tonal palettes: Primary, Secondary, Tertiary, Neutral, NeutralVariant and Error
  • Scheme: map the colors in a Core Palette to roles, like Primary, TertiaryContainer or OnError
  • Quantize: reduce the number of unique colors in an image to just 128
  • Score: order those colors based on suitability for theming
  • Blend: shift a color towards the theme hue
  • Extension: add custom colors, override mappings

Other stuff:

Install

Get the packages from Nuget:

Package Description Link
MaterialColorUtilites Contains all of the color algorithms and helpers NuGet
MaterialColorUtilites.Maui Adds dynamic colors to your .NET MAUI app NuGet

.NET MAUI

Adding beautiful Material You dynamic colors to your app is super simple, just follow these instructions. The library will handle everything else.

Implementation

Light/dark mode handling works on every platform, but accent color is different on every platform:

  • Android 12+: use system colors
  • Android 8.1+: use WallpaperColors.Primary as seed color
  • Android <8.1: if StorageRead permission is granted, extract a color from the wallpaper, otherwise use the default seed
  • iOS: no accent color, so use default seed
  • Mac: use accent color
  • Windows: use accent color

Setup

  1. Add the MaterialColorUtilites.Maui package using the link above
  2. Add the the highlighted lines to your MauiProgram.cs
+using MaterialColorUtilities.Maui;

namespace YourBeautifulApp;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        MauiAppBuilder builder = MauiApp
            .CreateBuilder()
+           .UseMaterialDynamicColors()
            .UseMauiApp<App>();

        return builder.Build();
    }
}
  1. (optional) Add placeholders to your App.xaml for suggestions when writing XAML
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:YourBeautifulApp"
             x:Class="YourBeautifulApp.App">
    <Application.Resources>
+       <Color x:Key="Primary" />
+       <Color x:Key="PrimaryContainer" />
+       <Color x:Key="Secondary" />
+       <Color x:Key="SecondaryContainer" />
+       <Color x:Key="Tertiary" />
+       <Color x:Key="TertiaryContainer" />
+       <Color x:Key="Surface" />
+       <Color x:Key="SurfaceVariant" />
+       <Color x:Key="Background" />
+       <Color x:Key="Error" />
+       <Color x:Key="ErrorContainer" />
+       <Color x:Key="OnPrimary" />
+       <Color x:Key="OnPrimaryContainer" />
+       <Color x:Key="OnSecondary" />
+       <Color x:Key="OnSecondaryContainer" />
+       <Color x:Key="OnTertiary" />
+       <Color x:Key="OnTertiaryContainer" />
+       <Color x:Key="OnSurface" />
+       <Color x:Key="OnSurfaceVariant" />
+       <Color x:Key="OnError" />
+       <Color x:Key="OnErrorContainer" />
+       <Color x:Key="OnBackground" />
+       <Color x:Key="Outline" />
+       <Color x:Key="Shadow" />
+       <Color x:Key="InverseSurface" />
+       <Color x:Key="InverseOnSurface" />
+       <Color x:Key="InversePrimary" />
+       <Color x:Key="Surface1" />
+       <Color x:Key="Surface2" />
+       <Color x:Key="Surface3" />
+       <Color x:Key="Surface4" />
+       <Color x:Key="Surface5" />
+       <SolidColorBrush x:Key="PrimaryBrush" />
+       <SolidColorBrush x:Key="PrimaryContainerBrush" />
+       <SolidColorBrush x:Key="SecondaryBrush" />
+       <SolidColorBrush x:Key="SecondaryContainerBrush" />
+       <SolidColorBrush x:Key="TertiaryBrush" />
+       <SolidColorBrush x:Key="TertiaryContainerBrush" />
+       <SolidColorBrush x:Key="SurfaceBrush" />
+       <SolidColorBrush x:Key="SurfaceVariantBrush" />
+       <SolidColorBrush x:Key="BackgroundBrush" />
+       <SolidColorBrush x:Key="ErrorBrush" />
+       <SolidColorBrush x:Key="ErrorContainerBrush" />
+       <SolidColorBrush x:Key="OnPrimaryBrush" />
+       <SolidColorBrush x:Key="OnPrimaryContainerBrush" />
+       <SolidColorBrush x:Key="OnSecondaryBrush" />
+       <SolidColorBrush x:Key="OnSecondaryContainerBrush" />
+       <SolidColorBrush x:Key="OnTertiaryBrush" />
+       <SolidColorBrush x:Key="OnTertiaryContainerBrush" />
+       <SolidColorBrush x:Key="OnSurfaceBrush" />
+       <SolidColorBrush x:Key="OnSurfaceVariantBrush" />
+       <SolidColorBrush x:Key="OnErrorBrush" />
+       <SolidColorBrush x:Key="OnErrorContainerBrush" />
+       <SolidColorBrush x:Key="OnBackgroundBrush" />
+       <SolidColorBrush x:Key="OutlineBrush" />
+       <SolidColorBrush x:Key="ShadowBrush" />
+       <SolidColorBrush x:Key="InverseSurfaceBrush" />
+       <SolidColorBrush x:Key="InverseOnSurfaceBrush" />
+       <SolidColorBrush x:Key="InversePrimaryBrush" />
+       <SolidColorBrush x:Key="Surface1Brush" />
+       <SolidColorBrush x:Key="Surface2Brush" />
+       <SolidColorBrush x:Key="Surface3Brush" />
+       <SolidColorBrush x:Key="Surface4Brush" />
+       <SolidColorBrush x:Key="Surface5Brush" />
    </Application.Resources>
</Application>

Options

Specify a fallback seed color as an argument to the extension method or use a lambda for more options:

.UseMaterialDynamicColors(options =>
{
    options.FallbackSeed = unchecked((int)0xFFB000B5);
    options.UseDynamicColor = false;
})

Usage

  • In XAML:
<Button Color="{DynamicResource Primary}" />
  • In C# views:
Button button = new();
button.SetDynamicResource(Button.BackgroundColorProperty, MaterialColorUtilities.Schemes.Keys.Primary);
  • You can also resolve DynamicColorService from the MAUI dependency injection container:
using MaterialColorUtilites.Maui;

public class MyService
{
    public MyService(DynamicColorService dynamicColorService)
    {
        Scheme<Color> scheme = dynamicColorService.SchemeMaui;
    }
}

For samples check out the Playground.Maui project.

Extension

Using custom components (like CorePalette, schemes and mappers) is supported. Just subclass DynamicColorService and supply your own types as generic arguments.

public class MyDynamicColorService : DynamicColorService<MyCorePalette, MyScheme<int>, MyScheme<Color>, MyLightSchemeMapper, MyDarkSchemeMapper>
{
    // You can also override Initialize() and Apply() for custom logic
}

Then use it using the generic extension method:

.UseMaterialDynamicColors<MyDynamicColorService>()

HCT

Google's new color space designed for meeting contrast standards easy. Properties:

  • Hue: 0-360, the color, like red, orange, yellow, green, blue or violet
  • Chroma: how colorful the color is. 0 is gray, and the maximum depends on Hue and Tone.
  • Tone: lightness, 0-100, 0 is black, 100 is white

Convert an RGB color to HCT, change its Tone, then convert back:

int argb = Colors.Azure.ToInt();
Hct hct = Hct.FromInt(argb);
hct.Tone = 30;
int tone30 = hct.ToInt();

Check out the Blazor playground running live here or Material Theme Builder for visualisations.

Palettes

Tonal palette

Used for retrieving tones of a color. alternate text is missing from this package README image

// Does the same as the above code
TonalPalette palette = TonalPalette.FromInt(argb);
int tone30 = palette[30];

Core palette

Contains the Tonal palettes of the key colors. alternate text is missing from this package README image Properties:

public TonalPalette Primary { get; set; }
public TonalPalette Secondary { get; set; }
public TonalPalette Tertiary { get; set; }
public TonalPalette Neutral { get; set; }
public TonalPalette NeutralVariant { get; set; }
public TonalPalette Error { get; set; }

Check out the source code if you want to know how they get constructed.

Scheme

Schemes store the Material Design 3 color role values. alternate text is missing from this package README image

public class Scheme<TColor>
{
    public TColor Primary { get; set; }
    public TColor OnPrimary { get; set; }
    public TColor PrimaryContainer { get; set; }
    public TColor OnPrimaryContainer { get; set; }
    public TColor Secondary { get; set; }
    public TColor OnSecondary { get; set; }
    public TColor SecondaryContainer { get; set; }
    ...
}

Schemes are generic so you can use whatever color model you want.

This library uses "mappers" to turn ColorPalettes into Schemes.

int seed = unchecked((int)0xFF5D5FDB);

// Construct a CorePalette
CorePalette corePalette = CorePalette.Of(seed);

// Get a mapper
DarkSchemeMapper mapper = new();

// Map
Scheme<int> scheme = mapper.Map(corePalette);

// Convert color type
Scheme<Color> schemeMaui = scheme.Convert(Color.FromInt);
Scheme<string> schemeString = scheme.Convert(x => "#" + x.ToString("X")[2..]);

For custom colors, roles or mapping check out Extension.

Quantize

Reduce the number of unique colors in an image. Multiple algorithms are available and they are used together.

Score

Order colors returned by quantization based on suitability for theming and remove similar ones.

Blend

Shift a color towards the hue of the seed color.

using MaterialColorUtilities.Blend;
int harmonized = Blender.Harmonize(color, seedColor);

Putting it all together

Generate a seed color from an image (e.g. the user's wallpaper):

// Load the image into an int[].
// The image is stored in a resource embedded in the assembly, and then decoded and resized using SkiaSharp.
string imageResourceId = "MaterialColorUtilities.Console.Resources.wallpaper.webp";
using Stream resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(imageResourceId);
SKBitmap bitmap = SKBitmap.Decode(resourceStream).Resize(new SKImageInfo(112, 112), SKFilterQuality.Medium);
int[] pixels = bitmap.Pixels.Select(p => (int)(uint)p).ToArray();

// Run quantization and scoring and use the best color
int seedColor = ImageUtils.ColorsFromImage(pixels).First();

Use that color to create a CorePalette;

CorePalette corePalette = new(seedColor);
int color = corePalette.Secondary[69];

Turn the CorePalette into a Scheme<int> using a mapper:

Scheme<int> lightScheme = new LightSchemeMapper().Map(corePalette);
Scheme<int> darkScheme = new DarkSchemeMapper().Map(corePalette);

Then convert your scheme to one with a different color type:

using Color = System.Drawing.Color;
Scheme<Color> lightSchemeColor = lightScheme.ConvertTo(Color.FromArgb);
Scheme<string> lightSchemeString = lightScheme.ConvertTo(x => "#" + x.ToString("X")[2..]);

The same code can be found in the Playground.Console project.

Extension

Adding custom colors

  1. Define a custom key color if you need by subclassing CorePalette:
public class MyCorePalette : CorePalette
{
    public TonalPalette Orange { get; set; }
    
    public MyCorePalette(int seed) : base(seed)
    {
        // You can harmonize a color to make it closer to the seed color
        int harmonizedOrange = Blender.Harmonize(unchecked(0xFFA500), seed);
        Orange = TonalPalette.FromInt(harmonizedOrange);
    }
}
  1. Subclass Scheme<TColor>
public partial class MyScheme<TColor> : Scheme<TColor>
{
    public TColor Orange { get; set; }
    public TColor OnOrange { get; set; }
    public TColor OrangeContainer { get; set; }
    public TColor OnOrangeContainer { get; set; }
}

A source generator will generate new converter methods automatically.

Make sure to mark the class partial and don't nest it inside another class.

If you get warning CS8032: An instance of analyzer MaterialColorUtilities.Schemes.SchemeConverterGenerator cannot be created... your IDE/compiler doesn't have Rosyln 4.0, so the source generator won't work. Make sure you are using Visual Studio 2022, MSBuild 17 or .NET 6.

  1. Create mappers
public class MyLightSchemeMapper : LightSchemeMapper<MyCorePalette, MyScheme<int>>
{
    protected override void MapCore(MyCorePalette palette, MyScheme<int> scheme)
    {
        base.MapCore(palette, scheme);
        scheme.Orange = palette.Orange[40];
        scheme.OnOrange = palette.Orange[100];
        scheme.OrangeContainer = palette.Orange[90];
        scheme.OnOrangeContainer = palette.Orange[10];

        // You can also override already mapped colors
        scheme.Surface = palette.Neutral[100];
    }
}

public class MyDarkSchemeMapper : DarkSchemeMapper<MyCorePalette, MyScheme<int>>
{
    protected override void MapCore(MyCorePalette palette, MyScheme<int> scheme)
    {
        base.MapCore(palette, scheme);
        scheme.Orange = palette.Orange[80];
        scheme.OnOrange = palette.Orange[20];
        scheme.OrangeContainer = palette.Orange[30];
        scheme.OnOrangeContainer = palette.Orange[90];
    }
}
  1. Use your awesome new code
MyCorePalette myCorePalette = new(seedColor);

MyScheme<string> myDarkScheme = new MyDarkSchemeMapper()
    .Map(myCorePalette)
    .ConvertTo(StringUtils.HexFromArgb);

The same code can be found in the Playground.Console project.

More info

For more info and samples check out the source code and the playground projects. If you have questions use the Discussions tab.

Contributing

If you have found a bug or need a new feature, go ahead and open an issue.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • .NETStandard 2.1

    • No dependencies.
  • net6.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on MaterialColorUtilities:

Package Downloads
Material.Components.Maui

Material design Components for .NET MAUI

MaterialColorUtilities.Maui

Material You dynamic theming for .NET MAUI

RichMvvm.MAUI

Package Description

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on MaterialColorUtilities:

Repository Stars
mdc-maui/mdc-maui
Material design components for .NET MAUI
Version Downloads Last updated
0.3.0 5,471 4/11/2023
0.2.0 3,906 11/4/2022
0.1.2 611 8/31/2022
0.1.1 622 7/14/2022
0.1.0 750 4/24/2022
0.0.7 504 4/8/2022
0.0.6 394 3/11/2022
0.0.5 388 3/9/2022