diff --git a/src/legends/ramp.js b/src/legends/ramp.js
index 3dcaf411c6..9b11b65c70 100644
--- a/src/legends/ramp.js
+++ b/src/legends/ramp.js
@@ -3,7 +3,7 @@ import {inferFontVariant} from "../axes.js";
import {createContext, create} from "../context.js";
import {map, maybeNumberChannel} from "../options.js";
import {interpolatePiecewise} from "../scales/quantitative.js";
-import {applyInlineStyles, impliedString, maybeClassName} from "../style.js";
+import {applyInlineStyles, impliedString, maybeClassName, offset} from "../style.js";
export function legendRamp(color, options) {
let {
@@ -164,6 +164,7 @@ export function legendRamp(color, options) {
.tickFormat(typeof tickFormat === "function" ? tickFormat : undefined)
.tickSize(tickSize)
.tickValues(Array.isArray(ticks) ? ticks : null)
+ .offset(offset)
)
.attr("font-size", null)
.attr("font-family", null)
diff --git a/src/style.js b/src/style.js
index 4fc1c9828a..3ab2105861 100644
--- a/src/style.js
+++ b/src/style.js
@@ -6,7 +6,11 @@ import {isNone, isNoneish, isRound, maybeColorChannel, maybeNumberChannel} from
import {keyof, number, string} from "./options.js";
import {warn} from "./warnings.js";
-export const offset = (typeof window !== "undefined" ? window.devicePixelRatio > 1 : typeof it === "undefined") ? 0 : 0.5; // prettier-ignore
+export let offset = typeof window === "undefined" || window.devicePixelRatio > 1 ? 0 : 0.5;
+
+export function setOffset(o) {
+ offset = o;
+}
let nextClipId = 0;
let nextPatternId = 0;
diff --git a/test/output/offsets.html b/test/output/offsets.html
new file mode 100644
index 0000000000..8b4d3f5a76
--- /dev/null
+++ b/test/output/offsets.html
@@ -0,0 +1,739 @@
+
\ No newline at end of file
diff --git a/test/plot.js b/test/plot.js
index 74c5895054..d9c238dd6a 100644
--- a/test/plot.js
+++ b/test/plot.js
@@ -3,10 +3,13 @@ import {createCanvas, loadImage} from "canvas";
import {max, mean, quantile} from "d3";
import * as path from "path";
import beautify from "js-beautify";
+import {setOffset} from "../src/style.js";
import assert from "./assert.js";
import it from "./jsdom.js";
import * as plots from "./plots/index.ts"; // TODO index.js
+setOffset(0.5);
+
for (const [name, plot] of Object.entries(plots)) {
it(`plot ${name}`, async () => {
const root = await (name.startsWith("warn") ? assert.warnsAsync : assert.doesNotWarnAsync)(plot);
diff --git a/test/plots/index.ts b/test/plots/index.ts
index f54398dca7..3c019e678f 100644
--- a/test/plots/index.ts
+++ b/test/plots/index.ts
@@ -186,6 +186,7 @@ export * from "./multiplication-table.js";
export * from "./music-revenue.js";
export * from "./nested-facets.js";
export * from "./npm-versions.js";
+export * from "./offset.js";
export * from "./opacity.js";
export * from "./ordinal-bar.js";
export * from "./pairs.js";
diff --git a/test/plots/offset.ts b/test/plots/offset.ts
new file mode 100644
index 0000000000..59b53c8f21
--- /dev/null
+++ b/test/plots/offset.ts
@@ -0,0 +1,33 @@
+import * as Plot from "@observablehq/plot";
+import * as d3 from "d3";
+import {html} from "htl";
+import {setOffset} from "../../src/style.js";
+
+export async function offsets() {
+ const penguins = await d3.csv("data/penguins.csv", d3.autoType);
+ return html`${Array.from([0, 0.5], (offset) => {
+ setOffset(offset);
+ const color = offset ? "blue" : "red";
+ return Plot.plot({
+ style: "position: absolute; top: 70px;",
+ grid: color,
+ height: 300,
+ marks: [
+ Plot.axisX({fill: color, stroke: color}),
+ Plot.axisY({fill: color, stroke: color}),
+ Plot.rect(
+ penguins,
+ Plot.bin(
+ {fillOpacity: "count"},
+ {
+ x: "culmen_depth_mm",
+ y: "culmen_length_mm",
+ fill: color,
+ thresholds: 50
+ }
+ )
+ )
+ ]
+ });
+ })}`;
+}