-
Notifications
You must be signed in to change notification settings - Fork 1
/
apca.scss
145 lines (124 loc) · 6.08 KB
/
apca.scss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
@use "sass:math";
@use "sass:color";
@use "sass:meta";
// -------------------
//
// Sass APCA
//
// Implementation of the [Accessible Perceptual Contrast Algorithm (APCA)](https://git.apcacontrast.com/) for the WCAG 3.0 specification.
//
// -------------------
// Powercurve exponents
$_Strc: 2.4;
$_Ntx: 0.57;
$_Nbg: 0.56;
$_Rtx: 0.62;
$_Rbg: 0.65;
// Clamps and scalers
$_Bclip: 1.414;
$_Bthrsh: 0.022;
$_Wscale: 1.14;
$_Woffset: 0.027;
$_Wclamp: 0.1;
// Multipliers
$_redMultiplier: 0.2126729;
$_greenMultiplier: 0.7151522;
$_blueMultiplier: 0.072175;
@function _luminance($color) {
$red: color.channel($color, "red", $space: rgb);
$green: color.channel($color, "green", $space: rgb);
$blue: color.channel($color, "blue", $space: rgb);
$redLuminance: math.pow(math.div($red, 255), $_Strc) * $_redMultiplier;
$greenLuminance: math.pow(math.div($green, 255), $_Strc) * $_greenMultiplier;
$blueLuminance: math.pow(math.div($blue, 255), $_Strc) * $_blueMultiplier;
@return $redLuminance + $greenLuminance + $blueLuminance;
}
@function _softClip($luminance) {
@if ($luminance < 0) {
@return 0;
} @else if ($luminance < $_Bthrsh) {
@return $luminance + math.pow(($_Bthrsh - $luminance), $_Bclip);
} @else {
@return $luminance;
}
}
@function _polarity($textColor, $backgroundColor) {
$Ytxt: _softClip(_luminance($textColor));
$Ybg: _softClip(_luminance($backgroundColor));
@if ($Ybg > $Ytxt) {
@return (math.pow($Ybg, $_Nbg) - math.pow($Ytxt, $_Ntx)) * $_Wscale;
} @else {
@return (math.pow($Ybg, $_Rbg) - math.pow($Ytxt, $_Rtx)) * $_Wscale;
}
}
/**
* Determine the lightness contrast of a given text and background color using the
* APC algorithm
*
* @param {color} $textColor Text color
* @param {color} $backgroundColor Background color
*
* @return {number} A number between -108 and 107 representing the lightness contrast
*/
@function contrast($textColor, $backgroundColor) {
@if (meta.type-of($textColor) != 'color') {
@error "Type Error: apca.contrast expects a color as first argument but received #{meta.type-of($textColor)}. Please provide a valid color.";
}
@if (meta.type-of($backgroundColor) != 'color') {
@error "Type Error: apca.contrast expects a color as second argument but received #{meta.type-of($backgroundColor)}. Please provide a valid color.";
}
$polarity: _polarity($textColor, $backgroundColor);
@if (math.abs($polarity) < $_Wclamp) {
@return 0;
} @else if ($polarity > 0) {
@return ($polarity - $_Woffset) * 100;
} @else {
@return ($polarity + $_Woffset) * 100;
}
}
/**
* Compares contrasts of light or dark text against a given background and recommends
* usage of the text color with the best contrast.
*
* @param {color} $backgroundColor Background color
* @param {color} $lightColor [optional] Preferred light color, default is white
* @param {color} $darkColor [optional] Preferred dark color, default is black
*
* @return {color} Light or dark text color, whichever provides the better contrast against the chosen background
*/
@function recommend-text-color($backgroundColor, $lightColor: white, $darkColor: black) {
@if (meta.type-of($backgroundColor) != 'color') {
@error "Type Error: apca.recommend-text-color expects a color as first argument but received #{meta.type-of($backgroundColor)}. Please provide a valid color.";
}
@if (meta.type-of($lightColor) != 'color') {
@error "Type Error: apca.recommend-text-color expects a color as second argument but received #{meta.type-of($lightColor)}. Please provide a valid color.";
}
@if (meta.type-of($backgroundColor) != 'color') {
@error "Type Error: apca.recommend-text-color expects a color as third argument but received #{meta.type-of($darkColor)}. Please provide a valid color.";
}
$lightContrast: math.abs(contrast($lightColor, $backgroundColor));
$darkContrast: math.abs(contrast($darkColor, $backgroundColor));
$contrastColor: null;
$bestContrast: null;
@if ($darkContrast > $lightContrast) {
$contrastColor: $darkColor;
$bestContrast: $darkContrast;
} @else {
$contrastColor: $lightColor;
$bestContrast: $lightContrast
}
@if ($bestContrast < 15) {
@warn "Your best contrast is #{$bestContrast}. This is lower than 15, the absolute minimum for any non-text that needs to be discernible and differentiable, and is no less than 6px in its smallest dimension. This may include disabled large buttons. Designers should treat anything below this level as invisible, as it will not be visible for many users. This minimum level should be avoided for any items important to the use, understanding, or interaction of the site.";
} @else if ($bestContrast < 30) {
@warn "Your best contrast is #{$bestContrast}. This is lower than 30, the absolute minimum for any text not listed above. This includes placeholder text and disabled element text. This is also the minimum for large/solid semantic & understandable non-text elements.";
} @else if ($bestContrast < 45) {
@warn "Your best contrast is #{$bestContrast}. This is lower than 45, the minimum for larger, heavier text (36px normal weight or 24px bold) such as headlines. This is also the minimum for pictograms with fine details.";
} @else if ($bestContrast < 60) {
@warn "Your best contrast is #{$bestContrast}. This is lower than 60, the minimum level recommended for content text that is not body, column, or block text. In other words, text you want people to read. The minimums: 24px normal weight (400) or 16px/700 (bold). These values based on the reference font Helvetica.";
} @else if ($bestContrast < 75) {
@warn "Your best contrast is #{$bestContrast}. This is lower than 75, the minimum level for columns of body text with a font no smaller than 18px/400. Lc 75 should be considered a minimum for text where readability is important.";
} @else if ($bestContrast < 90) {
@warn "Your best contrast is #{$bestContrast}. This is lower than 90, the preferred level for fluent text and columns of body text with a font no smaller than 14px/weight 400 (normal).";
}
@return $contrastColor;
}