├── Color Contrast Analyzer.sketchplugin └── Contents │ └── Sketch │ ├── analyzcolor.cocoascript │ └── manifest.json ├── README.md └── Sketch-Color-Contrast.png /Color Contrast Analyzer.sketchplugin/Contents/Sketch/analyzcolor.cocoascript: -------------------------------------------------------------------------------- 1 | // Calculates the color contrast of two layers 2 | // Read more about the Web Content Accessibility Guidelines 2.0 3 | // http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast-contrast 4 | // Read the MCOE Accessibility Guidelines 5 | // https://confluence.davita.com/display/MCoE/Accessibility 6 | // Based on these algorithms: http://gmazzocato.altervista.org/colorwheel/algo.php 7 | // ————————————————————————————————————————— 8 | 9 | // This plugin expects a selection of two layers or a single layer that will be checked against the artboard’s background color 10 | 11 | 12 | // Reference to the text layer if there is any 13 | var textLayer = null; 14 | var doc = null; 15 | 16 | var onRun = function(context) { 17 | doc = context.document; 18 | var selection = context.selection; 19 | var page = doc.currentPage(); 20 | 21 | // Setup 22 | var app = NSApplication.sharedApplication(); 23 | 24 | switch (selection.count()) { 25 | case 0: 26 | app.displayDialog_withTitle("Please select one or two layers.", "Color Contrast Analyzer"); 27 | break; 28 | 29 | case 1: 30 | var layer1 = selection[0]; 31 | var color1 = getColorOf(layer1); 32 | 33 | // Get Artboard background color 34 | 35 | if (page.currentArtboard() != null) { 36 | 37 | color2 = page.currentArtboard().backgroundColor(); 38 | 39 | var cr = getColorContrastOf(color1, color2); 40 | showResult(cr); 41 | } else { 42 | app.displayDialog_withTitle("This plugin requires a single layer on an artboard or two selected layers to work.", "Color Contrast Analyzer"); 43 | } 44 | 45 | break; 46 | 47 | case 2: 48 | var layer1 = selection[0]; 49 | var layer2 = selection[1]; 50 | 51 | var color1 = getColorOf(layer1); 52 | var color2 = getColorOf(layer2); 53 | 54 | var cr = getColorContrastOf(color1, color2); 55 | 56 | showResult(cr); 57 | break; 58 | } 59 | 60 | 61 | function getColorOf(layer) { 62 | var color = null; 63 | switch ([layer class]) { 64 | case MSTextLayer: 65 | color = layer.textColor(); 66 | textLayer = layer; 67 | 68 | // Check if text layer has a fill color 69 | 70 | var fill = layer.style().fills().firstObject(); 71 | if (fill != undefined && fill.isEnabled()) color = fill.color(); 72 | break; 73 | default: 74 | var fill = layer.style().fills().firstObject(); 75 | color = fill.color(); 76 | break; 77 | } 78 | return color; 79 | } 80 | } 81 | 82 | function getColorContrastOf(color1, color2) { 83 | 84 | // Color 1 85 | 86 | L1R = color1.red(); 87 | if (L1R <= 0.03928) { 88 | L1R = color1.red() / 12.92; 89 | } else { 90 | L1R = Math.pow(((L1R + 0.055)/1.055), 2.4) 91 | } 92 | 93 | L1G = color1.green(); 94 | if (L1G <= 0.03928) { 95 | L1G = color1.green() / 12.92; 96 | } else { 97 | L1G = Math.pow(((L1G + 0.055)/1.055), 2.4) 98 | } 99 | 100 | L1B = color1.blue(); 101 | if (L1B <= 0.03928) { 102 | L1B = color1.blue() / 12.92; 103 | } else { 104 | L1B = Math.pow(((L1B + 0.055)/1.055), 2.4) 105 | } 106 | 107 | // Color 2 108 | 109 | L2R = color2.red(); 110 | if (L2R <= 0.03928) { 111 | L2R = color2.red() / 12.92; 112 | } else { 113 | L2R = Math.pow(((L2R + 0.055)/1.055), 2.4) 114 | } 115 | 116 | L2G = color2.green(); 117 | if (L2G <= 0.03928) { 118 | L2G = color2.green() / 12.92; 119 | } else { 120 | L2G = Math.pow(((L2G + 0.055)/1.055), 2.4) 121 | } 122 | 123 | L2B = color2.blue(); 124 | if (L2B <= 0.03928) { 125 | L2B = color2.blue() / 12.92; 126 | } else { 127 | L2B = Math.pow(((L2B + 0.055)/1.055), 2.4) 128 | } 129 | 130 | var L1 = 0.2126 * L1R + 0.7152 * L1G + 0.0722 * L1B; 131 | var L2 = 0.2126 * L2R + 0.7152 * L2G + 0.0722 * L2B; 132 | 133 | // Make sure L1 is the lighter color 134 | 135 | if (L1 <= L2) { 136 | var temp = L2; 137 | L2 = L1; 138 | L1 = temp; 139 | } 140 | 141 | // Calculate contrast 142 | 143 | cr = ((L1 + 0.05) / (L2 + 0.05)).toFixed(1); 144 | 145 | return cr; 146 | } 147 | 148 | function showResult (cr) { 149 | // Check against AA / AAA 150 | var result = "❌ Failed MCOE guidelines"; 151 | 152 | var fontSize = 14; 153 | 154 | if (textLayer != null) { 155 | var fontSize = textLayer.fontSize(); 156 | var isBold = false; 157 | 158 | if (textLayer.fontPostscriptName().indexOf("Bold") != -1) { 159 | var isBold = true; 160 | } 161 | } 162 | if ((fontSize >= 18 || (fontSize >= 14 && isBold)) && cr >=4) result = "✅ Passed MCOE guidelines (large text)" 163 | if(cr >= 6) result = "✅ Passed MCOE guidelines" 164 | 165 | if ((fontSize >= 18 || (fontSize >= 14 && isBold)) && cr >=4.5) result = "✅ Exceeded MCOE guidelines (large text)" 166 | if(cr >= 7.0) result = "✅ Exceeded MCOE guidelines" 167 | 168 | // Show ratio 169 | doc.showMessage(result + " - " + cr + ":1"); 170 | 171 | } -------------------------------------------------------------------------------- /Color Contrast Analyzer.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Color Contrast Analyzer", 3 | "identifier" : "com.getflourish.sketch.colorcontrastanalyzer", 4 | "version" : "1.2", 5 | "description" : "A plugin that calculates the color contrast of two layers and evaluates it against MCOE's guidelines.", 6 | "author" : "Florian Schulz | Erica Augustine", 7 | "compatibleVersion": 3, 8 | "commands" : [ 9 | { 10 | "script" : "analyzcolor.cocoascript", 11 | "handler" : "onRun", 12 | "shortcut" : "", 13 | "name" : "Color Contrast Analyzer", 14 | "identifier" : "analyz-color" 15 | } 16 | ], 17 | "menu": { 18 | "isRoot": true, 19 | "items": [ 20 | "analyz-color", 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Color Contrast Analyzer for Sketch 2 | 3 | This Sketch plugin is based on Florian Schulz's [Color Contrast Analyser] (https://github.com/getflourish/Sketch-Color-Contrast-Analyser). It calculates the color contrast of two layers and evaluates them against MCOE's guidelines. If only a single layer is selected, than it will calculate with its artboard background color. The test may pass or fail because of a lack of contrast. When you do not need to meet the requirements, you see the actual ratio so you know how far off you are. This might help you design accessible content. 4 | 5 | Sketch Color Contrast 6 | 7 | ### MCOE requirements 8 | Color contrast of **6:1** or **4:1 for large text** 9 | 10 | ### AA requirements 11 | Color contrast of **4.5:1** or **3.0:1 for large text** 12 | 13 | ### AAA requirements 14 | Color contrast of **7.0:1** or **4.5:1 for large text**. 15 | 16 | ##Notes 17 | - **Large text** means a font size of at least 18pt regular or 14pt bold. [Further details can be found in the Web Content Accessibility Guidelines 2.0.](http://www.w3.org/WAI/WCAG20/quickref/#qr-visual-audio-contrast-contrast) 18 | - This plugin works with solid text and fill colors. Transparency is not supported yet. 19 | - Calculations are based on [these color contrast algorithms.](http://gmazzocato.altervista.org/colorwheel/algo.php) 20 | - Read the [MCOE Accessibility Guidelines] (https://confluence.davita.com/display/MCoE/Accessibility) 21 | -------------------------------------------------------------------------------- /Sketch-Color-Contrast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eaugustine/Sketch-Color-Contrast-Analyzer/d097d1bd368e9b4c2620d9d657b70c499cc7974c/Sketch-Color-Contrast.png --------------------------------------------------------------------------------