From d473b0dbf85223fd9fbe65a295ba9fc744c26050 Mon Sep 17 00:00:00 2001 From: Andrew Michael McNutt Date: Mon, 11 Mar 2024 19:16:42 -0700 Subject: [PATCH] End to end working now --- netlify/functions/suggest-lint.ts | 9 +- public/lang-docs.md | 94 ++++++++++++- public/lint-schema.json | 37 +++-- src/components/PalPreview.svelte | 12 +- src/content-modules/MainColumn.svelte | 2 +- src/controls/ColorTagger.svelte | 111 +++++++-------- src/example/Example.svelte | 2 +- src/example/Swatches.svelte | 2 +- src/lib/ColorLint.test.ts | 27 ++-- src/lib/ColorLint.ts | 1 + src/lib/CustomLint.ts | 1 + src/lib/LintDocs.test.ts | 6 +- src/lib/__snapshots__/ColorLint.test.ts.snap | 30 ++-- src/lib/__snapshots__/LintDocs.test.ts.snap | 94 ++++++++++++- src/lib/ai-docs.md | 7 +- src/lib/{blindness.ts => cvd-sim.ts} | 0 src/lib/lint-language/LintLanguage.test.ts | 22 +++ src/lib/lint-language/lint-language.ts | 130 ++++++++++++------ src/lib/lint-language/lint-type.ts | 13 +- src/lib/linter.ts | 6 +- src/lib/lints/color-tags.ts | 78 +++++++++++ .../{color-blindness.ts => cvd-check.ts} | 4 +- src/lib/utils.ts | 4 - src/linting/EvalColorColumn.svelte | 2 +- src/linting/EvalResponse.svelte | 22 +-- src/linting/LintCustomizationPreview.svelte | 34 +++++ src/scatterplot/ColorScatterPlot.svelte | 29 +++- src/types.ts | 18 +-- 28 files changed, 598 insertions(+), 199 deletions(-) rename src/lib/{blindness.ts => cvd-sim.ts} (100%) create mode 100644 src/lib/lints/color-tags.ts rename src/lib/lints/{color-blindness.ts => cvd-check.ts} (95%) diff --git a/netlify/functions/suggest-lint.ts b/netlify/functions/suggest-lint.ts index be0bf958..a19e662e 100644 --- a/netlify/functions/suggest-lint.ts +++ b/netlify/functions/suggest-lint.ts @@ -26,8 +26,8 @@ Conjunctions: {not: EXPR} Quantifiers -{all: {varbs: Variable[], predicate: EXPR, where?: EXPR, in: "colors"}} -{exist: {varbs: Variable[], predicate: EXPR, where?: EXPR, in: "colors"}} +{all: {varbs: Variable, predicate: EXPR, where?: EXPR, in: Variable | Map}} +{exist: {varbs: Variable, predicate: EXPR, where?: EXPR, in: Variable | Map}} Notes: - varbs each listed variable into the scope, as well as variables like index(a) for a variable a @@ -68,13 +68,14 @@ Aggregates {extent: Variable | Number[]} Color Manipulations: -{toColor: Variable, space: 'lab' | 'hsl' | etc, channel: 'a' | 'b' | 'l' | etc} +{toSpace: Variable, space: 'lab' | 'hsl' | etc, channel: 'a' | 'b' | 'l' | etc} {cvdSim: Variable, type: 'protanomaly' | 'deuteranomaly' | 'tritanopia' | 'grayscale'} {name: Variable} {inGamut: Variable | Color} +{isTag: Variable | Color, value: string} Notes -- toColor has a shorthand like {"rgb.b": Variable} +- toSpace has a shorthand like {"rgb.b": Variable} - When comparing colors, it can be helpful to switch color spaces. For instance, to check if a value is blue you might switch it to HSL and check if the hue is in a certain range. Maps: diff --git a/public/lang-docs.md b/public/lang-docs.md index b6f81b6b..b9a4058c 100644 --- a/public/lang-docs.md +++ b/public/lang-docs.md @@ -28,7 +28,7 @@ Quantifiers Notes: - varbs each listed variable into the scope, as well as variables like index(a) for a variable a -- To slice you might do something like {...where: {"<" : {left: "index(a), right: 3}}}. This is important! There is no other way to subset or filter for quantifiers. THE IN CLAUSE MUST ONLY HAVE A VARIABLE AND THING ELSE. +- To slice you might do something like {...where: {"<" : {"left": "index(a)", "right": 3}}}. This is important! There is no other way to subset or filter for quantifiers. THE IN CLAUSE MUST ONLY HAVE A VARIABLE AND THING ELSE. Comparisons: {"==": {left: Value, right: Value}} @@ -65,13 +65,14 @@ Aggregates {extent: Variable | Number[]} Color Manipulations: -{toColor: Variable, space: 'lab' | 'hsl' | etc, channel: 'a' | 'b' | 'l' | etc} +{toSpace: Variable, space: 'lab' | 'hsl' | etc, channel: 'a' | 'b' | 'l' | etc} {cvdSim: Variable, type: 'protanomaly' | 'deuteranomaly' | 'tritanopia' | 'grayscale'} {name: Variable} {inGamut: Variable | Color} +{isTag: Variable | Color, value: string} Notes -- toColor has a shorthand like {"rgb.b": Variable} +- toSpace has a shorthand like {"rgb.b": Variable} - When comparing colors, it can be helpful to switch color spaces. For instance, to check if a value is blue you might switch it to HSL and check if the hue is in a certain range. Maps: @@ -278,7 +279,7 @@ Program: -### Colorblind Friendly for deuteranopia +### CVD: deuteranopia Friendly Description: All colors in a palette should be differentiable by people with deuteranopia (ie can't see green). This is because if they are not, then they will not be differentiable from each other in some contexts. @@ -322,7 +323,7 @@ Program: -### Colorblind Friendly for protanopia +### CVD: protanopia Friendly Description: All colors in a palette should be differentiable by people with protanopia (ie can't see red). This is because if they are not, then they will not be differentiable from each other in some contexts. @@ -366,7 +367,7 @@ Program: -### Colorblind Friendly for tritanopia +### CVD: tritanopia Friendly Description: All colors in a palette should be differentiable by people with tritanopia (ie can't see blue). This is because if they are not, then they will not be differentiable from each other in some contexts. @@ -454,6 +455,87 @@ Program: +### Axes should have low contrast with background + +Description: Axes should have low contrast with the background. Having it too high can make the axes too distracting. See "Whisper, Don't Scream: Grids and Transparency" for more. + +Natural Language: ALL a in colors WHERE isTag(a, axis), contrast(a, background, Lstar) < 20 + +Palettes that will fail this test: + +- #000 (axis) with a #fff background + + + +Palettes that will pass this test: + +- #eee (axis) with a #fff background + + +Program: + +```json +{ + "$schema": "http://localhost:3000/lint-schema.json", + "all": { + "in": "colors", + "varb": "a", + "where": {"isTag": "a", "value": "axis"}, + "predicate": { + "<": { + "left": { "contrast": {"left": "a", "right": "background"}, "algorithm": "Lstar" }, + "right": 20 + } + } + } +} + +``` + + + + + +### Blue should be high probability for the basic color term blue + +Description: Blue should be high probability for the basic color term blue. If it's not, it can be confusing to users. + +Natural Language: ALL a in colors WHERE isTag(a, blue), name(a) == blue + +Palettes that will fail this test: + +- #f00 (blue), #ffa500 with a #fff background + + + +Palettes that will pass this test: + +- #00f (blue), #f00, #ffa500 with a #fff background + +- #00f, #f00, #ffa500 with a #fff background + + +Program: + +```json +{ + "$schema": "http://localhost:3000/lint-schema.json", + "all": { + "in": "colors", + "varb": "a", + "where": {"isTag": "a", "value": "blue"}, + "predicate": { + "==": { "left": {"name": "a"}, "right": "blue" } + } + } +} + +``` + + + + + ### Fair Description: Do the colors stand out equally? A color palette is described as fair if both chroma and luminance ranges are below a certain threshold and unfair if one of them is above a certain threshold. diff --git a/public/lint-schema.json b/public/lint-schema.json index b82ad3bf..b33868c7 100644 --- a/public/lint-schema.json +++ b/public/lint-schema.json @@ -186,13 +186,14 @@ "properties": { "channel": {"$ref": "#/definitions/ColorChannel"}, "space": {"$ref": "#/definitions/ColorSpace"}, - "toColor": { + "toSpace": { "anyOf": [ {"$ref": "#/definitions/LintVariable"}, {"$ref": "#/definitions/LintColor"} ] } }, - "required": ["toColor", "space", "channel"], + "required": ["toSpace", "space", "channel"], "type": "object" }, + {"$ref": "#/definitions/LintColorTagCheck"}, { "additionalProperties": { "anyOf": [ {"$ref": "#/definitions/LintVariable"}, {"type": "string"} ] @@ -213,6 +214,17 @@ "required": ["cvdSim", "type"], "type": "object" }, + "LintColorTagCheck": { + "additionalProperties": false, + "properties": { + "isTag": { + "anyOf": [ {"$ref": "#/definitions/LintVariable"}, {"$ref": "#/definitions/LintColor"} ] + }, + "value": {"type": "string"} + }, + "required": ["isTag", "value"], + "type": "object" + }, "LintComparison": { "anyOf": [ { @@ -363,10 +375,11 @@ }, "LintExpression": { "anyOf": [ - {"$ref": "#/definitions/LintConjunction"}, - {"$ref": "#/definitions/LintQuantifier" }, - {"$ref": "#/definitions/LintComparison" }, - {"$ref": "#/definitions/LintBoolean" } + {"$ref": "#/definitions/LintConjunction" }, + {"$ref": "#/definitions/LintQuantifier" }, + {"$ref": "#/definitions/LintComparison" }, + {"$ref": "#/definitions/LintBoolean" }, + {"$ref": "#/definitions/LintColorTagCheck"} ], "description": "A LintExpression is a JSON object that represents a logical expression. It is used to express a condition that is evaluated to a boolean value. It can be a conjunction, a quantifier, a comparison or a boolean value." }, @@ -375,7 +388,7 @@ { "additionalProperties": false, "properties": { - "filter": {"$ref": "#/definitions/alias-1448761563-3904-3965-1448761563-0-6322"}, + "filter": {"$ref": "#/definitions/alias-1448761563-3892-3953-1448761563-0-6459"}, "func" : {"$ref": "#/definitions/LintExpression"} , "varb" : {"type": "string"} }, @@ -386,7 +399,7 @@ "additionalProperties": false, "properties": { "func": {"$ref": "#/definitions/LintValue"} , - "map" : {"$ref": "#/definitions/alias-1448761563-3904-3965-1448761563-0-6322"}, + "map" : {"$ref": "#/definitions/alias-1448761563-3892-3953-1448761563-0-6459"}, "varb": {"type": "string"} }, "required": ["map", "func", "varb"], @@ -395,7 +408,7 @@ { "additionalProperties": false, "properties": { - "reverse": {"$ref": "#/definitions/alias-1448761563-3904-3965-1448761563-0-6322"} + "reverse": {"$ref": "#/definitions/alias-1448761563-3892-3953-1448761563-0-6459"} }, "required": ["reverse"], "type": "object" @@ -404,7 +417,7 @@ "additionalProperties": false, "properties": { "func": {"$ref": "#/definitions/LintValue"} , - "sort": {"$ref": "#/definitions/alias-1448761563-3904-3965-1448761563-0-6322"}, + "sort": {"$ref": "#/definitions/alias-1448761563-3892-3953-1448761563-0-6459"}, "varb": {"type": "string"} }, "required": ["sort", "func", "varb"], @@ -412,7 +425,7 @@ }, { "additionalProperties": false, - "properties": { "speed": {"$ref": "#/definitions/alias-1448761563-3904-3965-1448761563-0-6322"} }, + "properties": { "speed": {"$ref": "#/definitions/alias-1448761563-3892-3953-1448761563-0-6459"} }, "required": ["speed"], "type": "object" } @@ -727,7 +740,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-3904-3965-1448761563-0-6322": { + "alias-1448761563-3892-3953-1448761563-0-6459": { "anyOf": [ {"$ref": "#/definitions/LintVariable"} , { "items": {"$ref": "#/definitions/LintValue"}, "type": "array" }, diff --git a/src/components/PalPreview.svelte b/src/components/PalPreview.svelte index d829ce25..2b28e4c0 100644 --- a/src/components/PalPreview.svelte +++ b/src/components/PalPreview.svelte @@ -9,6 +9,8 @@ import { dealWithFocusEvent } from "../lib/utils"; $: focusSet = new Set($focusStore.focusedColors); + $: bgLum = pal.background.luminance(); + $: textColor = bgLum > 0.4 ? "#00000066" : "#ffffffaa"; @@ -19,7 +21,7 @@ on:click={() => focusStore.clearColors()} > {#each pal.colors as color, idx} -
+
{#if allowModification} {:else} @@ -41,8 +42,11 @@ >
{/if} {#if showTags} -
- {#each color.tags as tag, idx} +
+ {#each color.tags as tag}
{tag}
{/each}
diff --git a/src/content-modules/MainColumn.svelte b/src/content-modules/MainColumn.svelte index 15813fb7..ac20a0f2 100644 --- a/src/content-modules/MainColumn.svelte +++ b/src/content-modules/MainColumn.svelte @@ -14,7 +14,7 @@ import Nav from "../components/Nav.svelte"; import PalPreview from "../components/PalPreview.svelte"; import SetColorSpace from "../controls/SetColorSpace.svelte"; - import simulate_cvd from "../lib/blindness"; + import simulate_cvd from "../lib/cvd-sim"; import ContentEditable from "../components/ContentEditable.svelte"; diff --git a/src/controls/ColorTagger.svelte b/src/controls/ColorTagger.svelte index 18d6b7bc..8591fcf8 100644 --- a/src/controls/ColorTagger.svelte +++ b/src/controls/ColorTagger.svelte @@ -1,6 +1,7 @@
Tags
-
- These to mark properties like color, brand, and so on -
-
{ - updateTags([...currentColor.tags, tagInput]); - tagInput = ""; - }} -> - -
{#each currentColor.tags as tag} -
- {tag} - +
+
+ {/each} + +
+
Color Tags
+
+ These to mark properties like color, brand, and so on. {#if commonTags.length} + Here are some common ones that are have specific effects in the app.{/if} +
+ {#each commonTags as tag} + + {/each} + +
Custom
+ +
{ + updateTags([...currentColor.tags, tagInput]); + tagInput = ""; }} > - ✕ - + +
- {/each} + +
-
Object for this color
- - -
Size of color
- diff --git a/src/example/Example.svelte b/src/example/Example.svelte index 20f1355b..9f3358d9 100644 --- a/src/example/Example.svelte +++ b/src/example/Example.svelte @@ -5,7 +5,7 @@ import { Color } from "../lib/Color"; import focusStore from "../stores/focus-store"; import { idxToKey } from "../lib/charts"; - import simulate_cvd from "../lib/blindness"; + import simulate_cvd from "../lib/cvd-sim"; export let example: string; export let size = 300; export let paletteIdx: number; diff --git a/src/example/Swatches.svelte b/src/example/Swatches.svelte index 1ebdcbc8..866e785d 100644 --- a/src/example/Swatches.svelte +++ b/src/example/Swatches.svelte @@ -4,7 +4,7 @@ import colorStore from "../stores/color-store"; import focusStore from "../stores/focus-store"; import configStore from "../stores/config-store"; - import simulate_cvd from "../lib/blindness"; + import simulate_cvd from "../lib/cvd-sim"; import { dealWithFocusEvent } from "../lib/utils"; diff --git a/src/lib/ColorLint.test.ts b/src/lib/ColorLint.test.ts index 5cd08107..88f1da39 100644 --- a/src/lib/ColorLint.test.ts +++ b/src/lib/ColorLint.test.ts @@ -13,8 +13,9 @@ import type { CustomLint } from "./CustomLint"; import AvoidExtremes from "./lints/avoid-extremes"; import BackgroundContrast from "./lints/background-contrast"; import CatOrderSimilarity from "./lints/cat-order-similarity"; -import ColorBlindness from "./lints/color-blindness"; +import CVDCheck from "./lints/cvd-check"; import ColorNameDiscriminability, { getName } from "./lints/name-discrim"; +import ColorTags from "./lints/color-tags"; import EvenDistribution from "./lints/even-distribution"; import Fair from "./lints/fair"; import Gamut from "./lints/in-gamut"; @@ -125,17 +126,17 @@ test("ColorLint - Gamut", async () => { ]); }); -test("ColorLint - ColorBlind: Deuteranopia", async () => { - autoTest(ColorBlindness[0]); +test("ColorLint - CVD: Deuteranopia", async () => { + autoTest(CVDCheck[0]); }); -test("ColorLint - ColorBlind: Protanopia", async () => { - autoTest(ColorBlindness[1]); +test("ColorLint - CVD: Protanopia", async () => { + autoTest(CVDCheck[1]); }); -test("ColorLint - ColorBlind: Tritanopia", async () => { - autoTest(ColorBlindness[2]); +test("ColorLint - CVD: Tritanopia", async () => { + autoTest(CVDCheck[2]); }); -test("ColorLint - ColorBlind: Grayscale", async () => { - autoTest(ColorBlindness[3]); +test("ColorLint - CVD: Grayscale", async () => { + autoTest(CVDCheck[3]); }); const ughWhat = ["#00ffff", "#00faff", "#00e4ff", "#fdfdfc", "#00ffff"]; @@ -176,3 +177,11 @@ test("ColorLint - MuthGuidelines (3) Prefer yellowish or blueish greens", () => test("ColorLint - MuthGuidelines (4) Avoid too much contrast with the background", () => { autoTest(MuthGuidelines[3]); }); + +test("ColorLnt - ColorTags (1) Whisper don't scream", () => { + autoTest(ColorTags[0]); +}); + +test("ColorLnt - ColorTags (2) Blue should be high probability for the basic color term blue", () => { + autoTest(ColorTags[1]); +}); diff --git a/src/lib/ColorLint.ts b/src/lib/ColorLint.ts index 1aa4d52b..8e02daed 100644 --- a/src/lib/ColorLint.ts +++ b/src/lib/ColorLint.ts @@ -32,6 +32,7 @@ export class ColorLint { naturalLanguageProgram: string = ""; level: LintLevel = "error"; subscribedFix: string = "none"; + program: string = ""; constructor(Palette: Palette) { this.palette = Palette; diff --git a/src/lib/CustomLint.ts b/src/lib/CustomLint.ts index 788dc9f6..8ad3105c 100644 --- a/src/lib/CustomLint.ts +++ b/src/lib/CustomLint.ts @@ -41,6 +41,7 @@ export function CreateCustomLint(props: CustomLint) { blameMode = props.blameMode; subscribedFix = props.subscribedFix || "none"; naturalLanguageProgram = natProg; + program = props.program; _runCheck(options: any) { const prog = Json.parse(props.program); diff --git a/src/lib/LintDocs.test.ts b/src/lib/LintDocs.test.ts index 953c7e30..0ef253f6 100644 --- a/src/lib/LintDocs.test.ts +++ b/src/lib/LintDocs.test.ts @@ -17,7 +17,11 @@ const testCaseToText = ( .map( (x) => `- ${x.colors - .map((y) => y.color.toHex()) + .map((y) => { + const hex = y.color.toHex(); + const tags = y.tags.length ? ` (${y.tags.join(", ")})` : ""; + return `${hex}${tags}`; + }) .join(", ")} with a ${x.background.toHex()} background` ) .join("\n\n"); diff --git a/src/lib/__snapshots__/ColorLint.test.ts.snap b/src/lib/__snapshots__/ColorLint.test.ts.snap index 7f918c79..75d45646 100644 --- a/src/lib/__snapshots__/ColorLint.test.ts.snap +++ b/src/lib/__snapshots__/ColorLint.test.ts.snap @@ -28,25 +28,25 @@ exports[`ColorLint - Background Contrast 3`] = `"These colors () do not have a s 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 - CVD: Deuteranopia 1`] = `"This palette is not colorblind friendly for deuteranopia color blindness (ie can't see green). The following pairs are undifferentiable: ()"`; -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 - CVD: 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: Deuteranopia 1`] = `"This palette is not colorblind friendly for deuteranopia color blindness (ie can't see green). The following pairs are undifferentiable: ()"`; +exports[`ColorLint - CVD: Grayscale 1`] = `"This palette may not work in black and white. The following pairs are hard to tell the difference between: ()"`; -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 - CVD: 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: Grayscale 1`] = `"This palette may not work in black and white. The following pairs are hard to tell the difference between: ()"`; +exports[`ColorLint - CVD: 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: 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 - CVD: 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: Protanopia 1`] = `"This palette is not colorblind friendly for protanopia color blindness (ie can't see red). The following pairs are undifferentiable: ()"`; +exports[`ColorLint - CVD: 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: 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 - CVD: 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 - 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 - CatOrderSimilarity 1`] = `"Some sequences of colors are too similar based on dE scores: . Try reordering them or making them more distinguishable"`; -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 - 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 - ColorNameDiscriminability 1`] = `"The following pairs of colors have the same name: "`; @@ -117,3 +117,13 @@ exports[`ColorLint - UglyColors 1`] = `"This palette has some colors (specifical 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"`; + +exports[`ColorLnt - ColorTags (1) Whisper don't scream 1`] = `"Axes should not have high contrast with the background. This can make the axes too distracting. "`; + +exports[`ColorLnt - ColorTags (1) Whisper don't scream 2`] = `"Axes should not have high contrast with the background. This can make the axes too distracting. #000"`; + +exports[`ColorLnt - ColorTags (2) Blue should be high probability for the basic color term blue 1`] = `"Blue should be high probability for the basic color term blue. If it's not, it can be confusing to users. "`; + +exports[`ColorLnt - ColorTags (2) Blue should be high probability for the basic color term blue 2`] = `"Blue should be high probability for the basic color term blue. If it's not, it can be confusing to users. "`; + +exports[`ColorLnt - ColorTags (2) Blue should be high probability for the basic color term blue 3`] = `"Blue should be high probability for the basic color term blue. If it's not, it can be confusing to users. #f00"`; diff --git a/src/lib/__snapshots__/LintDocs.test.ts.snap b/src/lib/__snapshots__/LintDocs.test.ts.snap index dba0065b..da115f0f 100644 --- a/src/lib/__snapshots__/LintDocs.test.ts.snap +++ b/src/lib/__snapshots__/LintDocs.test.ts.snap @@ -31,7 +31,7 @@ Quantifiers Notes: - varbs each listed variable into the scope, as well as variables like index(a) for a variable a -- To slice you might do something like {...where: {"<" : {left: "index(a), right: 3}}}. This is important! There is no other way to subset or filter for quantifiers. THE IN CLAUSE MUST ONLY HAVE A VARIABLE AND THING ELSE. +- To slice you might do something like {...where: {"<" : {"left": "index(a)", "right": 3}}}. This is important! There is no other way to subset or filter for quantifiers. THE IN CLAUSE MUST ONLY HAVE A VARIABLE AND THING ELSE. Comparisons: {"==": {left: Value, right: Value}} @@ -68,13 +68,14 @@ Aggregates {extent: Variable | Number[]} Color Manipulations: -{toColor: Variable, space: 'lab' | 'hsl' | etc, channel: 'a' | 'b' | 'l' | etc} +{toSpace: Variable, space: 'lab' | 'hsl' | etc, channel: 'a' | 'b' | 'l' | etc} {cvdSim: Variable, type: 'protanomaly' | 'deuteranomaly' | 'tritanopia' | 'grayscale'} {name: Variable} {inGamut: Variable | Color} +{isTag: Variable | Color, value: string} Notes -- toColor has a shorthand like {"rgb.b": Variable} +- toSpace has a shorthand like {"rgb.b": Variable} - When comparing colors, it can be helpful to switch color spaces. For instance, to check if a value is blue you might switch it to HSL and check if the hue is in a certain range. Maps: @@ -281,7 +282,7 @@ Program: -### Colorblind Friendly for deuteranopia +### CVD: deuteranopia Friendly Description: All colors in a palette should be differentiable by people with deuteranopia (ie can't see green). This is because if they are not, then they will not be differentiable from each other in some contexts. @@ -325,7 +326,7 @@ Program: -### Colorblind Friendly for protanopia +### CVD: protanopia Friendly Description: All colors in a palette should be differentiable by people with protanopia (ie can't see red). This is because if they are not, then they will not be differentiable from each other in some contexts. @@ -369,7 +370,7 @@ Program: -### Colorblind Friendly for tritanopia +### CVD: tritanopia Friendly Description: All colors in a palette should be differentiable by people with tritanopia (ie can't see blue). This is because if they are not, then they will not be differentiable from each other in some contexts. @@ -457,6 +458,87 @@ Program: +### Axes should have low contrast with background + +Description: Axes should have low contrast with the background. Having it too high can make the axes too distracting. See "Whisper, Don't Scream: Grids and Transparency" for more. + +Natural Language: ALL a in colors WHERE isTag(a, axis), contrast(a, background, Lstar) < 20 + +Palettes that will fail this test: + +- #000 (axis) with a #fff background + + + +Palettes that will pass this test: + +- #eee (axis) with a #fff background + + +Program: + +\`\`\`json +{ + "$schema": "http://localhost:3000/lint-schema.json", + "all": { + "in": "colors", + "varb": "a", + "where": {"isTag": "a", "value": "axis"}, + "predicate": { + "<": { + "left": { "contrast": {"left": "a", "right": "background"}, "algorithm": "Lstar" }, + "right": 20 + } + } + } +} + +\`\`\` + + + + + +### Blue should be high probability for the basic color term blue + +Description: Blue should be high probability for the basic color term blue. If it's not, it can be confusing to users. + +Natural Language: ALL a in colors WHERE isTag(a, blue), name(a) == blue + +Palettes that will fail this test: + +- #f00 (blue), #ffa500 with a #fff background + + + +Palettes that will pass this test: + +- #00f (blue), #f00, #ffa500 with a #fff background + +- #00f, #f00, #ffa500 with a #fff background + + +Program: + +\`\`\`json +{ + "$schema": "http://localhost:3000/lint-schema.json", + "all": { + "in": "colors", + "varb": "a", + "where": {"isTag": "a", "value": "blue"}, + "predicate": { + "==": { "left": {"name": "a"}, "right": "blue" } + } + } +} + +\`\`\` + + + + + ### Fair Description: Do the colors stand out equally? A color palette is described as fair if both chroma and luminance ranges are below a certain threshold and unfair if one of them is above a certain threshold. diff --git a/src/lib/ai-docs.md b/src/lib/ai-docs.md index baf7aa4e..a9a4d76c 100644 --- a/src/lib/ai-docs.md +++ b/src/lib/ai-docs.md @@ -24,7 +24,7 @@ Quantifiers Notes: - varbs each listed variable into the scope, as well as variables like index(a) for a variable a -- To slice you might do something like {...where: {"<" : {left: "index(a), right: 3}}}. This is important! There is no other way to subset or filter for quantifiers. THE IN CLAUSE MUST ONLY HAVE A VARIABLE AND THING ELSE. +- To slice you might do something like {...where: {"<" : {"left": "index(a)", "right": 3}}}. This is important! There is no other way to subset or filter for quantifiers. THE IN CLAUSE MUST ONLY HAVE A VARIABLE AND THING ELSE. Comparisons: {"==": {left: Value, right: Value}} @@ -61,13 +61,14 @@ Aggregates {extent: Variable | Number[]} Color Manipulations: -{toColor: Variable, space: 'lab' | 'hsl' | etc, channel: 'a' | 'b' | 'l' | etc} +{toSpace: Variable, space: 'lab' | 'hsl' | etc, channel: 'a' | 'b' | 'l' | etc} {cvdSim: Variable, type: 'protanomaly' | 'deuteranomaly' | 'tritanopia' | 'grayscale'} {name: Variable} {inGamut: Variable | Color} +{isTag: Variable | Color, value: string} Notes -- toColor has a shorthand like {"rgb.b": Variable} +- toSpace has a shorthand like {"rgb.b": Variable} - When comparing colors, it can be helpful to switch color spaces. For instance, to check if a value is blue you might switch it to HSL and check if the hue is in a certain range. Maps: diff --git a/src/lib/blindness.ts b/src/lib/cvd-sim.ts similarity index 100% rename from src/lib/blindness.ts rename to src/lib/cvd-sim.ts diff --git a/src/lib/lint-language/LintLanguage.test.ts b/src/lib/lint-language/LintLanguage.test.ts index 79548089..a2b26c20 100644 --- a/src/lib/lint-language/LintLanguage.test.ts +++ b/src/lib/lint-language/LintLanguage.test.ts @@ -287,6 +287,28 @@ test("LintLanguage Quantifiers Exist - DENSE", () => { expect(LLEval(colorBlindExists, exampleColors).result).toBe(false); }); +test("LintLanguage Quantifiers All - DENSE", () => { + const colorBlindExists = { + all: { + in: "colors", + varbs: ["a", "b"], + predicate: { + not: { + similar: { + left: { cvdSim: "a", type: "deuteranopia" }, + right: { cvdSim: "b", type: "deuteranopia" }, + threshold: 9, + }, + }, + }, + }, + }; + expect(prettyPrintLL(colorBlindExists)).toBe( + "ALL (a, b) in colors, NOT similar(cvdSim(a, deuteranopia), cvdSim(b, deuteranopia)) < 9" + ); + expect(LLEval(colorBlindExists, exampleColors).result).toBe(false); +}); + test("LintLanguage Check exists", () => { const program = { exist: { diff --git a/src/lib/lint-language/lint-language.ts b/src/lib/lint-language/lint-language.ts index 0e47fad5..2c26a564 100644 --- a/src/lib/lint-language/lint-language.ts +++ b/src/lib/lint-language/lint-language.ts @@ -1,10 +1,17 @@ -import cvdSim from "../blindness"; -import type { Palette } from "../../types"; +import cvdSim from "../cvd-sim"; +import type { Palette, ColorWrap } from "../../types"; import { Color, colorPickerConfig } from "../Color"; import { getName } from "../lints/name-discrim"; import type { LintProgram } from "./lint-type"; +import { wrapInBlankSemantics } from "../utils"; -type RawValues = string | number | Color | string[] | number[] | Color[]; +type RawValues = + | string + | number + | ColorWrap + | string[] + | number[] + | ColorWrap[]; class Environment { constructor( private palette: Palette, @@ -35,13 +42,13 @@ class Environment { get(name: string) { if (name === "colors") { const children = this.palette.colors - .map((x) => new LLColor(x.color, x.color.toHex())) + .map((x) => new LLColor(x, x.color.toHex())) .map((x) => new LLValue(x)); return new LLValueArray(children); } if (name === "background") { return new LLColor( - this.palette.background, + wrapInBlankSemantics(this.palette.background), this.palette.background.toHex() ); } @@ -296,19 +303,29 @@ export class LLVariable extends LLNode { } export class LLColor extends LLNode { - constructor(private value: Color, private constructorString: string) { + constructor( + private value: ColorWrap, + private constructorString: string + ) { super(); } - evaluate(env: Environment): ReturnVal { + evaluate(env: Environment): ReturnVal> { this.evalCheck(env); + return { result: this.value, env }; } static tryToConstruct(value: any, options: OptionsConfig): false | LLColor { if (value instanceof Color) { - return new LLColor(value, value.toHex()); + return new LLColor(wrapInBlankSemantics(value), value.toHex()); + } + if (typeof value === "object" && value.color instanceof Color) { + return new LLColor(value, value.color.toHex()); } if (typeof value === "string" && Color.stringIsColor(value, "lab")) { - return new LLColor(Color.colorFromString(value, "lab"), value); + return new LLColor( + wrapInBlankSemantics(Color.colorFromString(value, "lab")), + value + ); } return false; } @@ -384,18 +401,19 @@ export class LLNumberOp extends LLNode { const predicateTypes = ["==", "!=", ">", "<", "similar"] as const; +type CompareType = number | boolean | string | ColorWrap; function compareValues( - leftVal: any, - rightVal: any, + leftVal: CompareType, + rightVal: CompareType, pred: LLPredicate, showValues: boolean ) { - let isColor = leftVal instanceof Color; + let isColor = getType(leftVal) === "Color"; let left = leftVal; let right = rightVal; if (isColor && pred.type !== "similar") { - left = leftVal.toHex(); - right = rightVal.toHex(); + left = (leftVal as ColorWrap).color.toHex(); + right = (rightVal as ColorWrap).color.toHex(); } if (showValues) { console.log(pred.type, left, right); @@ -405,9 +423,17 @@ function compareValues( let thresh = pred.threshold; if (!thresh) throw new Error("Similarity threshold not found"); if (isColor) { - const diff = left.symmetricDeltaE(right, "2000"); + let localLeft = left as ColorWrap; + let localRight = right as ColorWrap; + const diff = localLeft.color.symmetricDeltaE(localRight.color, "2000"); if (showValues) { - console.log("diff", diff, thresh, left.toHex(), right.toHex()); + console.log( + "diff", + diff, + thresh, + localLeft.color.toHex(), + localRight.color.toHex() + ); } return diff < thresh; } @@ -415,7 +441,8 @@ function compareValues( return Math.abs(left - right) < thresh; } throw new Error( - "Type error. Similar must be used with colors or numbers." + `Type error. Similar must be used with colors or numbers. + Got ${JSON.stringify(left)} and ${JSON.stringify(right)}` ); case "==": return left === right; @@ -428,7 +455,7 @@ function compareValues( } } const getType = (x: any) => { - if (x instanceof Color) return "Color"; + if (x?.color instanceof Color) return "Color"; return typeof x === "object" ? Array.isArray(x) ? "Array" @@ -541,27 +568,41 @@ export class LLValue extends LLNode { } type Params = Record; -const VFTypes = [ +const VFTypes: { + primaryKey: string; + params: string[]; + op: (val: ColorWrap, params: Params) => any; +}[] = [ { primaryKey: "cvdSim", params: ["type"] as string[], - op: (val: Color, params: Params) => cvdSim(params.type, val), + op: (val, params) => ({ ...val, color: cvdSim(params.type, val.color) }), }, { primaryKey: "name", params: [] as string[], - op: (val: Color, _params: Params) => getName(val).toLowerCase(), + op: (val, _params) => getName(val.color).toLowerCase(), }, { - primaryKey: "toColor", + primaryKey: "toSpace", params: ["space", "channel"] as string[], - op: (val: Color, params: Params) => - Number(val.toColorSpace(params.space as any).getChannel(params.channel)), + op: (val, params) => + Number( + val.color.toColorSpace(params.space as any).getChannel(params.channel) + ), }, { primaryKey: "inGamut", params: [], - op: (val: Color, _params: Params) => val.inGamut(), + op: (val, _params) => val.color.inGamut(), + }, + { + primaryKey: "isTag", + params: ["value"], + op: (val, params) => { + const tag = params.value.toLowerCase(); + return val.tags.some((x) => x.toLowerCase() === tag); + }, }, ]; @@ -571,8 +612,10 @@ Object.entries(colorPickerConfig).map(([colorSpace, value]) => { VFTypes.push({ primaryKey: `${colorSpace}.${channelKey.toLowerCase()}`, params: [] as string[], - op: (val: Color, _params: Params) => - Number(val.toColorSpace(colorSpace as any).getChannel(channelKey)), + op: (val: ColorWrap, _params: Params) => + Number( + val.color.toColorSpace(colorSpace as any).getChannel(channelKey) + ), }); }); }); @@ -590,7 +633,7 @@ export class LLValueFunction extends LLNode { const { input, params } = this; // get the value of the input, such as by deref const inputEval = input.evaluate(env).result; - if (!(inputEval instanceof Color)) { + if (!(typeof inputEval === "object" && inputEval.color instanceof Color)) { throw new Error( `Type error, was expecting a color, but got ${inputEval} in function ${this.type}` ); @@ -651,25 +694,29 @@ const getOp = const getParams = (op: any, node: any) => op.params.reduce((acc: any, key: any) => ({ ...acc, [key]: node[key] }), {}); -const LLPairFunctionTypes = [ +const LLPairFunctionTypes: { + primaryKey: string; + params: string[]; + op: (a: ColorWrap, b: ColorWrap, params: Params) => number; +}[] = [ { primaryKey: "dist", params: ["space"] as string[], - op: (valA: Color, valB: Color, params: Params) => - valA.distance(valB, params.space as any), + op: (valA, valB, params) => + valA.color.distance(valB.color, params.space as any), }, { primaryKey: "deltaE", params: ["algorithm"] as string[], - op: (valA: Color, valB: Color, params: Params) => - valA.symmetricDeltaE(valB, params.algorithm as any), + op: (valA, valB, params) => + valA.color.symmetricDeltaE(valB.color, params.algorithm as any), }, { primaryKey: "contrast", params: ["algorithm"] as string[], - op: (valA: Color, valB: Color, params: Params) => { - const a = valA.toColorIO(); - const b = valB.toColorIO(); + op: (valA, valB, params) => { + const a = valA.color.toColorIO(); + const b = valB.color.toColorIO(); return Math.abs(a.contrast(b, params.algorithm as any)); }, }, @@ -689,7 +736,10 @@ export class LLPairFunction extends LLNode { // get the value of the input, such as by deref const leftEval = left.evaluate(env).result; const rightEval = right.evaluate(env).result; - if (!(leftEval instanceof Color) || !(rightEval instanceof Color)) { + if ( + !(leftEval?.color instanceof Color) || + !(rightEval?.color instanceof Color) + ) { throw new Error("Type error"); } const op = LLPairFunctionTypes.find((x) => x.primaryKey === this.type); @@ -736,7 +786,7 @@ export class LLQuantifier extends LLNode { private input: LLValueArray | LLVariable | LLMap, private predicate: LLPredicate, private varbs: string[], - private where?: LLPredicate + private where?: LLPredicate | LLValueFunction ) { super(); } @@ -826,7 +876,7 @@ export class LLQuantifier extends LLNode { inputType, predicateType, varb ? [varb] : varbs, - where && tryTypes([LLPredicate], options)(where) + where && tryTypes([LLPredicate, LLValueFunction], options)(where) ); } toString(): string { @@ -984,7 +1034,7 @@ export class LLMap extends LLNode { typeof x === "number" || (typeof x === "object" && x?.type === "") ); - const allColors = children.every((x) => x instanceof Color); + const allColors = children.every((x) => x?.color instanceof Color); if (!allNumbers && !allColors) { const types = children.map((x) => x); console.log(children); diff --git a/src/lib/lint-language/lint-type.ts b/src/lib/lint-language/lint-type.ts index 6cebd6ca..8e3e893b 100644 --- a/src/lib/lint-language/lint-type.ts +++ b/src/lib/lint-language/lint-type.ts @@ -1,4 +1,3 @@ -import { Color } from "../Color"; /** * Lint Language * Lint Language is a language for expressing color logic. It is used to define color rules and constraints in the Lint system. @@ -12,7 +11,8 @@ export type LintExpression = | LintConjunction | LintQuantifier | LintComparison - | LintBoolean; + | LintBoolean + | LintColorTagCheck; /** * A logical conjunction expression. It is used to express the logical AND, OR and NOT operations. @@ -149,12 +149,18 @@ export type LintColorFunction = | { name: LintVariable | LintColor } | { inGamut: LintVariable | LintColor } | { - toColor: LintVariable | LintColor; + toSpace: LintVariable | LintColor; space: ColorSpace; channel: ColorChannel; } + | LintColorTagCheck | { [cmd: string]: LintVariable | string }; +export type LintColorTagCheck = { + isTag: LintVariable | LintColor; + value: string; +}; + /** * Converts a Color to a color space component. Has syntax like colorSpace.channel, where colorSpace is a color space and channel is a channel in that color space. Available spaces: hsl, hsv, jzazbz, lab, lch, oklab, oklch, rgb */ @@ -208,5 +214,6 @@ export type LintValue = // raw values export type LintBoolean = boolean; export type LintVariable = string; +import { Color } from "../Color"; export type LintColor = string | Color | LintVariable; // export type LintColor = string | LintVariable; diff --git a/src/lib/linter.ts b/src/lib/linter.ts index 12d79f9c..ceb70376 100644 --- a/src/lib/linter.ts +++ b/src/lib/linter.ts @@ -11,7 +11,8 @@ import Affects from "./lints/affects"; import AvoidExtremes from "./lints/avoid-extremes"; import BackgroundDifferentiability from "./lints/background-contrast"; import CatOrderSimilarity from "./lints/cat-order-similarity"; -import ColorBlindness from "./lints/color-blindness"; +import CVDCheck from "./lints/cvd-check"; +import ColorTags from "./lints/color-tags"; import EvenDistribution from "./lints/even-distribution"; import Fair from "./lints/fair"; import Gamut from "./lints/in-gamut"; @@ -25,7 +26,8 @@ import UglyColors from "./lints/ugly-colors"; export const BUILT_INS: CustomLint[] = [ ...Affects, - ...ColorBlindness, + ...CVDCheck, + ...ColorTags, ...Fair, ...MuthGuidelines, ...SizeDiscrim, diff --git a/src/lib/lints/color-tags.ts b/src/lib/lints/color-tags.ts new file mode 100644 index 00000000..a843e042 --- /dev/null +++ b/src/lib/lints/color-tags.ts @@ -0,0 +1,78 @@ +import { JSONToPrettyString, makePalFromString } from "../utils"; +import type { CustomLint } from "../CustomLint"; + +function createPalWithTags(colors: string[], tags: [number, string][]) { + const pal = makePalFromString(colors); + tags.forEach(([index, tag]) => { + pal.colors[index].tags.push(tag); + }); + return pal; +} + +// If Semantic Tag == Context then the color should be low contrast with the background (Whisper, Don't Scream work) + +const lints: CustomLint[] = []; +const whisperScream: CustomLint = { + name: "Axes should have low contrast with background", + program: JSONToPrettyString({ + // @ts-ignore + $schema: `${location.href}lint-schema.json`, + all: { + in: "colors", + varb: "a", + where: { isTag: "a", value: "axis" }, + // paper says 0.2 opacity is acceptable, which works out to be a 16 L* contrast, rounded up to 20 + predicate: { + "<": { + left: { + contrast: { left: "a", right: "background" }, + algorithm: "Lstar", + }, + right: 20, + }, + }, + }, + }), + + taskTypes: ["sequential", "diverging", "categorical"] as const, + level: "warning", + group: "design", + description: `Axes should have low contrast with the background. Having it too high can make the axes too distracting. See "Whisper, Don't Scream: Grids and Transparency" for more.`, + failMessage: `Axes should not have high contrast with the background. This can make the axes too distracting. {{blame}}`, + id: "whisper-scream-built-in", + blameMode: "single", + expectedPassingTests: [createPalWithTags(["#eee"], [[0, "axis"]])], + expectedFailingTests: [createPalWithTags(["#000"], [[0, "axis"]])], +}; +lints.push(whisperScream); + +// If Semantic Tag == Blue, then the color should be high probability for the basic color term blue +const colorNames = ["blue", "red", "orange"]; +const blueBasicColor: CustomLint = { + name: "Blue should be high probability for the basic color term blue", + program: JSONToPrettyString({ + // @ts-ignore + $schema: `${location.href}lint-schema.json`, + all: { + in: "colors", + varb: "a", + where: { isTag: "a", value: "blue" }, + predicate: { "==": { left: { name: "a" }, right: "blue" } }, + }, + }), + + taskTypes: ["sequential", "diverging", "categorical"] as const, + level: "warning", + group: "design", + description: `Blue should be high probability for the basic color term blue. If it's not, it can be confusing to users.`, + failMessage: `Blue should be high probability for the basic color term blue. If it's not, it can be confusing to users. {{blame}}`, + id: "blue-basic-color-term-built-in", + blameMode: "single", + expectedPassingTests: [ + createPalWithTags(colorNames, [[0, "blue"]]), + makePalFromString(colorNames), + ], + expectedFailingTests: [createPalWithTags(colorNames.slice(1), [[0, "blue"]])], +}; +lints.push(blueBasicColor); +export default lints; diff --git a/src/lib/lints/color-blindness.ts b/src/lib/lints/cvd-check.ts similarity index 95% rename from src/lib/lints/color-blindness.ts rename to src/lib/lints/cvd-check.ts index 610a24cc..91e0b248 100644 --- a/src/lib/lints/color-blindness.ts +++ b/src/lib/lints/cvd-check.ts @@ -50,9 +50,7 @@ const lints: CustomLint[] = blindTypes.map((type) => ({ }, }), name: - type === "grayscale" - ? "Right in black and white" - : `Colorblind Friendly for ${type}`, + type === "grayscale" ? "Right in black and white" : `CVD: ${type} Friendly`, taskTypes: ["sequential", "diverging", "categorical"], group: "accessibility", description: `All colors in a palette should be differentiable by people with ${type} ${blindnessLabels[type]}. This is because if they are not, then they will not be differentiable from each other in some contexts.`, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 8e74a4a8..de255f80 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -225,15 +225,11 @@ export const makePal = ( export const wrapInBlankSemantics = (x: Color): ColorWrap => ({ color: x, - markType: undefined, - size: undefined, tags: [], }); export const wrapInBlankStringSemantics = (x: string): ColorWrap => ({ color: x, - markType: undefined, - size: undefined, tags: [], }); diff --git a/src/linting/EvalColorColumn.svelte b/src/linting/EvalColorColumn.svelte index 4ef28058..76f743a3 100644 --- a/src/linting/EvalColorColumn.svelte +++ b/src/linting/EvalColorColumn.svelte @@ -7,7 +7,7 @@ import { colorNameSimple } from "../lib/lints/name-discrim"; import EvalResponse from "./EvalResponse.svelte"; - import simulate_cvd from "../lib/blindness"; + import simulate_cvd from "../lib/cvd-sim"; import { Color } from "../lib/Color"; import { checkLevelToSymbol, dealWithFocusEvent } from "../lib/utils"; import { buttonStyle } from "../lib/styles"; diff --git a/src/linting/EvalResponse.svelte b/src/linting/EvalResponse.svelte index 93fe27ab..95a3d6c8 100644 --- a/src/linting/EvalResponse.svelte +++ b/src/linting/EvalResponse.svelte @@ -82,17 +82,6 @@ {/if}
Actions
- {#if check.isCustom} - - {/if} {#if cbMatch} + {/if} {#if requestState === "loading"}
Loading...
diff --git a/src/linting/LintCustomizationPreview.svelte b/src/linting/LintCustomizationPreview.svelte index c274b710..a731ab3d 100644 --- a/src/linting/LintCustomizationPreview.svelte +++ b/src/linting/LintCustomizationPreview.svelte @@ -59,6 +59,40 @@ > Remove +
Tags
+
+ {#each color.tags as tag, jdx} +
+ {tag} + +
+ {/each} +
+ { + if (e.key === "Enter") { + const newColors = [...pal.colors]; + newColors[idx] = { + ...newColors[idx], + tags: [...newColors[idx].tags, e.currentTarget.value], + }; + updatePal({ ...pal, colors: newColors }); + e.currentTarget.value = ""; + } + }} + />