diff --git a/docs/features/marks.md b/docs/features/marks.md
index 65ce03d53a..df4a9dff0c 100644
--- a/docs/features/marks.md
+++ b/docs/features/marks.md
@@ -476,6 +476,7 @@ All marks support the following style options:
* **strokeDashoffset** - the [stroke dash offset](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dashoffset) (typically in pixels)
* **opacity** - object opacity (a number between 0 and 1)
* **mixBlendMode** - the [blend mode](https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode) (*e.g.*, *multiply*)
+* **imageFilter** - a CSS [filter](https://developer.mozilla.org/en-US/docs/Web/CSS/filter) (*e.g.*, *blur(5px)*)
* **shapeRendering** - the [shape-rendering mode](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering) (*e.g.*, *crispEdges*)
* **paintOrder** - the [paint order](https://developer.mozilla.org/en-US/docs/Web/CSS/paint-order) (*e.g.*, *stroke*)
* **dx** - horizontal offset (in pixels; defaults to 0)
diff --git a/src/mark.d.ts b/src/mark.d.ts
index 2989400944..64f4f496c9 100644
--- a/src/mark.d.ts
+++ b/src/mark.d.ts
@@ -399,6 +399,14 @@ export interface MarkOptions {
*/
mixBlendMode?: string;
+ /**
+ * A CSS [filter][1]; a constant string used to adjust the rendering of
+ * images, such as *blur(5px)*.
+ *
+ * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/filter
+ */
+ imageFilter?: string;
+
/**
* The [paint-order][1]; a constant string specifying the order in which the
* **fill**, **stroke**, and any markers are drawn; defaults to *normal*,
diff --git a/src/style.js b/src/style.js
index 9c7fe40857..5c229e192a 100644
--- a/src/style.js
+++ b/src/style.js
@@ -44,6 +44,7 @@ export function styles(
strokeDashoffset,
opacity,
mixBlendMode,
+ imageFilter,
paintOrder,
pointerEvents,
shapeRendering
@@ -135,6 +136,7 @@ export function styles(
mark.ariaHidden = string(ariaHidden);
mark.opacity = impliedNumber(copacity, 1);
mark.mixBlendMode = impliedString(mixBlendMode, "normal");
+ mark.imageFilter = impliedString(imageFilter, "none");
mark.paintOrder = impliedString(paintOrder, "normal");
mark.pointerEvents = impliedString(pointerEvents, "auto");
mark.shapeRendering = impliedString(shapeRendering, "auto");
@@ -368,6 +370,7 @@ export function applyIndirectStyles(selection, mark, dimensions, context) {
applyAttr(selection, "stroke-dasharray", mark.strokeDasharray);
applyAttr(selection, "stroke-dashoffset", mark.strokeDashoffset);
applyAttr(selection, "shape-rendering", mark.shapeRendering);
+ applyAttr(selection, "filter", mark.imageFilter);
applyAttr(selection, "paint-order", mark.paintOrder);
applyAttr(selection, "pointer-events", mark.pointerEvents);
}
diff --git a/test/output/penguinSpeciesImageFilter.svg b/test/output/penguinSpeciesImageFilter.svg
new file mode 100644
index 0000000000..24f4f65058
--- /dev/null
+++ b/test/output/penguinSpeciesImageFilter.svg
@@ -0,0 +1,60 @@
+
\ No newline at end of file
diff --git a/test/plots/index.ts b/test/plots/index.ts
index fed29255e1..ee9e6ab576 100644
--- a/test/plots/index.ts
+++ b/test/plots/index.ts
@@ -195,9 +195,7 @@ export * from "./penguin-quantile-unknown.js";
export * from "./penguin-sex-mass-culmen-species.js";
export * from "./penguin-sex.js";
export * from "./penguin-size-symbols.js";
-export * from "./penguin-species-cheysson.js";
-export * from "./penguin-species-gradient.js";
-export * from "./penguin-species-group.js";
+export * from "./penguin-species.js";
export * from "./penguin-species-island-relative.js";
export * from "./penguin-species-island-sex.js";
export * from "./penguin-species-island.js";
diff --git a/test/plots/penguin-species-gradient.ts b/test/plots/penguin-species-gradient.ts
deleted file mode 100644
index 8724060cdb..0000000000
--- a/test/plots/penguin-species-gradient.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import * as Plot from "@observablehq/plot";
-import * as d3 from "d3";
-import {svg} from "htl";
-
-export async function penguinSpeciesGradient() {
- const penguins = await d3.csv("data/penguins.csv", d3.autoType);
- return Plot.plot({
- marks: [
- () => svg`
-
-
-
-
-
- `,
- Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", fill: "url(#gradient)"})),
- Plot.ruleY([0])
- ]
- });
-}
diff --git a/test/plots/penguin-species-group.ts b/test/plots/penguin-species-group.ts
deleted file mode 100644
index 94e03b58b9..0000000000
--- a/test/plots/penguin-species-group.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import * as Plot from "@observablehq/plot";
-import * as d3 from "d3";
-
-export async function penguinSpeciesGroup() {
- const penguins = await d3.csv("data/penguins.csv", d3.autoType);
- return Plot.plot({
- marks: [
- Plot.barX(penguins, Plot.stackX(Plot.groupZ({x: "proportion"}, {fill: "species"}))),
- Plot.text(penguins, Plot.stackX(Plot.groupZ({x: "proportion", text: "first"}, {z: "species", text: "species"}))),
- Plot.ruleX([0, 1])
- ]
- });
-}
diff --git a/test/plots/penguin-species-cheysson.ts b/test/plots/penguin-species.ts
similarity index 83%
rename from test/plots/penguin-species-cheysson.ts
rename to test/plots/penguin-species.ts
index 43aa0040db..c7eacdde31 100644
--- a/test/plots/penguin-species-cheysson.ts
+++ b/test/plots/penguin-species.ts
@@ -28,3 +28,51 @@ export async function penguinSpeciesCheysson() {
]
});
}
+
+export async function penguinSpeciesGradient() {
+ const penguins = await d3.csv("data/penguins.csv", d3.autoType);
+ return Plot.plot({
+ marks: [
+ () => svg`
+
+
+
+
+
+ `,
+ Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", fill: "url(#gradient)"})),
+ Plot.ruleY([0])
+ ]
+ });
+}
+
+export async function penguinSpeciesGroup() {
+ const penguins = await d3.csv("data/penguins.csv", d3.autoType);
+ return Plot.plot({
+ marks: [
+ Plot.barX(penguins, Plot.stackX(Plot.groupZ({x: "proportion"}, {fill: "species"}))),
+ Plot.text(penguins, Plot.stackX(Plot.groupZ({x: "proportion", text: "first"}, {z: "species", text: "species"}))),
+ Plot.ruleX([0, 1])
+ ]
+ });
+}
+
+export async function penguinSpeciesImageFilter() {
+ const penguins = await d3.csv("data/penguins.csv", d3.autoType);
+ return Plot.plot({
+ marks: [
+ Plot.barY(
+ penguins,
+ Plot.groupX(
+ {y: "count"},
+ {
+ x: "species",
+ fill: "species",
+ imageFilter: "drop-shadow(3px 3px red) sepia(40%) drop-shadow(-3px -3px currentColor)"
+ }
+ )
+ ),
+ Plot.ruleY([0])
+ ]
+ });
+}