diff --git a/src/options.js b/src/options.js index a391623c61..87f9f7371d 100644 --- a/src/options.js +++ b/src/options.js @@ -1,4 +1,4 @@ -import {color, descending, quantile, range as rangei} from "d3"; +import {descending, quantile, range as rangei} from "d3"; import {parse as isoParse} from "isoformat"; import {defined} from "./defined.js"; import {maybeTimeInterval, maybeUtcInterval} from "./time.js"; @@ -459,20 +459,20 @@ export function isEvery(values, is) { return every; } -// Mostly relies on d3-color, with a few extra color keywords. Currently this -// strictly requires that the value be a string; we might want to apply string -// coercion here, though note that d3-color instances would need to support -// valueOf to work correctly with InternMap. +const namedColors = new Set("none,currentcolor,transparent,aliceblue,antiquewhite,aqua,aquamarine,azure,beige,bisque,black,blanchedalmond,blue,blueviolet,brown,burlywood,cadetblue,chartreuse,chocolate,coral,cornflowerblue,cornsilk,crimson,cyan,darkblue,darkcyan,darkgoldenrod,darkgray,darkgreen,darkgrey,darkkhaki,darkmagenta,darkolivegreen,darkorange,darkorchid,darkred,darksalmon,darkseagreen,darkslateblue,darkslategray,darkslategrey,darkturquoise,darkviolet,deeppink,deepskyblue,dimgray,dimgrey,dodgerblue,firebrick,floralwhite,forestgreen,fuchsia,gainsboro,ghostwhite,gold,goldenrod,gray,green,greenyellow,grey,honeydew,hotpink,indianred,indigo,ivory,khaki,lavender,lavenderblush,lawngreen,lemonchiffon,lightblue,lightcoral,lightcyan,lightgoldenrodyellow,lightgray,lightgreen,lightgrey,lightpink,lightsalmon,lightseagreen,lightskyblue,lightslategray,lightslategrey,lightsteelblue,lightyellow,lime,limegreen,linen,magenta,maroon,mediumaquamarine,mediumblue,mediumorchid,mediumpurple,mediumseagreen,mediumslateblue,mediumspringgreen,mediumturquoise,mediumvioletred,midnightblue,mintcream,mistyrose,moccasin,navajowhite,navy,oldlace,olive,olivedrab,orange,orangered,orchid,palegoldenrod,palegreen,paleturquoise,palevioletred,papayawhip,peachpuff,peru,pink,plum,powderblue,purple,rebeccapurple,red,rosybrown,royalblue,saddlebrown,salmon,sandybrown,seagreen,seashell,sienna,silver,skyblue,slateblue,slategray,slategrey,snow,springgreen,steelblue,tan,teal,thistle,tomato,turquoise,violet,wheat,white,whitesmoke,yellow".split(",")); // prettier-ignore + +// Returns true if value is a valid CSS color string. This is intentionally lax +// because the CSS color spec keeps growing, and we don’t need to parse these +// colors—we just need to disambiguate them from column names. // https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint +// https://www.w3.org/TR/css-color-5/ export function isColor(value) { if (typeof value !== "string") return false; value = value.toLowerCase().trim(); return ( - value === "none" || - value === "currentcolor" || - (value.startsWith("url(") && value.endsWith(")")) || // , e.g. pattern or gradient - (value.startsWith("var(") && value.endsWith(")")) || // CSS variable - color(value) !== null + /^#[0-9a-f]{3,8}$/.test(value) || // hex rgb, rgba, rrggbb, rrggbbaa + /^(?:url|var|rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color|color-mix)\(.*\)$/.test(value) || // , CSS variable, color, etc. + namedColors.has(value) // currentColor, red, etc. ); } diff --git a/test/output/varColor.svg b/test/output/varColor.svg new file mode 100644 index 0000000000..a6893fd6fa --- /dev/null +++ b/test/output/varColor.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + E + T + A + O + I + N + S + H + R + D + L + C + U + M + W + F + G + Y + P + B + V + K + J + X + Q + Z + + + letter + + + + + + + + + + + + 0.00 + 0.02 + 0.04 + 0.06 + 0.08 + 0.10 + 0.12 + + + frequency → + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/varColor2.svg b/test/output/varColor2.svg new file mode 100644 index 0000000000..17d020c5c4 --- /dev/null +++ b/test/output/varColor2.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + E + T + A + O + I + N + S + H + R + D + L + C + U + M + W + F + G + Y + P + B + V + K + J + X + Q + Z + + + letter + + + + + + + + + + + + 0.00 + 0.02 + 0.04 + 0.06 + 0.08 + 0.10 + 0.12 + + + frequency → + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/varColorP3.svg b/test/output/varColorP3.svg new file mode 100644 index 0000000000..65a9744759 --- /dev/null +++ b/test/output/varColorP3.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + E + T + A + O + I + N + S + H + R + D + L + C + U + M + W + F + G + Y + P + B + V + K + J + X + Q + Z + + + letter + + + + + + + + + + + + 0.00 + 0.02 + 0.04 + 0.06 + 0.08 + 0.10 + 0.12 + + + frequency → + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/index.ts b/test/plots/index.ts index 9998e92373..0a412b4b67 100644 --- a/test/plots/index.ts +++ b/test/plots/index.ts @@ -318,6 +318,7 @@ export * from "./us-retail-sales.js"; export * from "./us-state-capitals-voronoi.js"; export * from "./us-state-capitals.js"; export * from "./us-state-population-change.js"; +export * from "./var-color.js"; export * from "./vector-field.js"; export * from "./vector-frame.js"; export * from "./volcano.js"; diff --git a/test/plots/var-color.ts b/test/plots/var-color.ts new file mode 100644 index 0000000000..b9803c958b --- /dev/null +++ b/test/plots/var-color.ts @@ -0,0 +1,23 @@ +import * as Plot from "@observablehq/plot"; +import * as d3 from "d3"; + +export async function varColor() { + const alphabet = await d3.csv("data/alphabet.csv", d3.autoType); + return Plot.plot({ + style: "--a: 0.5; --b: rgba(255, 0, 0, var(--a));", + marks: [Plot.barX(alphabet, {x: "frequency", y: "letter", fill: "var(--b)", sort: {y: "-x"}})] + }); +} + +export async function varColor2() { + const alphabet = await d3.csv("data/alphabet.csv", d3.autoType); + return Plot.plot({ + style: "--a: 0.5;", + marks: [Plot.barX(alphabet, {x: "frequency", y: "letter", fill: "rgba(255, 0, 0, var(--a))", sort: {y: "-x"}})] + }); +} + +export async function varColorP3() { + const alphabet = await d3.csv("data/alphabet.csv", d3.autoType); + return Plot.barX(alphabet, {x: "frequency", y: "letter", fill: "color(display-p3 1 0.5 0)", sort: {y: "-x"}}).plot(); +}