Table of Contents

ColorExtensions

Extension methods for manipulating and transforming colors in .NET MAUI applications.

Namespace: GamaLearn.Extensions Assembly: GamaLearn.Maui.Core


Overview

ColorExtensions provides a comprehensive set of extension methods for the Color class, enabling common color operations like lightening, darkening, adjusting opacity, blending, and more. All methods are optimized for performance and maintain the original color's characteristics where appropriate.


Installation

dotnet add package GamaLearn.Maui.Core

Methods

Lighten(float amount = 0.2f)

Lightens the color by moving it toward white.

Parameters:

  • amount - Amount to lighten (0.0 to 1.0). Default is 0.2 (20%)

Returns: A new lightened color

Color blue = Colors.Blue;
Color lighterBlue = blue.Lighten(0.3f); // 30% lighter

Darken(float amount = 0.2f)

Darkens the color by moving it toward black.

Parameters:

  • amount - Amount to darken (0.0 to 1.0). Default is 0.2 (20%)

Returns: A new darkened color

Color red = Colors.Red;
Color darkerRed = red.Darken(0.4f); // 40% darker

WithAlpha(float alpha)

Returns a new color with the specified alpha (opacity) value.

Parameters:

  • alpha - Alpha value (0.0 to 1.0). 0 is fully transparent, 1 is fully opaque

Returns: A new color with the specified alpha

Color semiTransparentRed = Colors.Red.WithAlpha(0.5f); // 50% opacity

MultiplyAlpha(float factor)

Returns a new color with the alpha value multiplied by the specified factor.

Parameters:

  • factor - Factor to multiply alpha by (0.0 to 1.0)

Returns: A new color with adjusted alpha

Color color = Colors.Red.WithAlpha(0.8f);
Color faded = color.MultiplyAlpha(0.5f); // Alpha becomes 0.4 (0.8 * 0.5)

ToGrayscale()

Converts the color to grayscale using the luminosity method.

Returns: A grayscale version of the color

Color gray = Colors.Blue.ToGrayscale();

Invert()

Inverts the color (creates a negative).

Returns: The inverted color

Color inverted = Colors.Blue.Invert();

GetLuminance()

Gets the perceived brightness of the color.

Returns: Luminance value between 0 (black) and 1 (white)

float luminance = Colors.Blue.GetLuminance(); // ~0.2126

IsLight()

Determines if the color is considered light (luminance > 0.5).

Returns: True if the color is light, false if dark

if (backgroundColor.IsLight())
{
    // Use dark text
}

IsDark()

Determines if the color is considered dark (luminance <= 0.5).

Returns: True if the color is dark, false if light

if (backgroundColor.IsDark())
{
    // Use light text
}

GetContrastingTextColor()

Gets a contrasting color (black or white) for readable text.

Returns: Black if background is light, white if background is dark

Color textColor = backgroundColor.GetContrastingTextColor();

Blend(Color color2, float amount = 0.5f)

Blends two colors using linear interpolation.

Parameters:

  • color2 - The second color to blend with
  • amount - Blend amount (0.0 = color1, 1.0 = color2)

Returns: The blended color

Color purple = Colors.Red.Blend(Colors.Blue, 0.5f); // 50/50 blend

ToHex(bool includeAlpha = false)

Converts the color to a hex string representation.

Parameters:

  • includeAlpha - Whether to include the alpha channel

Returns: A hex string in format #RRGGBB or #AARRGGBB

string hex = Colors.Red.ToHex(); // "#FF0000"
string hexWithAlpha = Colors.Red.ToHex(includeAlpha: true); // "#FFFF0000"

FromHex(string hex)

Parses a hex color string to a Color object.

