Skip to content

Commit 3f29381

Browse files
committed
Fixed WCAG contrast ratio calculations to use sRGB to RGB formula
1 parent ec815f7 commit 3f29381

File tree

3 files changed

+25
-10
lines changed

3 files changed

+25
-10
lines changed

components/ColorAnalyzer/samples/ContrastHelper/ContrastHelperSampleBase.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ namespace ColorAnalyzerExperiment.Samples;
1313
public abstract partial class ContrastHelperSampleBase : Page
1414
{
1515
public static readonly DependencyProperty DesiredBackgroundProperty =
16-
DependencyProperty.Register(nameof(DesiredBackground), typeof(Color), typeof(TextBlockContrastSample), new PropertyMetadata(Colors.Black));
16+
DependencyProperty.Register(nameof(DesiredBackground), typeof(Color), typeof(ContrastHelperSampleBase), new PropertyMetadata(Colors.Black));
1717

1818
public static readonly DependencyProperty DesiredForegroundProperty =
19-
DependencyProperty.Register(nameof(DesiredForeground), typeof(Color), typeof(TextBlockContrastSample), new PropertyMetadata(Colors.White));
19+
DependencyProperty.Register(nameof(DesiredForeground), typeof(Color), typeof(ContrastHelperSampleBase), new PropertyMetadata(Colors.White));
2020

2121
private static readonly DependencyProperty MinRatioProperty =
22-
DependencyProperty.Register(nameof(MinRatio), typeof(double), typeof(TextBlockContrastSample), new PropertyMetadata(3d));
22+
DependencyProperty.Register(nameof(MinRatio), typeof(double), typeof(ContrastHelperSampleBase), new PropertyMetadata(3d));
2323

2424
public ContrastHelperSampleBase()
2525
{

components/ColorAnalyzer/src/ColorExtensions.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ internal static double ContrastRatio(this Color color1, Color color2)
3030
// Source WCAG guidelines: https://www.w3.org/TR/WCAG20/#contrast-ratiodef
3131

3232
// Calculate perceived luminance for both colors
33-
double luminance1 = color1.PerceivedLuminance();
34-
double luminance2 = color2.PerceivedLuminance();
33+
double luminance1 = color1.RelativeLuminance();
34+
double luminance2 = color2.RelativeLuminance();
3535

3636
// Determine lighter and darker luminance
3737
double lighter = Math.Max(luminance1, luminance2);
@@ -41,11 +41,11 @@ internal static double ContrastRatio(this Color color1, Color color2)
4141
return (lighter + 0.05) / (darker + 0.05);
4242
}
4343

44-
internal static double PerceivedLuminance(this Color color)
44+
internal static double RelativeLuminance(this Color color)
4545
{
4646
// Color theory is a massive iceberg. Here's a peek at the tippy top:
4747

48-
// There's two (main) standards for calculating luminance from RGB values.
48+
// There's two (main) standards for calculating percieved luminance from RGB values.
4949

5050
// + ------------- + ------------------------------------ + ------------------ + ------------------------------------------------------------------------------- +
5151
// | Standard | Formula | Ref. Section | Ref. Link |
@@ -61,11 +61,26 @@ internal static double PerceivedLuminance(this Color color)
6161

6262
// NOTE: If we for whatever reason we ever need to optimize this code,
6363
// we can make approximations using integer math instead of floating point math.
64-
// The precise values are not critical, as long as the relative luminance is accurate.
64+
// The precise values are not critical, as long as the proportions are similar and sum to 1.
6565
// Like so: return (2 * color.R + 7 * color.G + color.B);
6666

67+
// Adjust channels relative luminance out of sRGB:
68+
// https://www.w3.org/WAI/GL/wiki/Relative_luminance#Definition_as_Stated_in_WCAG_2.x
69+
float sRGBtoRGB(float s)
70+
{
71+
if (s <= 0.03928f)
72+
return s / 12.92f;
73+
74+
return MathF.Pow(((s + 0.055f) / 1.055f), 2.4f);
75+
}
76+
77+
var vec = color.ToVector3();
78+
var r = sRGBtoRGB(vec.X);
79+
var g = sRGBtoRGB(vec.Y);
80+
var b = sRGBtoRGB(vec.Z);
81+
6782
// TLDR: We use ITU Rec. 709 standard formula for perceived luminance.
68-
return (0.2126f * color.R + 0.7152f * color.G + 0.0722 * color.B) / 255;
83+
return (0.2126f * r + 0.7152f * g + 0.0722 * b);
6984
}
7085

7186
internal static float FindColorfulness(this Color color)

components/ColorAnalyzer/src/ContrastHelper/ContrastHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ private static void ApplyContrastCheck(DependencyObject d)
4444

4545
// Current contrast is too small.
4646
// Select either black or white backed on the opponent luminance
47-
var luminance = opponent.PerceivedLuminance();
47+
var luminance = opponent.RelativeLuminance();
4848
var contrastingColor = luminance < 0.5f ? Colors.White : Colors.Black;
4949
UpdateContrastedProperties(d, contrastingColor);
5050
}

0 commit comments

Comments
 (0)