Skip to content

Commit

Permalink
feat: Add pickHexColorContrast (#10)
Browse files Browse the repository at this point in the history
* feat: Add pickHexColorContrast

* chore: Export pickHexColorContrast

* Update publish-rc.yml

* Create thin-dolls-shout.md
  • Loading branch information
Marabyte authored Aug 19, 2022
1 parent 722f51f commit 748504c
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/thin-dolls-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sardine/colour": minor
---

feat: Add `pickHexColorContrast` function
4 changes: 2 additions & 2 deletions .github/workflows/publish-rc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:

- name: Publish Skroll Release Candidate
run: |
npm version prerelease --preid rc.${{ github.event.number }}.${{ github.run_id }} --tag rc --no-git-tag-version
npm publish
npm version prerelease --preid rc.${{ github.event.number }}.${{ github.run_id }} --git-tag-version=false
npm publish --tag rc
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
13 changes: 9 additions & 4 deletions src/getSRGBLuminanceFromHex.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { convertHextoRGB } from "./converters.js";
import { linearRGB } from "./util/index.js";
import type { WCAG } from "./types";

/**
* Returns the relative luminance of a colour in the sRGB space.
Expand All @@ -12,11 +13,15 @@ import { linearRGB } from "./util/index.js";
* https://www.w3.org/WAI/GL/wiki/Relative_luminance
* @param colour an hexadecimal colour
*/
export const getSRGBLuminanceFromHex = (colour: string) => {
export const getSRGBLuminanceFromHex = (
colour: string,
standard?: WCAG
) => {
const isWCAG21 = standard === "WCAG2.1";
const rgbColor = convertHextoRGB(colour);
const r = linearRGB(rgbColor.R);
const g = linearRGB(rgbColor.G);
const b = linearRGB(rgbColor.B);
const r = linearRGB(rgbColor.R, isWCAG21);
const g = linearRGB(rgbColor.G, isWCAG21);
const b = linearRGB(rgbColor.B, isWCAG21);

return 0.2126 * r + 0.7152 * g + 0.0722 * b;
};
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export {
} from "./converters.js";
export { RGBdistance } from "./RGBdistance.js";
export { getSRGBLuminanceFromHex } from "./getSRGBLuminanceFromHex.js";
export { pickHexColorContrast } from "./pickHexColorContrast.js";
33 changes: 33 additions & 0 deletions src/pickHexColorContrast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { getSRGBLuminanceFromHex } from "./getSRGBLuminanceFromHex.js";
import type { WCAG } from "./types";

type ColorArgs = {
backgroundColour: string;
optionOneColour: string;
optionTwoColour: string;
}

const relativeContrast = (firstColour: number, secondColour: number) => {
if (firstColour > secondColour) {
return firstColour / secondColour;
}
return secondColour / firstColour;
}

export const pickHexColorContrast = (
{backgroundColour, optionOneColour, optionTwoColour}: ColorArgs,
standard: WCAG
): string => {
const backgroundColourLuminance = getSRGBLuminanceFromHex(backgroundColour, standard) + 0.05;
const optionOneColourLuminance = getSRGBLuminanceFromHex(optionOneColour, standard) + 0.05;
const optionTwoColourLuminance = getSRGBLuminanceFromHex(optionTwoColour, standard) + 0.05;

const optionOneContrast = relativeContrast(optionOneColourLuminance, backgroundColourLuminance);
const optionTwoContrast = relativeContrast(optionTwoColourLuminance, backgroundColourLuminance);

if (optionOneContrast > optionTwoContrast) {
return optionOneColour;
}
return optionTwoColour;

};
35 changes: 35 additions & 0 deletions src/tests/pickColorContrast.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import test from "ava";
import { pickHexColorContrast } from "../pickHexColorContrast.js";

test("should return #FFFFFF as the best colour for a #333333 background", ({
is,
}) => {
const colours = {
backgroundColour: "#333333",
optionOneColour: "#FFFFFF",
optionTwoColour: "#000000",
};
is(pickHexColorContrast(colours, "WCAG2.1"), "#FFFFFF");
});

test("should return #000000 as the best colour for a #BED background", ({
is,
}) => {
const colours = {
backgroundColour: "#BED",
optionOneColour: "#FFFFFF",
optionTwoColour: "#000000",
};
is(pickHexColorContrast(colours, "WCAG2.1"), "#000000");
});

test("should return #000000 as the best colour for a #DD337F background", ({
is,
}) => {
const colours = {
backgroundColour: "#DD337F",
optionOneColour: "#FFFFFF",
optionTwoColour: "#000000",
};
is(pickHexColorContrast(colours, "WCAG2.1"), "#000000");
});
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ export interface HueHelper {
}

export type ColourSpace = "sRGB";

export type WCAG = "WCAG2.1" | "WCAG3.0";
5 changes: 3 additions & 2 deletions src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ export const bigSquare = (n: number): number =>
* It takes a RGB channel in the range [0 - 255] and returns a value between 0 and 1
* @param rgbValue number to be normalised
*/
export function linearRGB(rgbValue: number) {
export function linearRGB(rgbValue: number, WCAG21?: boolean) {
const rgbRatio = rgbValue / 255;
const threshold = WCAG21 ? 0.03928 : 0.04045;
let linearValue: number;

if (rgbRatio > 0.04045) {
if (rgbRatio > threshold) {
linearValue = Math.pow((rgbRatio + 0.055) / 1.055, 2.4);
} else {
linearValue = rgbRatio / 12.92;
Expand Down

0 comments on commit 748504c

Please sign in to comment.