Parameters:

  • hex - Hex string (#RGB, #ARGB, #RRGGBB, or #AARRGGBB)

Returns: The parsed color

Color red = ColorExtensions.FromHex("#FF0000");
Color blue = ColorExtensions.FromHex("#00F");
Color semiRed = ColorExtensions.FromHex("#80FF0000");

TryFromHex(string? hex, out Color? color)

Tries to parse a hex color string.

Parameters:

  • hex - Hex string to parse
  • color - The parsed color, or null if parsing failed

Returns: True if parsing succeeded

if (ColorExtensions.TryFromHex(userInput, out Color? color) && color is not null)
{
    // Use the color
}

Usage Examples

Theme-Adaptive Colors

public class ThemeService
{
    public Color GetButtonColor(Color baseColor, bool isDarkMode)
    {
        if (isDarkMode)
        {
            return baseColor.Lighten(0.2f);
        }
        else
        {
            return baseColor.Darken(0.1f);
        }
    }

    public Color GetTextColor(Color backgroundColor)
    {
        // Automatically choose black or white text
        return backgroundColor.GetContrastingTextColor();
    }
}

Hover Effects

public class ButtonViewModel : ObservableObject
{
    public Color NormalColor { get; } = Colors.Blue;
    public Color HoverColor => NormalColor.Lighten(0.15f);
    public Color PressedColor => NormalColor.Darken(0.1f);
    public Color DisabledColor => NormalColor.MultiplyAlpha(0.5f);
}

Color Transitions

public async Task AnimateColorTransitionAsync(View view, Color fromColor, Color toColor)
{
    const int steps = 30;
    const int delayMs = 16; // ~60 FPS

    for (int i = 0; i <= steps; i++)
    {
        float progress = i / (float)steps;
        Color currentColor = fromColor.Blend(toColor, progress);
        view.BackgroundColor = currentColor;

        await Task.Delay(delayMs);
    }
}

Dynamic Color Palette

public class ColorPaletteGenerator
{
    public List<Color> GenerateMonochromaticPalette(Color baseColor, int count = 5)
    {
        var palette = new List<Color>();
        float step = 0.8f / count;

        for (int i = 0; i < count; i++)
        {
            float factor = 0.1f + (step * i);
            palette.Add(baseColor.Lighten(factor));
        }

        return palette;
    }

    public List<Color> GenerateComplementaryColors(Color baseColor)
    {
        return new List<Color>
        {
            baseColor,
            baseColor.Invert(),
            baseColor.Lighten(0.2f),
            baseColor.Darken(0.2f)
        };
    }
}

Accessibility Helpers

public class AccessibilityHelper
{
    private const float MinimumContrastRatio = 4.5f; // WCAG AA standard

    public bool HasSufficientContrast(Color foreground, Color background)
    {
        float luminance1 = foreground.GetLuminance();
        float luminance2 = background.GetLuminance();

        float lighter = Math.Max(luminance1, luminance2);
        float darker = Math.Min(luminance1, luminance2);

        float contrastRatio = (lighter + 0.05f) / (darker + 0.05f);

        return contrastRatio >= MinimumContrastRatio;
    }

    public Color AdjustForContrast(Color foreground, Color background)
    {
        if (HasSufficientContrast(foreground, background))
        {
            return foreground;
        }

        // Adjust the foreground color
        if (background.IsLight())
        {
            return foreground.Darken(0.3f);
        }
        else
        {
            return foreground.Lighten(0.3f);
        }
    }
}

Color Picker

public class ColorPickerViewModel : ObservableObject
{
    [ObservableProperty]
    private Color selectedColor = Colors.Blue;

    [ObservableProperty]
    private string hexValue = "#0000FF";

    partial void OnSelectedColorChanged(Color value)
    {
        HexValue = value.ToHex();
        OnPropertyChanged(nameof(PreviewColors));
    }

    partial void OnHexValueChanged(string value)
    {
        if (ColorExtensions.TryFromHex(value, out Color? color) && color is not null)
        {
            SelectedColor = color;
        }
    }

    public List<Color> PreviewColors => new()
    {
        SelectedColor.Lighten(0.4f),
        SelectedColor.Lighten(0.2f),
        SelectedColor,
        SelectedColor.Darken(0.2f),
        SelectedColor.Darken(0.4f)
    };
}

Status Indicators

public class StatusIndicatorViewModel : ObservableObject
{
    public Color GetStatusColor(string status) => status switch
    {
        "success" => Colors.Green,
        "warning" => Colors.Orange,
        "error" => Colors.Red,
        "info" => Colors.Blue,
        _ => Colors.Gray
    };

    public Color GetStatusBackgroundColor(string status)
    {
        Color baseColor = GetStatusColor(status);
        return baseColor.Lighten(0.7f).WithAlpha(0.3f);
    }

    public Color GetStatusBorderColor(string status)
    {
        Color baseColor = GetStatusColor(status);
        return baseColor.Darken(0.1f);
    }
}

Gradient Generation

public class GradientHelper
{
    public List<Color> GenerateGradient(Color start, Color end, int steps)
    {
        var gradient = new List<Color>();

        for (int i = 0; i < steps; i++)
        {
            float progress = i / (float)(steps - 1);
            gradient.Add(start.Blend(end, progress));
        }

        return gradient;
    }

    public Brush CreateLinearGradient(Color start, Color end)
    {
        return new LinearGradientBrush(
            new GradientStopCollection
            {
                new GradientStop(start, 0.0f),
                new GradientStop(start.Blend(end, 0.5f), 0.5f),
                new GradientStop(end, 1.0f)
            },
            new Point(0, 0),
            new Point(1, 1)
        );
    }
}

Best Practices

1. Use Named Colors

✅ Good:
public static class AppColors
{
    public static Color Primary => Colors.Blue;
    public static Color PrimaryLight => Primary.Lighten(0.2f);
    public static Color PrimaryDark => Primary.Darken(0.2f);
}

❌ Avoid:
Color button = Color.FromRgb(0, 0, 255).Lighten(0.2f);

2. Cache Computed Colors

✅ Good:
private readonly Lazy<Color> hoverColor;

public MyViewModel()
{
    hoverColor = new Lazy<Color>(() => BaseColor.Lighten(0.15f));
}

public Color HoverColor => hoverColor.Value;

❌ Avoid:
public Color HoverColor => BaseColor.Lighten(0.15f); // Computed every access

3. Validate User Input

✅ Good:
if (ColorExtensions.TryFromHex(userInput, out Color? color) && color is not null)
{
    SelectedColor = color;
}
else
{
    await DisplayAlert("Invalid Color", "Please enter a valid hex color.", "OK");
}

❌ Avoid:
Color color = ColorExtensions.FromHex(userInput); // May throw exception

4. Consider Accessibility

✅ Good:
Color textColor = backgroundColor.GetContrastingTextColor();

if (!HasSufficientContrast(textColor, backgroundColor))
{
    textColor = backgroundColor.IsDark() ? Colors.White : Colors.Black;
}

❌ Avoid:
Color textColor = Colors.Gray; // May not have sufficient contrast

See Also