dotnet / dotnet/winforms

API Proposal: Calculate Contrast Color for Improved Accessibility

Open
#12,588 15 comments 3 reactions 1 assignee Claimed by @Tanya-Solyanik View on GitHub
api-suggestion area-System.Drawing
Dominant language
C#
Stars
4.9k
Forks
1.1k
Avg merge
20h 23m
Merged PRs (30d)
103

Description

### Background and motivation

When designing user interfaces, it is essential to ensure text or elements displayed over a background color have sufficient contrast for readability. This is particularly relevant for accessibility compliance (e.g., WCAG). Calculating a contrasting color, such as black or white, based on the background color's luminance, is a common requirement.

Currently, .NET's `Color` struct does not provide an in-built way to compute a contrast color. This proposal adds a `ContrastColor` method directly to the Color struct, allowing developers to easily determine the optimal contrasting color (black or white) for any given color.

I currently use a version of this in my applications to help get contrasting text color for different labels/controls where a user can configure its background color (such as the status bar).

Reference: https://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color

### API Proposal

```csharp
using System.Drawing;

public static class ColorExtensions
{
///
/// Calculates a WCAG 2.2 compliant contrasting color (black or white) based on the luminance of the current color.
///
/// The base color.
/// Black for bright colors, white for dark colors.
public static Color ContrastColor(this Color color);

///
/// Determines whether the specified foreground color meets or exceeds
/// the given WCAG contrast ratio when drawn over the specified background color.
///
/// The background color to test against.
/// The foreground color to verify.
///
/// The required WCAG contrast ratio. Defaults to 4.5, which is the standard for normal text.
///
///
/// true if the contrast ratio between and
/// is greater than or equal to ; otherwise false.
///
public static bool IsCompliant(Color background, Color foreground, double requiredRatio = RequiredRatio);
}

```

### API Usage

```csharp
using System.Drawing;

// Suppose you have a background color:
var background = Color.FromArgb(255, 0, 0); // Bright red

// Get a compliant foreground color:
Color foreground = background.ContrastColor();

// Check compliance explicitly if needed:
bool isAccessible = WcagContrastColor.IsCompliant(background, foreground);
```

### Alternative Designs

**Standalone Helper Function**
Instead of an extension method, a static utility function could be added in a helper class. However, attaching the method directly to Color improves discoverability and API integration.

**Configurable Threshold**
An additional overload could allow developers to specify a custom luminance threshold, but this would add complexity without significant value for most use cases.

### Risks

**Perceived Simplicity**
While the luminance formula used is standard, it assumes consistent behavior across platforms. Deviations in rendering systems or gamma settings might lead to slight visual discrepancies.

**Edge Cases**
Fully transparent colors (Color.A = 0) are handled by returning Color.Black as a fallback, which may not align with all design requirements.

### Will this feature affect UI controls?

N/A

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.