diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b0b90fd94..624ed6d086 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,124 @@
Year: **Current (2023)** · [2022](./CHANGELOG-2022.md) · [2021](./CHANGELOG-2021.md)
+## 0.6.12
+
+[Released December 7, 2023.](https://github.com/observablehq/plot/releases/tag/v0.6.12)
+
+To better support dark mode, we’ve made a breaking change to default styles: the background color is now `unset` (transparent) instead of `white`. Additionally, marks that expect to use the same color as the background (for instance, the tip mark) now use the CSS custom property `--plot-background` instead of `white`.
+
+As since [version 0.6.6](#066), you can override Plot’s styles via the `plot-d6a7b5` class. For example, the following stylesheet applies a dark background and white foreground:
+
+```css
+.plot-d6a7b5 {
+ --plot-background: #333;
+ background: var(--plot-background);
+ color: white;
+}
+```
+
+Previously Plot forced you to choose between [rect](https://observablehq.com/plot/marks/rect), [bar](https://observablehq.com/plot/marks/bar), and [cell](https://observablehq.com/plot/marks/cell) based on whether the *x* and *y* scales were ordinal or quantitative. Rejoice: the [rect mark](https://observablehq.com/plot/marks/rect) now supports *band* scales so you can simply use rect in all cases! 🎉 (The bar and cell marks still offer conveniences for ordinal scales, so use them if you like.)
+
+
+
+```js
+Plot.rectY(alphabet, {x: "letter", y: "frequency"}).plot()
+```
+
+Categorical color scales now default to the new *observable10* color scheme by [Jeff Pettiross](https://github.com/pettiross). These colors are intended as a drop-in replacement for *tableau10* with similar ease of discrimination and ordering, but with a slightly more saturated vibe that helps charts pop.
+
+
+
+```js
+Plot.cellX(d3.range(10)).plot({color: {type: "categorical"}})
+```
+
+The new [difference mark](https://observablehq.com/plot/marks/difference) puts a metric in context by comparing it to another metric or constant value. Like the [area mark](https://observablehq.com/plot/marks/area), the region between two lines is filled; unlike the area mark, alternating color shows when the metric is above or below the comparison.
+
+
+
+```js
+Plot.differenceY(gistemp, Plot.windowY(28, {x: "Date", y: "Anomaly"})).plot()
+```
+
+The chart above shows a moving average of global temperature anomaly; above-average temperatures are shown in green and below-average temperatures are shown in blue.
+
+The difference mark can also compare a metric to its earlier self, showing change over time. The chart below shows the year-over-year change in Apple stock price; the gray region represents an increase over last year while the red region represents a decrease.
+
+
+
+```js
+Plot.differenceY(
+ aapl,
+ Plot.shiftX("year", {
+ x: "Date",
+ y: "Close",
+ positiveFillOpacity: 0.2,
+ positiveFill: "currentColor",
+ negativeFillOpacity: 0.8,
+ negativeFill: "red"
+ })
+).plot()
+```
+
+The chart above is constructed using the new [shift transform](https://observablehq.com/plot/transforms/shift), which derives a time-shifted copy of a metric for the given time interval, enabling year–over-year, month-over-month, or any other period-over-period comparison.
+
+The new [find reducer](https://observablehq.com/plot/transforms/group#find) allows you to pivot data with the [bin](https://observablehq.com/plot/transforms/bin) and [group](https://observablehq.com/plot/transforms/group) transforms, effectively turning “tall” data (with fewer columns) into “wide” data (with more columns). For example, say you have time-series data that has separate rows for observed daily temperatures in San Francisco (SF) and San Jose (SJ):
+
+```csv
+date,station,tmax,tmin
+2020-12-31,SJ,59,43
+2020-12-31,SF,60,47
+2020-12-30,SJ,58,33
+2020-12-30,SF,57,40
+2020-12-29,SJ,62,38
+2020-12-29,SF,61,41
+2020-12-28,SJ,57,43
+2020-12-28,SF,56,47
+2020-12-27,SJ,62,43
+2020-12-27,SF,57,46
+2020-12-26,SJ,60,46
+2020-12-26,SF,61,49
+```
+
+To compare the average minimum temperatures of San Francisco and San Jose as another difference chart:
+
+
+
+```js
+Plot.plot({
+ x: {tickFormat: "%b"},
+ y: {grid: true},
+ marks: [
+ Plot.ruleY([32]),
+ Plot.differenceY(
+ temperature,
+ Plot.windowY(
+ 14,
+ Plot.groupX(
+ {y1: Plot.find((d) => d.station === "SJ"), y2: Plot.find((d) => d.station === "SF")},
+ {x: "date", y: "tmin"}
+ )
+ )
+ )
+ ]
+})
+```
+
+The green region above represents when San Francisco was warmer than San Jose, and the blue region when San Francisco was cooler than San Jose.
+
+The [tip mark](https://observablehq.com/plot/marks/tip) now supports a **preferredAnchor** option, providing greater control over tip placement: if the tip fits within the frame at the preferred anchor, this anchor will be used. (In contrast, the **anchor** option uses the specified anchor regardless of whether it will fit.) The tip mark now also prefers the *bottom* anchor by default for a more traditional, less comic-like appearance.
+
+The [**marker** option](https://observablehq.com/plot/features/markers) now supports new marker types: *tick*, *tick-x*, and *tick-y*. The [bin](https://observablehq.com/plot/transforms/bin) and [group](https://observablehq.com/plot/transforms/group) transforms now pass _data_ to reducers. The [group](https://observablehq.com/plot/transforms/group) and [hexbin](https://observablehq.com/plot/transforms/hexbin) transforms now support _x_ and _y_ reducers.
+
+This release includes several additional fixes:
+
+* The default axis for a *time* scale now uses local time.
+* The text mark’s **lineWidth** option is now more accurate when **monospace** is true.
+* The tip mark no longer truncates **title** text.
+* The scale **type** option is now case-insensitive.
+* Transform type definitions have correct overload precedence.
+
## 0.6.11
[Released September 20, 2023.](https://github.com/observablehq/plot/releases/tag/v0.6.11)
diff --git a/docs/features/markers.md b/docs/features/markers.md
index d9396ac699..82f306c8c6 100644
--- a/docs/features/markers.md
+++ b/docs/features/markers.md
@@ -56,9 +56,9 @@ The following named markers are supported:
* *dot* - a filled *circle* without a stroke and 2.5px radius
* *circle*, equivalent to *circle-fill* - a filled circle with a white stroke and 3px radius
* *circle-stroke* - a hollow circle with a colored stroke and a white fill and 3px radius
-* *tick* - a small opposing line
-* *tick-x* - a small horizontal line
-* *tick-y* - a small vertical line
+* *tick* - a small opposing line
+* *tick-x* - a small horizontal line
+* *tick-y* - a small vertical line
If **marker** is true, it defaults to *circle*. If **marker** is a function, it will be called with a given *color* and must return an [SVG marker element](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/marker).
diff --git a/docs/features/scales.md b/docs/features/scales.md
index 52f3e2fc39..78b0da69ba 100644
--- a/docs/features/scales.md
+++ b/docs/features/scales.md
@@ -11,7 +11,7 @@ const align = ref(0.5);
const radius = ref(8);
const schemeq = ref("turbo");
const schemed = ref("rdbu");
-const schemeo = ref("Tableau10");
+const schemeo = ref("Observable10");
const interpolateq = ref("rgb");
const anomaly = gistemp.map((d) => d.Anomaly);
const aapl = shallowRef([]);
@@ -457,6 +457,7 @@ Plot also provides color schemes for discrete data. Use the *categorical* type f
+
@@ -730,7 +731,7 @@ Plot.plot({
The normal scale types — *linear*, *sqrt*, *pow*, *log*, *symlog*, and *ordinal* — can be used to encode color. In addition, Plot supports special scale types for color:
-* *categorical* - like *ordinal*, but defaults to *tableau10*
+* *categorical* - like *ordinal*, but defaults to *observable10*
* *sequential* - like *linear*
* *cyclical* - like *linear*, but defaults to *rainbow*
* *threshold* - discretizes using thresholds given as the **domain**; defaults to *rdylbu*
@@ -878,6 +879,7 @@ Plot.plot({
["Accent", d3.schemeAccent],
["Category10", d3.schemeCategory10],
["Dark2", d3.schemeDark2],
+ ["Observable10", Plot.scale({color: {type: "categorical"}}).range],
["Paired", d3.schemePaired],
["Pastel1", d3.schemePastel1],
["Pastel2", d3.schemePastel2],
diff --git a/docs/marks/difference.md b/docs/marks/difference.md
index 9480ef259b..f551e2d7ea 100644
--- a/docs/marks/difference.md
+++ b/docs/marks/difference.md
@@ -18,7 +18,7 @@ onMounted(() => {
-# Difference mark
+# Difference mark
The **difference mark** puts a metric in context by comparing it. Like the [area mark](./area.md), the region between two lines is filled; unlike the area mark, alternating color shows when the metric is above or below the comparison value.
diff --git a/docs/marks/tip.md b/docs/marks/tip.md
index 0079e8afb9..388d10c7de 100644
--- a/docs/marks/tip.md
+++ b/docs/marks/tip.md
@@ -184,7 +184,7 @@ Plot.plot({
```
:::
-If you don’t specify an explicit **anchor**, the tip mark will choose one automatically, using the **preferredAnchor** if it fits. The preferred anchor defaults to *bottom*, except when using the **tip** option and the [pointerY pointing mode](../interactions/pointer.md), in which case it defaults to *left*. In some cases, it may not be possible to fit the tip within the plot’s frame; consider setting the plot’s **style** to `overflow: visible;` to prevent the tip from being truncated.
+If you don’t specify an explicit **anchor**, the tip mark will choose one automatically, using the **preferredAnchor** if it fits. The preferred anchor defaults to *bottom*, except when using the **tip** option and the [pointerY pointing mode](../interactions/pointer.md), in which case it defaults to *left*. In some cases, it may not be possible to fit the tip within the plot’s frame; consider setting the plot’s **style** to `overflow: visible;` to prevent the tip from being truncated.
The tip mark is compatible with transforms that derive **x** and **y** dynamically from data, such as the [centroid transform](../transforms/centroid.md) which computes polygon centroids. Below, a map of the United States shows state names. We reduce the size of the tips by setting the **textPadding** option to 3 pixels instead of the default 8.
diff --git a/docs/transforms/group.md b/docs/transforms/group.md
index df128f8dcc..9108d4f7d8 100644
--- a/docs/transforms/group.md
+++ b/docs/transforms/group.md
@@ -368,8 +368,8 @@ The following named reducers are supported:
* *deviation* - the standard deviation
* *variance* - the variance per [Welford’s algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm)
* *identity* - the array of values
-* *x* - the group’s *x* value (when grouping on *x*)
-* *y* - the group’s *y* value (when grouping on *y*)
+* *x* - the group’s *x* value (when grouping on *x*)
+* *y* - the group’s *y* value (when grouping on *y*)
In addition, a reducer may be specified as:
@@ -440,7 +440,7 @@ Plot.groupZ({x: "proportion"}, {fill: "species"})
Groups on the first channel of **z**, **fill**, or **stroke**, if any. If none of **z**, **fill**, or **stroke** are channels, then all data (within each facet) is placed into a single group.
-## find(*test*) {#find}
+## find(*test*) {#find}
```js
Plot.groupX(
diff --git a/docs/transforms/hexbin.md b/docs/transforms/hexbin.md
index 1ca25d311b..91198eaf64 100644
--- a/docs/transforms/hexbin.md
+++ b/docs/transforms/hexbin.md
@@ -197,8 +197,8 @@ The following named reducers are supported:
* *variance* - the variance per [Welford’s algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm)
* *mode* - the value with the most occurrences
* *identity* - the array of values
-* *x* - the hexagon’s *x* center
-* *y* - the hexagon’s *y* center
+* *x* - the hexagon’s *x* center
+* *y* - the hexagon’s *y* center
In addition, a reducer may be specified as:
diff --git a/docs/transforms/shift.md b/docs/transforms/shift.md
index 8ab45d3a73..297dbeb8b5 100644
--- a/docs/transforms/shift.md
+++ b/docs/transforms/shift.md
@@ -13,7 +13,7 @@ onMounted(() => {
-# Shift transform
+# Shift transform
The **shift transform** is a specialized [map transform](./map.md) that derives an output **x1** channel by shifting the **x** channel; it can be used with the [difference mark](../marks/difference.md) to show change over time. For example, the chart below shows the price of Apple stock. The green region shows when the price went up over the given interval, while the blue region shows when the price went down.
diff --git a/img/difference-find.png b/img/difference-find.png
new file mode 100644
index 0000000000..06ef816340
Binary files /dev/null and b/img/difference-find.png differ
diff --git a/img/difference-shift.png b/img/difference-shift.png
new file mode 100644
index 0000000000..fa01ff0ba8
Binary files /dev/null and b/img/difference-shift.png differ
diff --git a/img/difference-zero.png b/img/difference-zero.png
new file mode 100644
index 0000000000..7cb3a5c9db
Binary files /dev/null and b/img/difference-zero.png differ
diff --git a/img/observable10.png b/img/observable10.png
new file mode 100644
index 0000000000..284a5aacbf
Binary files /dev/null and b/img/observable10.png differ
diff --git a/img/rect-band.png b/img/rect-band.png
new file mode 100644
index 0000000000..dd82d59dd6
Binary files /dev/null and b/img/rect-band.png differ
diff --git a/package.json b/package.json
index a3258edfc3..2a9dfe76dd 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@observablehq/plot",
"description": "A JavaScript library for exploratory data visualization.",
- "version": "0.6.11",
+ "version": "0.6.12",
"author": {
"name": "Observable, Inc.",
"url": "https://observablehq.com"