diff --git a/public/lang-docs.md b/public/lang-docs.md index 4d8c2353..0ab166c6 100644 --- a/public/lang-docs.md +++ b/public/lang-docs.md @@ -91,6 +91,7 @@ Description: Highly saturated light colors are not appropriate for palettes that Natural Language: ALL c in colors WHERE hsl.l(c) > 70, NOT hsl.s(c) > 70 + Program: ```json @@ -122,6 +123,7 @@ Description: Highly saturated light colors are not appropriate for palettes that Natural Language: ALL c in colors WHERE hsl.l(c) > 70, NOT hsl.s(c) > 70 + Program: ```json @@ -153,6 +155,7 @@ Description: Highly saturated light colors are not appropriate for palettes that Natural Language: ALL c in colors WHERE hsl.l(c) > 70, NOT hsl.s(c) > 70 + Program: ```json @@ -184,6 +187,7 @@ Description: Palettes that seek to be playful should have at least one light blu Natural Language: EXIST c in colors, (similar(c, #add8e6) < 20 or similar(c, #f5f5dc) < 20 or similar(c, #808080) < 20) + Program: ```json @@ -214,6 +218,7 @@ Description: Palettes that seek to be positive should not have dark reds or brow Natural Language: ALL c in colors, NOT (similar(c, #8b0000) < 20 or similar(c, #a52a2a) < 20) + Program: ```json @@ -245,6 +250,7 @@ Description: Palettes that seek to be negative should not have light colors, par Natural Language: ALL c in colors, NOT (similar(c, #008000) < 20 or lab.l(c) > 70) + Program: ```json @@ -278,6 +284,17 @@ Description: All colors in a palette should be differentiable by people with deu Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), NOT similar(cvdSim(a, deuteranopia), cvdSim(b, deuteranopia)) < 9 +Palettes that will fail this test: + +- #0078b4, #ff7e0e, #3d9f2f, #da2827, #8c69bc, #8e564b, #e179c1, #7f7f7f, #c4bc27, #00becf with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #00f with a #fff background + + Program: ```json @@ -311,6 +328,17 @@ Description: All colors in a palette should be differentiable by people with pro Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), NOT similar(cvdSim(a, protanopia), cvdSim(b, protanopia)) < 9 +Palettes that will fail this test: + +- #0078b4, #ff7e0e, #3d9f2f, #da2827, #8c69bc, #8e564b, #e179c1, #7f7f7f, #c4bc27, #00becf with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #00f with a #fff background + + Program: ```json @@ -344,6 +372,17 @@ Description: All colors in a palette should be differentiable by people with tri Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), NOT similar(cvdSim(a, tritanopia), cvdSim(b, tritanopia)) < 9 +Palettes that will fail this test: + +- #0078b4, #ff7e0e, #3d9f2f, #da2827, #8c69bc, #8e564b, #e179c1, #7f7f7f, #c4bc27, #00becf with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #00f with a #fff background + + Program: ```json @@ -377,6 +416,17 @@ Description: All colors in a palette should be differentiable by people with gra Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), NOT similar(cvdSim(a, grayscale), cvdSim(b, grayscale)) < 9 +Palettes that will fail this test: + +- #0078b4, #ff7e0e, #3d9f2f, #da2827, #8c69bc, #8e564b, #e179c1, #7f7f7f, #c4bc27, #00becf with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #00f with a #fff background + + Program: ```json @@ -410,6 +460,17 @@ Description: Do the colors stand out equally? A color palette is described as fa Natural Language: (extent(sort(colors, x => lch.l(x))) < 50 and extent(sort(colors, x => lch.c(x))) < 80) +Palettes that will fail this test: + +- #debdb5, #2a2a2a, #76fc00 with a #fff background + + + +Palettes that will pass this test: + +- #000 with a #fff background + + Program: ```json @@ -447,6 +508,17 @@ Description: Do the colors stand out equally? A color palette is described as fa Natural Language: (extent(sort(colors, x => lch.l(x))) < 50) +Palettes that will fail this test: + +- #debdb5, #2a2a2a, #76fc00 with a #fff background + + + +Palettes that will pass this test: + +- #000 with a #fff background + + Program: ```json @@ -476,6 +548,17 @@ Description: Background should be sufficiently desaturated. Natural Language: (((hsl.l(background) > 90 and hsv.s(background) < 8) or hsl.l(background) > 99) or (hsl.l(background) > 10 and hsl.l(background) < 26 and hsv.s(background) < 21)) +Palettes that will fail this test: + +- #0084a9, #009de5, #5fb1ff, #ecddff with a #000 background + + + +Palettes that will pass this test: + +- #0084a9, #009de5, #5fb1ff, #bbc3ff with a #f4e3e3 background + + Program: ```json @@ -527,6 +610,17 @@ Description: Tetradic palettes are hard to work with and are not recommended. Natural Language: NOT EXIST a in colors, (EXIST b in colors, similar(hsl.h(a), hsl.h(b) + 90) < 5 and EXIST b in colors, similar(hsl.h(a), hsl.h(b) + 90) < 5 and EXIST b in colors, similar(hsl.h(a), hsl.h(b) + 90) < 5) +Palettes that will fail this test: + +- #d23bae, #3b6dd2, #d89a35, #36d745 with a #fff background + + + +Palettes that will pass this test: + +- #0084a9, #009de5, #5fb1ff, #bbc3ff with a #fff background + + Program: ```json @@ -601,6 +695,17 @@ Description: When using green, make it a yellow or blue one. This makes it easie Natural Language: ALL a in colors, (hsl.h(a) < 90 or hsl.h(a) > 150) +Palettes that will fail this test: + +- #0084a9, #93e789 with a #fff background + + + +Palettes that will pass this test: + +- #bee38d, #bbc3ff with a #fff background + + Program: ```json @@ -634,6 +739,17 @@ Description: Don't make your colors too dark and saturated when you're using a b Natural Language: ((hsl.l(background) > 50 and ALL a in colors, contrast(a, background, WCAG21) < 10) or hsl.l(background) < 50) +Palettes that will fail this test: + +- #0e2d48, #3c828d, #b87930 with a #f9f9f9 background + + + +Palettes that will pass this test: + +- #f4b05d, #3c828d, #1c4b76 with a #f9f9f9 background + + Program: ```json @@ -677,6 +793,7 @@ Description: Use color complements whenever possible Natural Language: EXIST (a, b) in colors, similar(hsl.h(a), hsl.h(b) + 180) < 5 + Program: ```json @@ -709,6 +826,17 @@ Description: Pairs of colors in a palette should be differentiable from each oth Natural Language: ALL (x, y) in colors WHERE index(x) != index(y), (lab.l(x) absDiff lab.l(y) > 12.58 or lab.a(x) absDiff lab.a(y) > 20.740000000000002 or lab.b(x) absDiff lab.b(y) > 34.05) +Palettes that will fail this test: + +- #0084a9, #009de5, #8ca9fa with a #fff background + + + +Palettes that will pass this test: + +- #0084a9, #bad, #008000 with a #fff background + + Program: ```json @@ -761,6 +889,17 @@ Description: Pairs of colors in a palette should be differentiable from each oth Natural Language: ALL (x, y) in colors WHERE index(x) != index(y), (lab.l(x) absDiff lab.l(y) > 6.58 or lab.a(x) absDiff lab.a(y) > 8.42 or lab.b(x) absDiff lab.b(y) > 11.09) +Palettes that will fail this test: + +- #a77865, #468bbc, #bc6c6c, #a67873, #ff008c with a #fff background + + + +Palettes that will pass this test: + +- #cf5f67, #468bbc, #848475, #c55eab, #ff008c with a #fff background + + Program: ```json @@ -813,6 +952,17 @@ Description: Pairs of colors in a palette should be differentiable from each oth Natural Language: ALL (x, y) in colors WHERE index(x) != index(y), (lab.l(x) absDiff lab.l(y) > 5.83 or lab.a(x) absDiff lab.a(y) > 6.88 or lab.b(x) absDiff lab.b(y) > 8.219999999999999) +Palettes that will fail this test: + +- #a77865, #468bbc, #bc6c6c, #a67873, #ff008c with a #fff background + + + +Palettes that will pass this test: + +- #cf5f67, #468bbc, #848475, #c55eab, #ff008c with a #fff background + + Program: ```json @@ -865,6 +1015,17 @@ Description: Colors at either end of the lightness spectrum can be hard to discr Natural Language: ALL a in colors, ALL b in ([#000, #fff, #00f, #f00, #0f0]), NOT a == b +Palettes that will fail this test: + +- #000, #fff, #ff7e0e, #0f0, #0084a9, #00f with a #fff background + + + +Palettes that will pass this test: + +- #ff7e0e with a #fff background + + Program: ```json @@ -897,6 +1058,17 @@ Description: All colors in a palette should have a sufficient contrast ratio wit Natural Language: ALL a in colors, contrast(a, background, WCAG21) > 1.1 +Palettes that will fail this test: + +- #0ff, #00faff, #00e4ff, #fdfdfc, #0ff with a #fff background + + + +Palettes that will pass this test: + +- #cf5f67, #468bbc, #848475, #c55eab, #ff008c with a #fff background + + Program: ```json @@ -926,6 +1098,17 @@ Description: Opt for colors that are perceptually distinguishable in a logical s Natural Language: ALL (a, b) in colors WHERE index(a) == index(b) - 1, deltaE(a, b, 2000) > 10 +Palettes that will fail this test: + +- #0084a9, #009de5, #5fb1ff, #bbc3ff, #ecddff with a #fff background + + + +Palettes that will pass this test: + +- #0084a9, #009de5, #8ca9fa, #bbc3ff, #ecddff with a #fff background + + Program: ```json @@ -961,6 +1144,7 @@ Description: Categorical values should have an even distribution around the hue Natural Language: (std(speed(sort(colors, x => lch.h(x)), => )) < 10 or std(speed(sort(colors, x => lch.h(x) + 180 % 360), => )) < 10) + Program: ```json @@ -1013,6 +1197,17 @@ Description: Checks if the colors are in the sRGB gamut. This is important to en Natural Language: ALL a in colors, inGamut(a) == TRUE +Palettes that will fail this test: + +- #0087a5 with a #fff background + + + +Palettes that will pass this test: + +- #0084a9, #009de5, #8ca9fa, #f00 with a #fff background + + Program: ```json @@ -1039,6 +1234,17 @@ Description: Palettes should have a maximum number of colors. Higher numbers of Natural Language: count(colors) < 11 +Palettes that will fail this test: + +- #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000 with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #0f0, #00f with a #fff background + + Program: ```json @@ -1059,6 +1265,17 @@ Description: All colors in a palette should be different from each other. This i Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), dist(a, b, lab) > 15 +Palettes that will fail this test: + +- #d2b48c, #f5f5dc, #d7fcef with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #0f0, #00f with a #fff background + + Program: ```json @@ -1089,6 +1306,17 @@ Description: Being able to identify colors by name is important for usability an Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), name(a) != name(b) +Palettes that will fail this test: + +- #5260d1, #005ebe with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #0f0, #00f with a #fff background + + Program: ```json @@ -1116,6 +1344,17 @@ Description: Sequential palettes should be ordered by lightness. This is a defin Natural Language: (sort(colors, x => lch.l(x)) == map(colors, x => lch.l(x)) or sort(colors, x => lch.l(x)) == reverse(map(colors, x => lch.l(x)), => )) +Palettes that will fail this test: + +- #0084a9, #009de5, #5fb1ff, #ecddff, #bbc3ff with a #fff background + + + +Palettes that will pass this test: + +- #0084a9, #009de5, #5fb1ff, #bbc3ff, #ecddff with a #fff background + + Program: ```json @@ -1151,6 +1390,19 @@ Description: Colors that are close to what are known as ugly colors are sometime Natural Language: ALL a in colors, ALL b in ([#56ff00, #0010ff, #6a7e25, #ff00ef, #806e28]), deltaE(a, b, 2000) > 10 +Palettes that will fail this test: + +- #000, #56ff22 with a #fff background + +- #000, #0010ff, #6a7e25, #0f0, #00f with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #00c000, #9c70ff with a #fff background + + Program: ```json diff --git a/public/lint-schema.json b/public/lint-schema.json index 1d2cdaf9..b82ad3bf 100644 --- a/public/lint-schema.json +++ b/public/lint-schema.json @@ -218,6 +218,7 @@ { "additionalProperties": false, "properties": { + "$schema": {"type": "string"}, "==": { "additionalProperties": false, "properties": { @@ -270,7 +271,8 @@ }, "required": ["left", "right"], "type": "object" - } + }, + "$schema": {"type": "string"} }, "required": ["!="], "type": "object" @@ -278,6 +280,7 @@ { "additionalProperties": false, "properties": { + "$schema": {"type": "string"}, "<": { "additionalProperties": false, "properties": { @@ -294,6 +297,7 @@ { "additionalProperties": false, "properties": { + "$schema": {"type": "string"}, ">": { "additionalProperties": false, "properties": { @@ -310,22 +314,7 @@ { "additionalProperties": false, "properties": { - "absDiff": { - "additionalProperties": false, - "properties": { - "left" : {"$ref": "#/definitions/LintValue"}, - "right": {"$ref": "#/definitions/LintValue"} - }, - "required": ["left", "right"], - "type": "object" - } - }, - "required": ["absDiff"], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { + "$schema": {"type": "string"}, "similar": { "additionalProperties": false, "properties": { @@ -348,7 +337,8 @@ { "additionalProperties": false, "properties": { - "and": { "items": {"$ref": "#/definitions/LintExpression"}, "type": "array" } + "$schema": { "type": "string" }, + "and" : { "type": "array" , "items": {"$ref": "#/definitions/LintExpression"} } }, "required": ["and"], "type": "object" @@ -356,14 +346,15 @@ { "additionalProperties": false, "properties": { - "or": { "items": {"$ref": "#/definitions/LintExpression"}, "type": "array" } + "$schema": { "type": "string" }, + "or" : { "type": "array" , "items": {"$ref": "#/definitions/LintExpression"} } }, "required": ["or"], "type": "object" }, { "additionalProperties": false, - "properties": { "not": {"$ref": "#/definitions/LintExpression"} }, + "properties": { "$schema": {"type": "string"}, "not": {"$ref": "#/definitions/LintExpression"} }, "required": ["not"], "type": "object" } @@ -384,7 +375,7 @@ { "additionalProperties": false, "properties": { - "filter": {"$ref": "#/definitions/alias-1448761563-3695-3756-1448761563-0-6113"}, + "filter": {"$ref": "#/definitions/alias-1448761563-3904-3965-1448761563-0-6322"}, "func" : {"$ref": "#/definitions/LintExpression"} , "varb" : {"type": "string"} }, @@ -395,7 +386,7 @@ "additionalProperties": false, "properties": { "func": {"$ref": "#/definitions/LintValue"} , - "map" : {"$ref": "#/definitions/alias-1448761563-3695-3756-1448761563-0-6113"}, + "map" : {"$ref": "#/definitions/alias-1448761563-3904-3965-1448761563-0-6322"}, "varb": {"type": "string"} }, "required": ["map", "func", "varb"], @@ -404,7 +395,7 @@ { "additionalProperties": false, "properties": { - "reverse": {"$ref": "#/definitions/alias-1448761563-3695-3756-1448761563-0-6113"} + "reverse": {"$ref": "#/definitions/alias-1448761563-3904-3965-1448761563-0-6322"} }, "required": ["reverse"], "type": "object" @@ -413,7 +404,7 @@ "additionalProperties": false, "properties": { "func": {"$ref": "#/definitions/LintValue"} , - "sort": {"$ref": "#/definitions/alias-1448761563-3695-3756-1448761563-0-6113"}, + "sort": {"$ref": "#/definitions/alias-1448761563-3904-3965-1448761563-0-6322"}, "varb": {"type": "string"} }, "required": ["sort", "func", "varb"], @@ -421,7 +412,7 @@ }, { "additionalProperties": false, - "properties": { "speed": {"$ref": "#/definitions/alias-1448761563-3695-3756-1448761563-0-6113"} }, + "properties": { "speed": {"$ref": "#/definitions/alias-1448761563-3904-3965-1448761563-0-6322"} }, "required": ["speed"], "type": "object" } @@ -508,6 +499,22 @@ }, "required": ["%"], "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "absDiff": { + "additionalProperties": false, + "properties": { + "left" : {"$ref": "#/definitions/LintValue"}, + "right": {"$ref": "#/definitions/LintValue"} + }, + "required": ["left", "right"], + "type": "object" + } + }, + "required": ["absDiff"], + "type": "object" } ] }, @@ -596,6 +603,7 @@ { "additionalProperties": false, "properties": { + "$schema": {"type": "string"}, "all": { "anyOf": [ { @@ -649,6 +657,7 @@ { "additionalProperties": false, "properties": { + "$schema": {"type": "string"}, "exist": { "anyOf": [ { @@ -718,7 +727,7 @@ "description": "A LintValue is a JSON object that represents a value. It can be a string, a number, a boolean, a LintColor, a LintVariable, a LintMathOps, a LintPairOps, a LintAggregate, a LintColorFunction or a LintExpression" }, "LintVariable": {"type": "string"}, - "alias-1448761563-3695-3756-1448761563-0-6113": { + "alias-1448761563-3904-3965-1448761563-0-6322": { "anyOf": [ {"$ref": "#/definitions/LintVariable"} , { "items": {"$ref": "#/definitions/LintValue"}, "type": "array" }, diff --git a/src/lib/ColorLint.test.ts b/src/lib/ColorLint.test.ts index 39d8bf98..f71abe93 100644 --- a/src/lib/ColorLint.test.ts +++ b/src/lib/ColorLint.test.ts @@ -7,6 +7,8 @@ import { CreateCustomLint } from "./CustomLint"; import { suggestLintFix } from "./linter-tools/lint-fixer"; import { makePalFromString } from "./utils"; +import type { CustomLint } from "./CustomLint"; + // Lints import AvoidExtremes from "./lints/avoid-extremes"; import BackgroundContrast from "./lints/background-contrast"; @@ -24,148 +26,58 @@ import UglyColors from "./lints/ugly-colors"; const unique = (arr: T[]): T[] => [...new Set(arr)]; +function autoTest(lint: CustomLint) { + lint.expectedPassingTests.forEach((pal) => { + const newLint = CreateCustomLint(lint); + const exampleLint = new newLint(pal).run(); + expect(exampleLint.passes).toBe(true); + expect(exampleLint.message).toMatchSnapshot(); + }); + lint.expectedFailingTests.forEach((pal) => { + const newLint = CreateCustomLint(lint); + const exampleLint = new newLint(pal).run(); + expect(exampleLint.passes).toBe(false); + expect(exampleLint.message).toMatchSnapshot(); + }); + expect(lint.expectedFailingTests.length).toBeGreaterThan(0); + expect(lint.expectedPassingTests.length).toBeGreaterThan(0); +} + test("ColorLint - AvoidExtremes", () => { - const examplePal = makePalFromString([ - "#000000", - "#ffffff", - "#ff7e0e", - "#00ff00", - "#0084a9", - "#0000ff", - ]); - const newLint = CreateCustomLint(AvoidExtremes); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(false); - expect(exampleLint.message).toBe( - "Colors at either end of the lightness spectrum #000, #fff, #0f0, #00f are hard to discriminate in some contexts, and are sometimes advised against" - ); + autoTest(AvoidExtremes); }); test("ColorLint - MutuallyDistinct", () => { - const examplePal = makePalFromString([ - "#000000", - "#ffffff", - "#ff0000", - "#00ff00", - "#0000ff", - ]); - const newLint = CreateCustomLint(MutuallyDistinct); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - // TODO add a failing case - const examplePal2 = makePalFromString(["#d2b48c", "#f5f5dc", "#d7fcef"]); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(MutuallyDistinct); }); test("ColorLint - MaxColors", () => { - const examplePal = makePalFromString(["#000000"]); - const newLint = CreateCustomLint(MaxColors); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - const examplePal2 = makePalFromString( - [...new Array(20)].map(() => "#000000") - ); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(MaxColors); }); test("ColorLint - UglyColors", () => { - const examplePal = makePalFromString(["#000000"]); - const newLint = CreateCustomLint(UglyColors); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - const examplePal2 = makePalFromString(["#000000", "#56FF22"]); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(UglyColors); }); test("ColorLint - Fair Nominal", () => { - const examplePal = makePalFromString(["#000000"]); - const newLint = CreateCustomLint(Fair[0]); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - const examplePal2 = makePalFromString(["#debdb5", "#2a2a2a", "#76fc00"]); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(Fair[0]); }); test("ColorLint - Fair Sequential", () => { - const examplePal = makePalFromString(["#000000"]); - const newLint = CreateCustomLint(Fair[1]); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - const examplePal2 = makePalFromString(["#debdb5", "#2a2a2a", "#76fc00"]); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(Fair[1]); }); test("ColorLint - SequentialOrder", () => { - const examplePal = makePalFromString([ - "#0084a9", - "#009de5", - "#5fb1ff", - "#bbc3ff", - "#ecddff", - ]); - const newLint = CreateCustomLint(SequentialOrder); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - const examplePal2 = makePalFromString([ - "#0084a9", - "#009de5", - "#5fb1ff", - "#ecddff", - "#bbc3ff", - ]); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(SequentialOrder); }); test("ColorLint - CatOrderSimilarity", () => { - const examplePal = makePalFromString([ - "#0084a9", - "#009de5", - "#8ca9fa", - "#bbc3ff", - "#ecddff", - ]); - const newLint = CreateCustomLint(CatOrderSimilarity); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - const examplePal2 = makePalFromString([ - "#0084a9", - "#009de5", - "#5fb1ff", - "#bbc3ff", - "#ecddff", - ]); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(CatOrderSimilarity); }); test("ColorLint - ColorNameDiscriminability", async () => { + autoTest(ColorNameDiscriminability); + const examplePal = makePalFromString(["#5260d1", "#005ebe"]); const lint = CreateCustomLint(ColorNameDiscriminability); const exampleLint = new lint(examplePal).run(); @@ -183,19 +95,19 @@ test("ColorLint - ColorNameDiscriminability", async () => { }); test("ColorLint - SizeDiscrim (Thin)", () => { - const examplePal = makePalFromString(["#0084a9", "#bad", "#008000"]); - const newLint = CreateCustomLint(SizeDiscrims[0]); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); + autoTest(SizeDiscrims[0]); +}); - const examplePal2 = makePalFromString(["#0084a9", "#009de5", "#8ca9fa"]); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); +test("ColorLint - SizeDiscrim (Medium)", () => { + autoTest(SizeDiscrims[1]); +}); + +test("ColorLint - SizeDiscrim (Wide)", () => { + autoTest(SizeDiscrims[2]); }); test("ColorLint - Gamut", async () => { + autoTest(Gamut); const examplePal = makePalFromString(["lab(50.625% -91.737 -88.303)"]); const newLint = CreateCustomLint(Gamut); const exampleLint = new newLint(examplePal).run(); @@ -206,51 +118,19 @@ test("ColorLint - Gamut", async () => { expect(fix[0].colors.map((x) => x.toString())).toStrictEqual([ "lab(51.296% -23.327 -25.373)", ]); - - const examplePal2 = makePalFromString([ - "#0084a9", - "#009de5", - "#8ca9fa", - "#ff0000", - ]); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(true); - expect(exampleLint2.message).toMatchSnapshot(); }); -test("ColorLint - ColorBlind", async () => { - const tableau10 = [ - "#0078b4", - "#ff7e0e", - "#3d9f2f", - "#da2827", - "#8c69bc", - "#8e564b", - "#e179c1", - "#7f7f7f", - "#c4bc27", - "#00becf", - ]; - const examplePal = makePalFromString(tableau10); - const cbDeuteranopia = CreateCustomLint(ColorBlindness[0]); - const exampleLint1 = new cbDeuteranopia(examplePal).run(); - expect(exampleLint1.passes).toBe(false); - expect(exampleLint1.message).toMatchSnapshot(); - - const cbProtanopia = CreateCustomLint(ColorBlindness[1]); - const exampleLint2 = new cbProtanopia(examplePal).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); - - const cbTritanopia = CreateCustomLint(ColorBlindness[2]); - const exampleLint3 = new cbTritanopia(examplePal).run(); - expect(exampleLint3.passes).toBe(false); - expect(exampleLint3.message).toMatchSnapshot(); - - const cbGrayscale = CreateCustomLint(ColorBlindness[3]); - const exampleLint4 = new cbGrayscale(examplePal).run(); - expect(exampleLint4.passes).toBe(false); - expect(exampleLint4.message).toMatchSnapshot(); +test("ColorLint - ColorBlind: Deuteranopia", async () => { + autoTest(ColorBlindness[0]); +}); +test("ColorLint - ColorBlind: Protanopia", async () => { + autoTest(ColorBlindness[1]); +}); +test("ColorLint - ColorBlind: Tritanopia", async () => { + autoTest(ColorBlindness[2]); +}); +test("ColorLint - ColorBlind: Grayscale", async () => { + autoTest(ColorBlindness[3]); }); const ughWhat = ["#00ffff", "#00faff", "#00e4ff", "#fdfdfc", "#00ffff"]; @@ -273,84 +153,21 @@ test("ColorLint - Background Contrast", async () => { ); const fix2 = await suggestLintFix(examplePal, exampleLint2).then((x) => x[0]); expect(fix2.colors.map((x) => x.toHex())).toMatchSnapshot(); + autoTest(BackgroundContrast); }); test("ColorLint - MuthGuidelines (1) Background desaturation sufficient", () => { - const newLint = CreateCustomLint(MuthGuidelines[0]); - - const examplePal = makePalFromString([ - "#0084a9", - "#009de5", - "#5fb1ff", - "#bbc3ff", - ]); - examplePal.background = Color.colorFromHex("#f4e3e3", "lab"); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - const examplePal2 = makePalFromString([ - "#0084a9", - "#009de5", - "#5fb1ff", - "#ecddff", - ]); - examplePal2.background = Color.colorFromHex("#000", "lab"); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(MuthGuidelines[0]); }); test("ColorLint - MuthGuidelines (2) Avoid Tetradic Palettes", () => { - const newLint = CreateCustomLint(MuthGuidelines[1]); - - const examplePal = makePalFromString([ - "#0084a9", - "#009de5", - "#5fb1ff", - "#bbc3ff", - ]); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - const examplePal2 = makePalFromString([ - "#d23bae", - "#3b6dd2", - "#d89a35", - "#36d745", - ]); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(MuthGuidelines[1]); }); test("ColorLint - MuthGuidelines (3) Prefer yellowish or blueish greens", () => { - const newLint = CreateCustomLint(MuthGuidelines[2]); - - const examplePal = makePalFromString(["#bee38d", "#bbc3ff"]); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - const examplePal2 = makePalFromString(["#0084a9", "#93e789"]); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(MuthGuidelines[2]); }); test("ColorLint - MuthGuidelines (4) Avoid too much contrast with the background", () => { - const newLint = CreateCustomLint(MuthGuidelines[3]); - - const examplePal = makePalFromString(["#f4b05d", "#3c828d", "#1c4b76"]); - examplePal.background = Color.colorFromHex("#f9f9f9", "lab"); - const exampleLint = new newLint(examplePal).run(); - expect(exampleLint.passes).toBe(true); - expect(exampleLint.message).toMatchSnapshot(); - - const examplePal2 = makePalFromString(["#0e2d48", "#3c828d", "#b87930"]); - examplePal2.background = Color.colorFromHex("#f9f9f9", "lab"); - const exampleLint2 = new newLint(examplePal2).run(); - expect(exampleLint2.passes).toBe(false); - expect(exampleLint2.message).toMatchSnapshot(); + autoTest(MuthGuidelines[3]); }); diff --git a/src/lib/LintDocs.test.ts b/src/lib/LintDocs.test.ts index 86e2d5ec..b560f173 100644 --- a/src/lib/LintDocs.test.ts +++ b/src/lib/LintDocs.test.ts @@ -1,19 +1,44 @@ import { BUILT_INS } from "./linter"; import { prettyPrintLL } from "./lint-language/lint-language"; import { expect, test } from "vitest"; +import type { CustomLint } from "./CustomLint"; import fs from "fs/promises"; -async function buildDocs() { - const aiDocs = await fs.readFile("./src/lib/ai-docs.md", "utf-8"); +const testCaseToText = ( + tests: + | CustomLint["expectedFailingTests"] + | CustomLint["expectedPassingTests"], + type: "pass" | "fail" +) => { + if (tests.length === 0) { + return ""; + } + const testMsgs = tests + .map( + (x: any) => + `- ${x.colors + .map((y: any) => y.toHex()) + .join(", ")} with a ${x.background.toHex()} background` + ) + .join("\n\n"); + + return ` +Palettes that will ${type} this test: + +${testMsgs} + +`; +}; - const examples = BUILT_INS.map((lint) => { - return ` +function lintToText(lint: CustomLint) { + return ` ### ${lint.name} Description: ${lint.description} Natural Language: ${prettyPrintLL(JSON.parse(lint.program))} - +${testCaseToText(lint.expectedFailingTests || [], "fail")} +${testCaseToText(lint.expectedPassingTests || [], "pass")} Program: \`\`\`json @@ -21,8 +46,12 @@ ${lint.program} \`\`\` `; - }).join("\n\n\n"); +} + +async function buildDocs() { + const aiDocs = await fs.readFile("./src/lib/ai-docs.md", "utf-8"); + const examples = BUILT_INS.map(lintToText).join("\n\n\n"); const newDocs = `# Language Docs These docs provide a high-level overview of the language and its built-in functions. The first section covers the syntax of the language in its concrete JSON syntax, with a particular set of notes meant to guide LLM usage of the language. The second section provides all of the built in lints as examples. diff --git a/src/lib/__snapshots__/ColorLint.test.ts.snap b/src/lib/__snapshots__/ColorLint.test.ts.snap index 971a606d..1ef81b0d 100644 --- a/src/lib/__snapshots__/ColorLint.test.ts.snap +++ b/src/lib/__snapshots__/ColorLint.test.ts.snap @@ -1,5 +1,9 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`ColorLint - AvoidExtremes 1`] = `"Colors at either end of the lightness spectrum are hard to discriminate in some contexts, and are sometimes advised against"`; + +exports[`ColorLint - AvoidExtremes 2`] = `"Colors at either end of the lightness spectrum #000, #fff, #0f0, #00f are hard to discriminate in some contexts, and are sometimes advised against"`; + exports[`ColorLint - Background Contrast 1`] = ` [ "#0ff", @@ -20,17 +24,33 @@ exports[`ColorLint - Background Contrast 2`] = ` ] `; +exports[`ColorLint - Background Contrast 3`] = `"These colors () do not have a sufficient contrast ratio with the background and may be hard to discriminate in some contexts."`; + +exports[`ColorLint - Background Contrast 4`] = `"These colors (#fdfdfc) do not have a sufficient contrast ratio with the background and may be hard to discriminate in some contexts."`; + exports[`ColorLint - CatOrderSimilarity 1`] = `"Some sequences of colors are too similar based on dE scores: . Try reordering them or making them more distinguishable"`; exports[`ColorLint - CatOrderSimilarity 2`] = `"Some sequences of colors are too similar based on dE scores: #009de5 and #5fb1ff. Try reordering them or making them more distinguishable"`; -exports[`ColorLint - ColorBlind 1`] = `"This palette is not colorblind friendly for deuteranopia color blindness (ie can't see green). The following pairs are undifferentiable: (#0078b4 and #8c69bc, #ff7e0e and #c4bc27, #3d9f2f and #da2827, #e179c1 and #00becf)"`; +exports[`ColorLint - ColorBlind: Deuteranopia 1`] = `"This palette is not colorblind friendly for deuteranopia color blindness (ie can't see green). The following pairs are undifferentiable: ()"`; + +exports[`ColorLint - ColorBlind: Deuteranopia 2`] = `"This palette is not colorblind friendly for deuteranopia color blindness (ie can't see green). The following pairs are undifferentiable: (#0078b4 and #8c69bc, #ff7e0e and #c4bc27, #3d9f2f and #da2827, #e179c1 and #00becf)"`; + +exports[`ColorLint - ColorBlind: Grayscale 1`] = `"This palette may not work in black and white. The following pairs are hard to tell the difference between: ()"`; + +exports[`ColorLint - ColorBlind: Grayscale 2`] = `"This palette may not work in black and white. The following pairs are hard to tell the difference between: (#0078b4 and #da2827, #0078b4 and #8c69bc, #0078b4 and #8e564b, #0078b4 and #7f7f7f, #ff7e0e and #3d9f2f, #ff7e0e and #e179c1, #ff7e0e and #c4bc27, #ff7e0e and #00becf, #3d9f2f and #8c69bc, #3d9f2f and #e179c1, #3d9f2f and #7f7f7f, #da2827 and #8c69bc, #da2827 and #8e564b, #da2827 and #7f7f7f, #8c69bc and #8e564b, #8c69bc and #7f7f7f, #e179c1 and #c4bc27, #e179c1 and #00becf, #c4bc27 and #00becf)"`; + +exports[`ColorLint - ColorBlind: Protanopia 1`] = `"This palette is not colorblind friendly for protanopia color blindness (ie can't see red). The following pairs are undifferentiable: ()"`; -exports[`ColorLint - ColorBlind 2`] = `"This palette is not colorblind friendly for protanopia color blindness (ie can't see red). The following pairs are undifferentiable: (#0078b4 and #8c69bc, #ff7e0e and #3d9f2f, #da2827 and #8e564b)"`; +exports[`ColorLint - ColorBlind: Protanopia 2`] = `"This palette is not colorblind friendly for protanopia color blindness (ie can't see red). The following pairs are undifferentiable: (#0078b4 and #8c69bc, #ff7e0e and #3d9f2f, #da2827 and #8e564b)"`; -exports[`ColorLint - ColorBlind 3`] = `"This palette is not colorblind friendly for tritanopia color blindness (ie can't see blue). The following pairs are undifferentiable: (#ff7e0e and #e179c1, #8c69bc and #7f7f7f)"`; +exports[`ColorLint - ColorBlind: Tritanopia 1`] = `"This palette is not colorblind friendly for tritanopia color blindness (ie can't see blue). The following pairs are undifferentiable: ()"`; -exports[`ColorLint - ColorBlind 4`] = `"This palette may not work in black and white. The following pairs are hard to tell the difference between: (#0078b4 and #da2827, #0078b4 and #8c69bc, #0078b4 and #8e564b, #0078b4 and #7f7f7f, #ff7e0e and #3d9f2f, #ff7e0e and #e179c1, #ff7e0e and #c4bc27, #ff7e0e and #00becf, #3d9f2f and #8c69bc, #3d9f2f and #e179c1, #3d9f2f and #7f7f7f, #da2827 and #8c69bc, #da2827 and #8e564b, #da2827 and #7f7f7f, #8c69bc and #8e564b, #8c69bc and #7f7f7f, #e179c1 and #c4bc27, #e179c1 and #00becf, #c4bc27 and #00becf)"`; +exports[`ColorLint - ColorBlind: Tritanopia 2`] = `"This palette is not colorblind friendly for tritanopia color blindness (ie can't see blue). The following pairs are undifferentiable: (#ff7e0e and #e179c1, #8c69bc and #7f7f7f)"`; + +exports[`ColorLint - ColorNameDiscriminability 1`] = `"The following pairs of colors have the same name: "`; + +exports[`ColorLint - ColorNameDiscriminability 2`] = `"The following pairs of colors have the same name: #5260d1 and #005ebe"`; exports[`ColorLint - Fair Nominal 1`] = `"This palette is unfair (meaning that some values may unduely stand out). Note that this check is naturally at odds with color blind friendly palettes. Maximum chroma range: 80, maximum luminance range: 50."`; @@ -40,9 +60,11 @@ exports[`ColorLint - Fair Sequential 1`] = `"This palette is unfair (meaning tha exports[`ColorLint - Fair Sequential 2`] = `"This palette is unfair (meaning that some values may unduely stand out). Note that this check is naturally at odds with color blind friendly palettes. Maximum chroma range: 50."`; -exports[`ColorLint - Gamut 1`] = `"A color or colors is not in the sRGB gamut (#0087a5). Please adjust the color so that it can be displayed on most devices."`; +exports[`ColorLint - Gamut 1`] = `"A color or colors is not in the sRGB gamut (). Please adjust the color so that it can be displayed on most devices."`; -exports[`ColorLint - Gamut 2`] = `"A color or colors is not in the sRGB gamut (). Please adjust the color so that it can be displayed on most devices."`; +exports[`ColorLint - Gamut 2`] = `"A color or colors is not in the sRGB gamut (#0087a5). Please adjust the color so that it can be displayed on most devices."`; + +exports[`ColorLint - Gamut 3`] = `"A color or colors is not in the sRGB gamut (#0087a5). Please adjust the color so that it can be displayed on most devices."`; exports[`ColorLint - MaxColors 1`] = `"This palette has too many colors and may be hard to discriminate in some contexts. Maximum: 10."`; @@ -72,10 +94,20 @@ exports[`ColorLint - SequentialOrder 1`] = `"This pal should be ordered by light exports[`ColorLint - SequentialOrder 2`] = `"This pal should be ordered by lightness if being used as a sequential palette. #ecddff, #bbc3ff may be to blame."`; +exports[`ColorLint - SizeDiscrim (Medium) 1`] = `"This palette has some colors () that are close to each other in perceptual space and will not be resolvable for Medium areas. This involves elements like medium blocks such as bars in a bar chart or small graphics"`; + +exports[`ColorLint - SizeDiscrim (Medium) 2`] = `"This palette has some colors (#a77865 and #a67873) that are close to each other in perceptual space and will not be resolvable for Medium areas. This involves elements like medium blocks such as bars in a bar chart or small graphics"`; + exports[`ColorLint - SizeDiscrim (Thin) 1`] = `"This palette has some colors () that are close to each other in perceptual space and will not be resolvable for Thin areas. This involves elements like small blocks such as small circles or lines"`; exports[`ColorLint - SizeDiscrim (Thin) 2`] = `"This palette has some colors (#0084a9 and #009de5) that are close to each other in perceptual space and will not be resolvable for Thin areas. This involves elements like small blocks such as small circles or lines"`; +exports[`ColorLint - SizeDiscrim (Wide) 1`] = `"This palette has some colors () that are close to each other in perceptual space and will not be resolvable for Wide areas. This involves elements like large blocks of color such as backgrounds or countries on a map"`; + +exports[`ColorLint - SizeDiscrim (Wide) 2`] = `"This palette has some colors (#a77865 and #a67873) that are close to each other in perceptual space and will not be resolvable for Wide areas. This involves elements like large blocks of color such as backgrounds or countries on a map"`; + exports[`ColorLint - UglyColors 1`] = `"This palette has some colors (specifically ) that are close to what are known as ugly colors"`; exports[`ColorLint - UglyColors 2`] = `"This palette has some colors (specifically #56ff22) that are close to what are known as ugly colors"`; + +exports[`ColorLint - UglyColors 3`] = `"This palette has some colors (specifically #0010ff, #6a7e25, #0f0, #00f) that are close to what are known as ugly colors"`; diff --git a/src/lib/__snapshots__/LintDocs.test.ts.snap b/src/lib/__snapshots__/LintDocs.test.ts.snap index 59b5166e..64f6a621 100644 --- a/src/lib/__snapshots__/LintDocs.test.ts.snap +++ b/src/lib/__snapshots__/LintDocs.test.ts.snap @@ -94,6 +94,7 @@ Description: Highly saturated light colors are not appropriate for palettes that Natural Language: ALL c in colors WHERE hsl.l(c) > 70, NOT hsl.s(c) > 70 + Program: \`\`\`json @@ -125,6 +126,7 @@ Description: Highly saturated light colors are not appropriate for palettes that Natural Language: ALL c in colors WHERE hsl.l(c) > 70, NOT hsl.s(c) > 70 + Program: \`\`\`json @@ -156,6 +158,7 @@ Description: Highly saturated light colors are not appropriate for palettes that Natural Language: ALL c in colors WHERE hsl.l(c) > 70, NOT hsl.s(c) > 70 + Program: \`\`\`json @@ -187,6 +190,7 @@ Description: Palettes that seek to be playful should have at least one light blu Natural Language: EXIST c in colors, (similar(c, #add8e6) < 20 or similar(c, #f5f5dc) < 20 or similar(c, #808080) < 20) + Program: \`\`\`json @@ -217,6 +221,7 @@ Description: Palettes that seek to be positive should not have dark reds or brow Natural Language: ALL c in colors, NOT (similar(c, #8b0000) < 20 or similar(c, #a52a2a) < 20) + Program: \`\`\`json @@ -248,6 +253,7 @@ Description: Palettes that seek to be negative should not have light colors, par Natural Language: ALL c in colors, NOT (similar(c, #008000) < 20 or lab.l(c) > 70) + Program: \`\`\`json @@ -281,6 +287,17 @@ Description: All colors in a palette should be differentiable by people with deu Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), NOT similar(cvdSim(a, deuteranopia), cvdSim(b, deuteranopia)) < 9 +Palettes that will fail this test: + +- #0078b4, #ff7e0e, #3d9f2f, #da2827, #8c69bc, #8e564b, #e179c1, #7f7f7f, #c4bc27, #00becf with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #00f with a #fff background + + Program: \`\`\`json @@ -314,6 +331,17 @@ Description: All colors in a palette should be differentiable by people with pro Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), NOT similar(cvdSim(a, protanopia), cvdSim(b, protanopia)) < 9 +Palettes that will fail this test: + +- #0078b4, #ff7e0e, #3d9f2f, #da2827, #8c69bc, #8e564b, #e179c1, #7f7f7f, #c4bc27, #00becf with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #00f with a #fff background + + Program: \`\`\`json @@ -347,6 +375,17 @@ Description: All colors in a palette should be differentiable by people with tri Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), NOT similar(cvdSim(a, tritanopia), cvdSim(b, tritanopia)) < 9 +Palettes that will fail this test: + +- #0078b4, #ff7e0e, #3d9f2f, #da2827, #8c69bc, #8e564b, #e179c1, #7f7f7f, #c4bc27, #00becf with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #00f with a #fff background + + Program: \`\`\`json @@ -380,6 +419,17 @@ Description: All colors in a palette should be differentiable by people with gra Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), NOT similar(cvdSim(a, grayscale), cvdSim(b, grayscale)) < 9 +Palettes that will fail this test: + +- #0078b4, #ff7e0e, #3d9f2f, #da2827, #8c69bc, #8e564b, #e179c1, #7f7f7f, #c4bc27, #00becf with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #00f with a #fff background + + Program: \`\`\`json @@ -413,6 +463,17 @@ Description: Do the colors stand out equally? A color palette is described as fa Natural Language: (extent(sort(colors, x => lch.l(x))) < 50 and extent(sort(colors, x => lch.c(x))) < 80) +Palettes that will fail this test: + +- #debdb5, #2a2a2a, #76fc00 with a #fff background + + + +Palettes that will pass this test: + +- #000 with a #fff background + + Program: \`\`\`json @@ -450,6 +511,17 @@ Description: Do the colors stand out equally? A color palette is described as fa Natural Language: (extent(sort(colors, x => lch.l(x))) < 50) +Palettes that will fail this test: + +- #debdb5, #2a2a2a, #76fc00 with a #fff background + + + +Palettes that will pass this test: + +- #000 with a #fff background + + Program: \`\`\`json @@ -479,6 +551,17 @@ Description: Background should be sufficiently desaturated. Natural Language: (((hsl.l(background) > 90 and hsv.s(background) < 8) or hsl.l(background) > 99) or (hsl.l(background) > 10 and hsl.l(background) < 26 and hsv.s(background) < 21)) +Palettes that will fail this test: + +- #0084a9, #009de5, #5fb1ff, #ecddff with a #000 background + + + +Palettes that will pass this test: + +- #0084a9, #009de5, #5fb1ff, #bbc3ff with a #f4e3e3 background + + Program: \`\`\`json @@ -530,6 +613,17 @@ Description: Tetradic palettes are hard to work with and are not recommended. Natural Language: NOT EXIST a in colors, (EXIST b in colors, similar(hsl.h(a), hsl.h(b) + 90) < 5 and EXIST b in colors, similar(hsl.h(a), hsl.h(b) + 90) < 5 and EXIST b in colors, similar(hsl.h(a), hsl.h(b) + 90) < 5) +Palettes that will fail this test: + +- #d23bae, #3b6dd2, #d89a35, #36d745 with a #fff background + + + +Palettes that will pass this test: + +- #0084a9, #009de5, #5fb1ff, #bbc3ff with a #fff background + + Program: \`\`\`json @@ -604,6 +698,17 @@ Description: When using green, make it a yellow or blue one. This makes it easie Natural Language: ALL a in colors, (hsl.h(a) < 90 or hsl.h(a) > 150) +Palettes that will fail this test: + +- #0084a9, #93e789 with a #fff background + + + +Palettes that will pass this test: + +- #bee38d, #bbc3ff with a #fff background + + Program: \`\`\`json @@ -637,6 +742,17 @@ Description: Don't make your colors too dark and saturated when you're using a b Natural Language: ((hsl.l(background) > 50 and ALL a in colors, contrast(a, background, WCAG21) < 10) or hsl.l(background) < 50) +Palettes that will fail this test: + +- #0e2d48, #3c828d, #b87930 with a #f9f9f9 background + + + +Palettes that will pass this test: + +- #f4b05d, #3c828d, #1c4b76 with a #f9f9f9 background + + Program: \`\`\`json @@ -680,6 +796,7 @@ Description: Use color complements whenever possible Natural Language: EXIST (a, b) in colors, similar(hsl.h(a), hsl.h(b) + 180) < 5 + Program: \`\`\`json @@ -712,6 +829,17 @@ Description: Pairs of colors in a palette should be differentiable from each oth Natural Language: ALL (x, y) in colors WHERE index(x) != index(y), (lab.l(x) absDiff lab.l(y) > 12.58 or lab.a(x) absDiff lab.a(y) > 20.740000000000002 or lab.b(x) absDiff lab.b(y) > 34.05) +Palettes that will fail this test: + +- #0084a9, #009de5, #8ca9fa with a #fff background + + + +Palettes that will pass this test: + +- #0084a9, #bad, #008000 with a #fff background + + Program: \`\`\`json @@ -764,6 +892,17 @@ Description: Pairs of colors in a palette should be differentiable from each oth Natural Language: ALL (x, y) in colors WHERE index(x) != index(y), (lab.l(x) absDiff lab.l(y) > 6.58 or lab.a(x) absDiff lab.a(y) > 8.42 or lab.b(x) absDiff lab.b(y) > 11.09) +Palettes that will fail this test: + +- #a77865, #468bbc, #bc6c6c, #a67873, #ff008c with a #fff background + + + +Palettes that will pass this test: + +- #cf5f67, #468bbc, #848475, #c55eab, #ff008c with a #fff background + + Program: \`\`\`json @@ -816,6 +955,17 @@ Description: Pairs of colors in a palette should be differentiable from each oth Natural Language: ALL (x, y) in colors WHERE index(x) != index(y), (lab.l(x) absDiff lab.l(y) > 5.83 or lab.a(x) absDiff lab.a(y) > 6.88 or lab.b(x) absDiff lab.b(y) > 8.219999999999999) +Palettes that will fail this test: + +- #a77865, #468bbc, #bc6c6c, #a67873, #ff008c with a #fff background + + + +Palettes that will pass this test: + +- #cf5f67, #468bbc, #848475, #c55eab, #ff008c with a #fff background + + Program: \`\`\`json @@ -868,6 +1018,17 @@ Description: Colors at either end of the lightness spectrum can be hard to discr Natural Language: ALL a in colors, ALL b in ([#000, #fff, #00f, #f00, #0f0]), NOT a == b +Palettes that will fail this test: + +- #000, #fff, #ff7e0e, #0f0, #0084a9, #00f with a #fff background + + + +Palettes that will pass this test: + +- #ff7e0e with a #fff background + + Program: \`\`\`json @@ -900,6 +1061,17 @@ Description: All colors in a palette should have a sufficient contrast ratio wit Natural Language: ALL a in colors, contrast(a, background, WCAG21) > 1.1 +Palettes that will fail this test: + +- #0ff, #00faff, #00e4ff, #fdfdfc, #0ff with a #fff background + + + +Palettes that will pass this test: + +- #cf5f67, #468bbc, #848475, #c55eab, #ff008c with a #fff background + + Program: \`\`\`json @@ -929,6 +1101,17 @@ Description: Opt for colors that are perceptually distinguishable in a logical s Natural Language: ALL (a, b) in colors WHERE index(a) == index(b) - 1, deltaE(a, b, 2000) > 10 +Palettes that will fail this test: + +- #0084a9, #009de5, #5fb1ff, #bbc3ff, #ecddff with a #fff background + + + +Palettes that will pass this test: + +- #0084a9, #009de5, #8ca9fa, #bbc3ff, #ecddff with a #fff background + + Program: \`\`\`json @@ -964,6 +1147,7 @@ Description: Categorical values should have an even distribution around the hue Natural Language: (std(speed(sort(colors, x => lch.h(x)), => )) < 10 or std(speed(sort(colors, x => lch.h(x) + 180 % 360), => )) < 10) + Program: \`\`\`json @@ -1016,6 +1200,17 @@ Description: Checks if the colors are in the sRGB gamut. This is important to en Natural Language: ALL a in colors, inGamut(a) == TRUE +Palettes that will fail this test: + +- #0087a5 with a #fff background + + + +Palettes that will pass this test: + +- #0084a9, #009de5, #8ca9fa, #f00 with a #fff background + + Program: \`\`\`json @@ -1042,6 +1237,17 @@ Description: Palettes should have a maximum number of colors. Higher numbers of Natural Language: count(colors) < 11 +Palettes that will fail this test: + +- #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000, #000 with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #0f0, #00f with a #fff background + + Program: \`\`\`json @@ -1062,6 +1268,17 @@ Description: All colors in a palette should be different from each other. This i Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), dist(a, b, lab) > 15 +Palettes that will fail this test: + +- #d2b48c, #f5f5dc, #d7fcef with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #0f0, #00f with a #fff background + + Program: \`\`\`json @@ -1092,6 +1309,17 @@ Description: Being able to identify colors by name is important for usability an Natural Language: ALL (a, b) in colors WHERE index(a) != index(b), name(a) != name(b) +Palettes that will fail this test: + +- #5260d1, #005ebe with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #0f0, #00f with a #fff background + + Program: \`\`\`json @@ -1119,6 +1347,17 @@ Description: Sequential palettes should be ordered by lightness. This is a defin Natural Language: (sort(colors, x => lch.l(x)) == map(colors, x => lch.l(x)) or sort(colors, x => lch.l(x)) == reverse(map(colors, x => lch.l(x)), => )) +Palettes that will fail this test: + +- #0084a9, #009de5, #5fb1ff, #ecddff, #bbc3ff with a #fff background + + + +Palettes that will pass this test: + +- #0084a9, #009de5, #5fb1ff, #bbc3ff, #ecddff with a #fff background + + Program: \`\`\`json @@ -1154,6 +1393,19 @@ Description: Colors that are close to what are known as ugly colors are sometime Natural Language: ALL a in colors, ALL b in ([#56ff00, #0010ff, #6a7e25, #ff00ef, #806e28]), deltaE(a, b, 2000) > 10 +Palettes that will fail this test: + +- #000, #56ff22 with a #fff background + +- #000, #0010ff, #6a7e25, #0f0, #00f with a #fff background + + + +Palettes that will pass this test: + +- #000, #fff, #f00, #00c000, #9c70ff with a #fff background + + Program: \`\`\`json diff --git a/src/lib/lint-language/lint-type.ts b/src/lib/lint-language/lint-type.ts index 44cdedc9..6cebd6ca 100644 --- a/src/lib/lint-language/lint-type.ts +++ b/src/lib/lint-language/lint-type.ts @@ -18,9 +18,9 @@ export type LintExpression = * A logical conjunction expression. It is used to express the logical AND, OR and NOT operations. */ export type LintConjunction = - | { and: LintExpression[] } - | { or: LintExpression[] } - | { not: LintExpression }; + | { $schema?: string; and: LintExpression[] } + | { $schema?: string; or: LintExpression[] } + | { $schema?: string; not: LintExpression }; type LintQuantifierBase = | { varbs: LintVariable[]; @@ -51,8 +51,8 @@ type LintQuantifierBase = * A logical quantifier expression. It is used to express the existence of a value (exist) or the existence of a value for all elements in a collection (all). */ export type LintQuantifier = - | { all: LintQuantifierBase } - | { exist: LintQuantifierBase }; + | { $schema?: string; all: LintQuantifierBase } + | { $schema?: string; exist: LintQuantifierBase }; // Operations /** @@ -60,27 +60,32 @@ export type LintQuantifier = */ export type LintComparison = | { + $schema?: string; "==": { left: LintValue | LintArrayValue; right: LintValue | LintArrayValue; }; } | { + $schema?: string; "!=": { left: LintValue | LintArrayValue; right: LintValue | LintArrayValue; }; } - | { "<": { left: LintValue; right: LintValue } } - | { ">": { left: LintValue; right: LintValue } } - | { absDiff: { left: LintValue; right: LintValue } } - | { similar: { left: LintValue; right: LintValue; threshold: number } }; + | { $schema?: string; "<": { left: LintValue; right: LintValue } } + | { $schema?: string; ">": { left: LintValue; right: LintValue } } + | { + $schema?: string; + similar: { left: LintValue; right: LintValue; threshold: number }; + }; export type LintMathOps = | { "+": { left: LintValue; right: LintValue } } | { "-": { left: LintValue; right: LintValue } } | { "*": { left: LintValue; right: LintValue } } | { "/": { left: LintValue; right: LintValue } } - | { "%": { left: LintValue; right: LintValue } }; + | { "%": { left: LintValue; right: LintValue } } + | { absDiff: { left: LintValue; right: LintValue } }; export type LintPairOps = | LintPairOpsDist diff --git a/src/lib/lints/affects.ts b/src/lib/lints/affects.ts index 1bb8bf8d..a7334e24 100644 --- a/src/lib/lints/affects.ts +++ b/src/lib/lints/affects.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; // "Highly saturated light colors will not be appropriate for SERIOUS/TRUST/CALM": ALL (FILTER colors c, lab(c) > threshold) b, NOT hsl(b) > threshold diff --git a/src/lib/lints/background-contrast.ts b/src/lib/lints/background-contrast.ts index a640af91..7a2021d3 100644 --- a/src/lib/lints/background-contrast.ts +++ b/src/lib/lints/background-contrast.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; import { Color } from "../Color"; import type { LintFixer } from "../linter-tools/lint-fixer"; @@ -38,6 +38,12 @@ const lint: CustomLint = { id: "background-contrast-built-in", blameMode: "single" as const, subscribedFix: "fixBackgroundDifferentiability", + expectedFailingTests: [ + makePalFromString(["#00ffff", "#00faff", "#00e4ff", "#fdfdfc", "#00ffff"]), + ], + expectedPassingTests: [ + makePalFromString(["#cf5f67", "#468bbc", "#848475", "#c55eab", "#ff008c"]), + ], }; export default lint; diff --git a/src/lib/lints/cat-order-similarity.ts b/src/lib/lints/cat-order-similarity.ts index dd4a6896..f30d4cd7 100644 --- a/src/lib/lints/cat-order-similarity.ts +++ b/src/lib/lints/cat-order-similarity.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; const lint: CustomLint = { @@ -31,5 +31,11 @@ const lint: CustomLint = { failMessage: `Some sequences of colors are too similar based on dE scores: {{blame}}. Try reordering them or making them more distinguishable`, id: "cat-order-similarity-built-in", blameMode: "pair", + expectedPassingTests: [ + makePalFromString(["#0084a9", "#009de5", "#8ca9fa", "#bbc3ff", "#ecddff"]), + ], + expectedFailingTests: [ + makePalFromString(["#0084a9", "#009de5", "#5fb1ff", "#bbc3ff", "#ecddff"]), + ], }; export default lint; diff --git a/src/lib/lints/color-blindness.ts b/src/lib/lints/color-blindness.ts index a52cd570..610a24cc 100644 --- a/src/lib/lints/color-blindness.ts +++ b/src/lib/lints/color-blindness.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; // old algorithm - https://github.dev/gka/palettes @@ -18,6 +18,18 @@ const blindnessLabels = { const blindTypes = Object.keys( blindnessLabels ) as (keyof typeof blindnessLabels)[]; +const tableau10 = [ + "#0078b4", + "#ff7e0e", + "#3d9f2f", + "#da2827", + "#8c69bc", + "#8e564b", + "#e179c1", + "#7f7f7f", + "#c4bc27", + "#00becf", +]; const lints: CustomLint[] = blindTypes.map((type) => ({ program: JSONToPrettyString({ // @ts-ignore @@ -51,5 +63,9 @@ const lints: CustomLint[] = blindTypes.map((type) => ({ : `This palette is not colorblind friendly for ${type} color blindness ${blindnessLabels[type]}. The following pairs are undifferentiable: ({{blame}})`, id: `colorblind-friendly-${type}-built-in`, blameMode: "pair" as const, + expectedPassingTests: [ + makePalFromString(["#000000", "#ffffff", "#ff0000", "#0000ff"]), + ], + expectedFailingTests: [makePalFromString(tableau10)], })); export default lints; diff --git a/src/lib/lints/discriminative-power.ts b/src/lib/lints/discriminative-power.ts index 25c1f8f3..9cfd0f3a 100644 --- a/src/lib/lints/discriminative-power.ts +++ b/src/lib/lints/discriminative-power.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; import type { LintFixer } from "../linter-tools/lint-fixer"; diff --git a/src/lib/lints/even-distribution.ts b/src/lib/lints/even-distribution.ts index 7cf8ea10..28f5853c 100644 --- a/src/lib/lints/even-distribution.ts +++ b/src/lib/lints/even-distribution.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; const lint: CustomLint = { diff --git a/src/lib/lints/fair.ts b/src/lib/lints/fair.ts index 86083eee..9e18b0d7 100644 --- a/src/lib/lints/fair.ts +++ b/src/lib/lints/fair.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; // magic numbers supplied by the paper @@ -34,6 +34,8 @@ const FairNominal: CustomLint = { failMessage: `${failMsgBase} Maximum chroma range: ${cRangeUnfair}, maximum luminance range: ${lRangeUnfair}.`, id: "fair-nominal-built-in", blameMode: "single", + expectedPassingTests: [makePalFromString(["#000000"])], + expectedFailingTests: [makePalFromString(["#debdb5", "#2a2a2a", "#76fc00"])], }; const FairSequential: CustomLint = { ...FairNominal, @@ -47,5 +49,7 @@ const FairSequential: CustomLint = { id: "fair-sequential-built-in", description: "Do the colors stand out equally? A color palette is described as fair if the luminance ranges are below a certain threshold and unfair if one of them is above a certain threshold. ", + expectedPassingTests: [makePalFromString(["#000000"])], + expectedFailingTests: [makePalFromString(["#debdb5", "#2a2a2a", "#76fc00"])], }; export default [FairNominal, FairSequential]; diff --git a/src/lib/lints/in-gamut.ts b/src/lib/lints/in-gamut.ts index e344a197..d8e519b1 100644 --- a/src/lib/lints/in-gamut.ts +++ b/src/lib/lints/in-gamut.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; import type { LintFixer } from "../linter-tools/lint-fixer"; import { clipToGamut } from "../utils"; @@ -23,6 +23,10 @@ const lint: CustomLint = { id: "gamut-check-built-in", blameMode: "single", subscribedFix: "fixGamut", + expectedPassingTests: [ + makePalFromString(["#0084a9", "#009de5", "#8ca9fa", "#ff0000"]), + ], + expectedFailingTests: [makePalFromString(["lab(50.625% -91.737 -88.303)"])], }; export default lint; diff --git a/src/lib/lints/max-colors.ts b/src/lib/lints/max-colors.ts index 1fec7e17..408de27c 100644 --- a/src/lib/lints/max-colors.ts +++ b/src/lib/lints/max-colors.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; import type { LintFixer } from "../linter-tools/lint-fixer"; @@ -18,6 +18,12 @@ const lint: CustomLint = { id: "too-many-colors-built-in", blameMode: "single", subscribedFix: "fixMaxColors", + expectedPassingTests: [ + makePalFromString(["#000000", "#ffffff", "#ff0000", "#00ff00", "#0000ff"]), + ], + expectedFailingTests: [ + makePalFromString([...new Array(20)].map(() => "#000000")), + ], }; export default lint; diff --git a/src/lib/lints/muth-guidelines.ts b/src/lib/lints/muth-guidelines.ts index 225f59cd..0aa4017a 100644 --- a/src/lib/lints/muth-guidelines.ts +++ b/src/lib/lints/muth-guidelines.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; const lints: CustomLint[] = []; @@ -42,6 +42,12 @@ const bgDeSaturated: CustomLint = { failMessage: `Colorful backgrounds can seem like a good idea, but they can easily distract from your data and they limit your potential color palette. Desaturated colors are your best bet. Roughly, you want either HSL.L > 90 and HSV.S < 8 for light backgrounds, or you want 10 < HSL.L < 25 and HSV.S < 20 for dark backgrounds. See "https://blog.datawrapper.de/beautifulcolors/#12" for more`, id: `background-de-saturation-built-in`, blameMode: "none", + expectedPassingTests: [ + makePalFromString(["#0084a9", "#009de5", "#5fb1ff", "#bbc3ff"], "#f4e3e3"), + ], + expectedFailingTests: [ + makePalFromString(["#0084a9", "#009de5", "#5fb1ff", "#ecddff"], "#000"), + ], }; lints.push(bgDeSaturated); @@ -82,6 +88,12 @@ const avoidTetradic: CustomLint = { failMessage: `This palette is tetradic, which is not recommended.`, id: `avoid-tetradic-built-in`, blameMode: "pair", + expectedPassingTests: [ + makePalFromString(["#0084a9", "#009de5", "#5fb1ff", "#bbc3ff"]), + ], + expectedFailingTests: [ + makePalFromString(["#d23bae", "#3b6dd2", "#d89a35", "#36d745"]), + ], }; lints.push(avoidTetradic); @@ -112,6 +124,8 @@ const avoidGreen: CustomLint = { failMessage: `When using green, it is better to try to it a yellow or blue one (hue angle less than 60 or greater than 160). T See "https://blog.datawrapper.de/beautifulcolors/#5" for more.`, id: `avoid-green-built-in`, blameMode: "single", + expectedPassingTests: [makePalFromString(["#bee38d", "#bbc3ff"])], + expectedFailingTests: [makePalFromString(["#0084a9", "#93e789"])], }; lints.push(avoidGreen); @@ -156,6 +170,12 @@ const AvoidTooMuchContrastWithTheBackground: CustomLint = { failMessage: `Don't make your colors too dark and saturated when you're using a bright background. If in doubt, try it out. Make your colors lighter, pull some saturation out of them and see how it feels. See "https://blog.datawrapper.de/beautifulcolors/#11" for more.`, id: `avoid-too-much-contrast-with-the-background-built-in`, blameMode: "single", + expectedPassingTests: [ + makePalFromString(["#f4b05d", "#3c828d", "#1c4b76"], "#f9f9f9"), + ], + expectedFailingTests: [ + makePalFromString(["#0e2d48", "#3c828d", "#b87930"], "#f9f9f9"), + ], }; lints.push(AvoidTooMuchContrastWithTheBackground); @@ -186,6 +206,8 @@ const requireColorComplements: CustomLint = { failMessage: `This palette has colors that do not have good contrast.`, id: `require-color-complements-built-in`, blameMode: "pair", + expectedPassingTests: [], + expectedFailingTests: [], }; lints.push(requireColorComplements); diff --git a/src/lib/lints/mutually-distinct.ts b/src/lib/lints/mutually-distinct.ts index ae10455c..4f6de2c8 100644 --- a/src/lib/lints/mutually-distinct.ts +++ b/src/lib/lints/mutually-distinct.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; const lint: CustomLint = { program: JSONToPrettyString({ @@ -24,6 +24,10 @@ const lint: CustomLint = { failMessage: `Some colors in this palette ({{blame}}) are not differentiable from each other.`, id: "mutually-distinct-built-in", blameMode: "pair" as const, + expectedPassingTests: [ + makePalFromString(["#000000", "#ffffff", "#ff0000", "#00ff00", "#0000ff"]), + ], + expectedFailingTests: [makePalFromString(["#d2b48c", "#f5f5dc", "#d7fcef"])], }; export default lint; diff --git a/src/lib/lints/name-discrim.ts b/src/lib/lints/name-discrim.ts index 4d02cd52..b6db1334 100644 --- a/src/lib/lints/name-discrim.ts +++ b/src/lib/lints/name-discrim.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; import namer from "color-namer"; import { Color } from "../Color"; @@ -64,6 +64,10 @@ const lint: CustomLint = { id: "color-name-discriminability-built-in", blameMode: "pair" as const, subscribedFix: "fixColorNameDiscriminability", + expectedPassingTests: [ + makePalFromString(["#000", "#fff", "#f00", "#0f0", "#00f"]), + ], + expectedFailingTests: [makePalFromString(["#5260d1", "#005ebe"])], }; export default lint; diff --git a/src/lib/lints/sequential-order.ts b/src/lib/lints/sequential-order.ts index ae9a6f8f..7d4bffc1 100644 --- a/src/lib/lints/sequential-order.ts +++ b/src/lib/lints/sequential-order.ts @@ -1,4 +1,4 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; import { Color } from "../Color"; import type { Palette } from "../../types"; @@ -35,6 +35,12 @@ const lint: CustomLint = { id: "sequential-order-built-in", blameMode: "single", subscribedFix: "fixSequentialOrder", + expectedPassingTests: [ + makePalFromString(["#0084a9", "#009de5", "#5fb1ff", "#bbc3ff", "#ecddff"]), + ], + expectedFailingTests: [ + makePalFromString(["#0084a9", "#009de5", "#5fb1ff", "#ecddff", "#bbc3ff"]), + ], }; export default lint; diff --git a/src/lib/lints/size-discrim.ts b/src/lib/lints/size-discrim.ts index b3a13cf6..4067fa12 100644 --- a/src/lib/lints/size-discrim.ts +++ b/src/lib/lints/size-discrim.ts @@ -1,6 +1,6 @@ -import { JSONToPrettyString } from "../utils"; +import { JSONToPrettyString, makePalFromString } from "../utils"; import type { CustomLint } from "../CustomLint"; -// based on + // https://github.com/connorgr/d3-jnd/blob/master/src/jnd.js const A = { l: 10.16, a: 10.68, b: 10.7 }; @@ -29,6 +29,53 @@ function jndLabInterval(p: pType, s: sType) { return nd(pVal, sVal); } +const testCase = { + Thin: { + passing: [makePalFromString(["#0084a9", "#bad", "#008000"])], + failing: [makePalFromString(["#0084a9", "#009de5", "#8ca9fa"])], + }, + Medium: { + passing: [ + makePalFromString([ + "#cf5f67", + "#468bbc", + "#848475", + "#c55eab", + "#ff008c", + ]), + ], + failing: [ + makePalFromString([ + "#a77865", + "#468bbc", + "#bc6c6c", + "#a67873", + "#ff008c", + ]), + ], + }, + Wide: { + passing: [ + makePalFromString([ + "#cf5f67", + "#468bbc", + "#848475", + "#c55eab", + "#ff008c", + ]), + ], + failing: [ + makePalFromString([ + "#a77865", + "#468bbc", + "#bc6c6c", + "#a67873", + "#ff008c", + ]), + ], + }, +}; + const itemSizeDescriptions = { Thin: "small blocks such as small circles or lines", Medium: "medium blocks such as bars in a bar chart or small graphics", @@ -86,6 +133,8 @@ const lints: CustomLint[] = keys.map((key) => { failMessage: `This palette has some colors ({{blame}}) that are close to each other in perceptual space and will not be resolvable for ${key} areas. This involves elements like ${itemSizeDescriptions[key]}`, id: `${key}-discrim-built-in`, blameMode: "pair", + expectedPassingTests: testCase[key].passing, + expectedFailingTests: testCase[key].failing, }; }); diff --git a/src/lib/lints/ugly-colors.ts b/src/lib/lints/ugly-colors.ts index 0a15e15c..d3412171 100644 --- a/src/lib/lints/ugly-colors.ts +++ b/src/lib/lints/ugly-colors.ts @@ -32,9 +32,10 @@ const lint: CustomLint = { id: "ugly-colors-built-in", blameMode: "single", expectedPassingTests: [ - makePalFromString(["#000000", "#FFFFFF", "#FF0000", "#00FF00", "#0000FF"]), + makePalFromString(["#000", "#fff", "#f00", "#00c000", "#9c70ff"]), ], expectedFailingTests: [ + makePalFromString(["#000000", "#56FF22"]), makePalFromString(["#000000", "#0010FF", "#6A7E25", "#00FF00", "#0000FF"]), ], }; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index cf2b3862..0784ed9b 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -236,11 +236,14 @@ export function newGenericPal(name: string): StringPalette { colors: pick(outfits), }; } -export function makePalFromString(strings: string[]): Palette { +export function makePalFromString( + strings: string[], + bg: string = "#ffffff" +): Palette { return { ...defaultHexPal, colors: strings.map((str) => Color.colorFromString(str, "lab")), - background: Color.colorFromString("#ffffff", "lab"), + background: Color.colorFromString(bg, "lab"), }; } diff --git a/src/linting/LintCustomizationPreview.svelte b/src/linting/LintCustomizationPreview.svelte index 612fdea8..94065a6c 100644 --- a/src/linting/LintCustomizationPreview.svelte +++ b/src/linting/LintCustomizationPreview.svelte @@ -105,6 +105,9 @@ }); }} /> +
+ Colors: [{pal.colors.map((x) => `"${x.toHex()}"`).join(", ")}] +
diff --git a/src/linting/LintCustomizationTab.svelte b/src/linting/LintCustomizationTab.svelte index 9040fcee..164a7795 100644 --- a/src/linting/LintCustomizationTab.svelte +++ b/src/linting/LintCustomizationTab.svelte @@ -473,6 +473,7 @@ Add Test +
Items marked with dashed sides are blamed
{/if}