From 68a8c3ba90fa1b71fdd6120fecb42203c3c61ef4 Mon Sep 17 00:00:00 2001
From: Chuck Dries <chuck@chuckdries.com>
Date: Sat, 19 Jun 2021 10:35:17 -0700
Subject: [PATCH] Programmatically check and improve contrast ratios on dynamic
 color palettes

---
 gatsby-node.js | 75 ++++++++++++++++++++++++++++++++++++--------------
 package.json   |  1 +
 yarn.lock      |  2 +-
 3 files changed, 57 insertions(+), 21 deletions(-)

diff --git a/gatsby-node.js b/gatsby-node.js
index c213308..b9341ba 100644
--- a/gatsby-node.js
+++ b/gatsby-node.js
@@ -4,11 +4,61 @@ const path = require('path');
 const { read } = require('fast-exif');
 const iptc = require('node-iptc');
 const Vibrant = require('node-vibrant');
-const R = require('ramda');
 const chroma = require('chroma-js');
+const chalk = require('chalk');
 
 const readFile = util.promisify(fs.readFile);
 
+const badContrast = (color1, color2) => chroma.contrast(color1, color2) < 4.5;
+
+function processColors(vibrantData, imagePath) {
+  let Vibrant = chroma(vibrantData.Vibrant.getRgb());
+  let DarkVibrant = chroma(vibrantData.DarkVibrant.getRgb());
+  let LightVibrant = chroma(vibrantData.LightVibrant.getRgb());
+  let Muted = chroma(vibrantData.Muted.getRgb());
+  let DarkMuted = chroma(vibrantData.DarkMuted.getRgb());
+  let LightMuted = chroma(vibrantData.LightMuted.getRgb());
+
+  // first pass - darken bg and lighten relevant fg colors
+  if (badContrast(DarkVibrant, Vibrant) || badContrast(DarkVibrant, LightMuted)) {
+    DarkVibrant = DarkVibrant.darken();
+    if (badContrast(DarkVibrant, Vibrant)) {
+      Vibrant = Vibrant.brighten();
+    }
+    if (badContrast(DarkVibrant, Vibrant)) {
+      Vibrant = Vibrant.brighten();
+    }
+  }
+  // second pass - first doesn't always get it right.
+  if (badContrast(DarkVibrant, Vibrant) || badContrast(DarkVibrant, LightMuted)) {
+    DarkVibrant = DarkVibrant.darken();
+    if (badContrast(DarkVibrant, Vibrant)) {
+      Vibrant = Vibrant.brighten(2);
+    }
+    if (badContrast(DarkVibrant, LightMuted)) {
+      LightMuted = LightMuted.brighten(2);
+    }
+  }
+
+  if (badContrast(DarkVibrant, Vibrant)){
+    console.log('contrast still too low', imagePath);
+    console.log(chalk.hex(Vibrant.hex()).bgHex(DarkVibrant.hex())(`DV-V: ${chroma.contrast(DarkVibrant, Vibrant)}`));
+  }
+  if (badContrast(DarkVibrant, LightMuted)){
+    console.log('contrast still too low', imagePath);
+    console.log(chalk.hex(LightMuted.hex()).bgHex(DarkVibrant.hex())(`DV-LM: ${chroma.contrast(DarkVibrant, LightMuted)}`));
+  }
+
+
+  return {
+    Vibrant: Vibrant.rgb(),
+    DarkVibrant: DarkVibrant.rgb(),
+    LightVibrant: LightVibrant.rgb(),
+    Muted: Muted.rgb(),
+    DarkMuted: DarkMuted.rgb(),
+    LightMuted: LightMuted.rgb(),
+  };
+}
 
 function convertDMSToDD(dms, positiveDirection) {
   const res = dms
@@ -19,7 +69,7 @@ function convertDMSToDD(dms, positiveDirection) {
   return positiveDirection ? res : -res;
 }
 
-function transformMetaToNodeData(exifData, iptcData, vibrantData) {
+function transformMetaToNodeData(exifData, iptcData, vibrantData, imagePath) {
   const gps = { longitude: null, latitude: null };
 
   if (exifData) {
@@ -39,22 +89,7 @@ function transformMetaToNodeData(exifData, iptcData, vibrantData) {
     }
   }
 
-  const vbChroma = R.map((swatch) => (chroma(swatch.getRgb()))
-    , vibrantData);
-
-
-  if (chroma.contrast(vbChroma.DarkVibrant, vbChroma.Vibrant) < 4.5) {
-    // console.log('adjusting colors', chroma.contrast(vbChroma.DarkVibrant, vbChroma.Vibrant));
-    // console.log(vbChroma.DarkVibrant.hex());
-    // console.log(vbChroma.Vibrant.hex());
-    vbChroma.DarkVibrant = vbChroma.DarkVibrant.darken();
-    vbChroma.Vibrant = vbChroma.Vibrant.brighten();
-    // console.log('adjusted', chroma.contrast(vbChroma.DarkVibrant, vbChroma.Vibrant));
-    // console.log(vbChroma.DarkVibrant.hex());
-    // console.log(vbChroma.Vibrant.hex());
-  }
-
-  const vibrantRgb = R.map((color) => color.rgb(), vbChroma);
+  const vibrant = processColors(vibrantData, imagePath);
 
 
   return {
@@ -62,7 +97,7 @@ function transformMetaToNodeData(exifData, iptcData, vibrantData) {
     gps,
     dateTaken: exifData?.exif?.DateTimeOriginal,
     iptc: iptcData || undefined,
-    vibrant: vibrantRgb,
+    vibrant,
   };
 }
 
@@ -82,7 +117,7 @@ exports.onCreateNode = async function ({ node, getNode, actions }) {
     createNodeField({
       node,
       name: 'imageMeta',
-      value: transformMetaToNodeData(exifData, iptcData, vibrantData),
+      value: transformMetaToNodeData(exifData, iptcData, vibrantData, parent.absolutePath),
     });
   }
 };
diff --git a/package.json b/package.json
index 6a60f28..d8cf12b 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
     "babel-eslint": "^10.1.0",
     "babel-plugin-preval": "^5.0.0",
     "babel-plugin-styled-components": "^1.12.0",
+    "chalk": "^4.1.1",
     "chroma-js": "^2.1.2",
     "classnames": "^2.3.1",
     "eslint": "^7.28.0",
diff --git a/yarn.lock b/yarn.lock
index 94a7b74..5709c6d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3581,7 +3581,7 @@ chalk@^3.0.0:
     ansi-styles "^4.1.0"
     supports-color "^7.1.0"
 
-chalk@^4.0.0, chalk@^4.1.0:
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
   integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==