feat(palette): variants v1 + grid token harmonization to 0.15#7692
Open
MarkusNeusinger wants to merge 6 commits into
Open
feat(palette): variants v1 + grid token harmonization to 0.15#7692MarkusNeusinger wants to merge 6 commits into
MarkusNeusinger wants to merge 6 commits into
Conversation
The style-guide had two contradicting values for grid/rule opacity: the Theme-adaptive Chrome token table at 0.10 vs the dedicated Grid Guidelines section "opacity 15-25%, very subtle". Per-library prompts (matplotlib, plotly, bokeh, makie, highcharts, altair, plotnine, seaborn rcParams) all inherited the 0.10 token; seaborn's hardcoded ax.yaxis.grid call already used 0.2 (following the verbal rule). Harmonize the spec at 0.15 — the lower edge of the Grid Guidelines band — so the token table and the verbal rule agree, and propagate to every library prompt that referenced the 0.10 token. Generated plots stay subtle but become clearly readable against the warm paper-ink surfaces. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… color wheel Second round of palette exploration (#5817 follow-up). v0 (variants A-F) compared against Okabe-Ito; v1 (this) measures everything against live D, which has shipped as ANYPLOT_PALETTE since the v0 round. New deliverable at docs/reference/palette-variants-v1/: - D-baseline.html — live anyplot palette rendered with the same template as candidates, so it sits in the lineup as the bar to beat - D1 tight-chroma — C ∈ [24, 32], position-1 pinned to red band [15°, 35°] so the corridor still yields a true crimson (#AE3030) rather than a rust pick - D3 expand-8 — live D's 7 hues + 1 freely-picked 8th hue (indigo #7981FD), diametrically opposite tan; fills both remaining wheel gaps without forcing a swap - T tetradic — 4 hue anchors 90° apart starting at brand green, 3 mid-quadrant fillers - W warm-pole — D's max-min selection plus a warm-pole scoring bonus centred at 55°, with #B71D27 pinned for semantic-red availability - index.html — hero color wheel with candidate-overlay toggles + baseline-card layout flip + Δ-vs-D coloring - compare.html — side-by-side card grid with D as the reference row New generator scripts/palette-variants-v1.py extends the v0 algorithm with: - forbidden_hue_bands / warm_bonus knobs in select_palette - tetradic + d-* strategy branches in _strategy_bands - n_hues parameter on Variant (D3 uses 8) - render_color_wheel — pre-rendered CAM02-UCS PNG disk (perceptually honest chroma fade, no slice seams) with palette dots placed at their actual (C, H) coords, optional chroma-corridor toggle, overlay live-D comparison scripts/_palette_common.py: bump v1 sample-chart gridlines from 0.06 to 0.15 (catch-up to the new style-guide token), bump PAGE_CSS --rule from 0.10 to 0.15 for the same consistency. v0 (palette-variants/) untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR (1) harmonizes the “grid/rule” opacity design token to 0.15 across generator prompts/style guide, and (2) adds a new v1 palette-variant diagnostic generator under scripts/ intended to produce docs/reference/palette-variants-v1/* HTML pages comparing candidates against the live variant D palette in core/images.py.
Changes:
- Update grid/rule opacity token from
0.10→0.15across the default style guide, plot generator prompt, and multiple per-library prompt snippets. - Add
scripts/palette-variants-v1.py, a new palette-variants v1 generator (baseline = live D) including a CAM02-UCS color wheel visualization. - Align the palette diagnostics shared CSS/sample-chart grid rendering in
scripts/_palette_common.pyto the new0.15rule/grid token.
Reviewed changes
Copilot reviewed 12 out of 19 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/palette-variants-v1.py | New v1 palette diagnostic generator (baseline vs live D) with variant pages, index, compare page, and color wheel rendering. |
| scripts/_palette_common.py | Updates diagnostic sample-chart gridline opacity and CSS --rule token to 0.15 for consistency. |
| prompts/default-style-guide.md | Updates the canonical grid/rule token to 0.15 and keeps reference snippet consistent. |
| prompts/plot-generator.md | Updates matplotlib-style grid alpha token to 0.15 in the generator guidance snippet. |
| prompts/library/seaborn.md | Updates seaborn rcParams grid.alpha guidance token to 0.15. |
| prompts/library/plotnine.md | Updates plotnine panel_grid_major alpha to 0.15. |
| prompts/library/plotly.md | Updates Plotly GRID rgba token to 0.15. |
| prompts/library/matplotlib.md | Updates matplotlib ax.grid alpha token to 0.15. |
| prompts/library/makie.md | Updates Makie grid RGBA alpha to 0.15 in examples. |
| prompts/library/highcharts.md | Updates Highcharts GRID rgba token to 0.15. |
| prompts/library/bokeh.md | Updates Bokeh grid line alpha guidance to 0.15. |
| prompts/library/altair.md | Updates Altair gridOpacity guidance token to 0.15. |
Comment on lines
+19
to
+27
| Five new candidates explore "refine vs. rethink": | ||
|
|
||
| D1 — d-tight-chroma (D's max-min but C ∈ [24, 32] — narrower paper-ink) | ||
| D2 — d-wide-spread (D's max-min with 60° pairwise hue spread target) | ||
| D3 — d-swap-tan (D's max-min but hue band [50°, 90°] banned at pos 6 | ||
| — forces an alternative to the live tan #BA843E) | ||
| T — tetradic (4 anchors 90° apart, brand-green anchored, 3 fillers) | ||
| W — warm-pole (D's max-min plus a warm-hue scoring bonus 30°–80°) | ||
|
|
Comment on lines
+358
to
+362
| """Pick 7 hues for a variant. Greedy max-min ΔE selection under all 4 | ||
| CVD conditions, with per-position hue bands and the per-variant chroma | ||
| corridor as candidate masks. If no candidate matches the strictest band, | ||
| the band half-width is widened in 10° steps until something fits. | ||
|
|
Comment on lines
+367
to
+373
| v1 additions: | ||
| - ``forbidden_hue_bands``: a list of (center_deg, half_width_deg) bands | ||
| to EXCLUDE from every position globally. Used by D3 (d-swap-tan) to | ||
| ban the tan band [50°, 90°] so a different 7th hue gets picked. | ||
| - ``warm_bonus``: (center_deg, half_width_deg, weight) — a soft additive | ||
| score bonus for candidates whose hue is within (half-width) of the | ||
| center. Used by W (warm-pole) to bias picks toward 30°–80°. |
Comment on lines
+1138
to
+1142
| Variant( | ||
| "D1", "tight-chroma", "d-tight-chroma", | ||
| "d-tight-chroma", | ||
| "live D's max-min ΔE selection but with the paper-ink chroma corridor narrowed to C ∈ [24, 32] — predicts cleaner co-existence in dense charts at the cost of some headroom. live D's semantic red #B71D27 is pinned at position 1 so loss/error/bad can map to the expected colour rather than a tight-corridor brown", | ||
| ), |
| import sys | ||
| from dataclasses import dataclass | ||
| from pathlib import Path | ||
| from typing import Callable |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
D1-8 mirrors D3's expand-8 approach for the tight-chroma corridor: D1's
7 picks leave a 75° purple→red back-gap, the 8th slot is greedy-picked
there for a matte rosé (#954477) that bridges purple and red while
staying inside C ∈ [24, 32].
Also introduces ``reorder_pure_cvd_greedy`` (opt-in via
USE_PURE_CVD_REORDER) for D1-8 — the original wheel-gap-first heuristic
in ``reorder_first_4`` was picking a 60°-valid but CVD-weak first-4
like {green, blue, tan, mauve} whenever the 8th hue opened new
gap-valid quadruples. Pure-CVD greedy keeps the worst-pair curve high:
n=4 lifts from 10.70 → 17.44 (now beats D3's 15.61 at the same n).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ortings
palette-variants-v2 narrows the field from v1's 5 candidates to the two
real contenders (vivid-8 = D3, muted-8 = D1-8) and adds tooling to
compare slot orderings side-by-side:
- hero with both color wheels (chroma corridor always visible) and
hue-sorted strips for direct hue-by-hue comparison between palettes
- sticky TOC linking the 4 sortings: pure-CVD greedy, wheel-gap-first,
hue-order (rainbow), every-other-hue (interleaved)
- per-sorting scorecard with per-n winner strips for CVD + normal
vision (green = vivid, blue = muted, grey = tie)
- per-cell chart stack: light theme block + dark theme block, each
with lines, bars (all 8), pie (first 4), stocks (first 4),
edge-cluster overlap scatter (centre mix)
- column-major pair grid so strips/tables align between palettes
even when intros wrap to different line counts
- per-n worst-pair ΔE table with normal-vision row + CVD-min row
Reuses v1 utilities via importlib (hyphen in filename).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment on lines
+82
to
+90
| _V1_SPEC = importlib.util.spec_from_file_location( | ||
| "palette_variants_v1", REPO_ROOT / "scripts" / "palette-variants-v1.py" | ||
| ) | ||
| assert _V1_SPEC is not None and _V1_SPEC.loader is not None | ||
| v1 = importlib.util.module_from_spec(_V1_SPEC) | ||
| # Register before exec_module so @dataclass can resolve cls.__module__ during | ||
| # class construction (otherwise dataclasses.py raises NoneType.__dict__). | ||
| sys.modules["palette_variants_v1"] = v1 | ||
| _V1_SPEC.loader.exec_module(v1) |
Comment on lines
+46
to
+48
| 1. pure-CVD greedy max-min (slowest possible per-n ΔE degradation; | ||
| pos 0 fixed brand-green; pos 1 fixed at | ||
| muted-8's semantic red for stability) |
Comment on lines
+11
to
+13
| """Palette variants v2 — head-to-head: vivid-8 (D3) vs muted-8 (D1-8). | ||
|
|
||
| v1 produced five candidate variants (D-baseline, D1, D1-8, D3, T, W). Two |
Comment on lines
+2077
to
+2082
| WARM_BONUS: dict[str, tuple[float, float, float]] = { | ||
| # W — additive bonus centred at 55° (warm orange-red), half-width 30°, | ||
| # weight 3.0 ΔE units at the centre. Strong enough to nudge selection | ||
| # toward warms without overriding the no-clash gap mask. | ||
| "warm-pole": (55.0, 30.0, 3.0), | ||
| } |
- Documented unanimous expert verdict favoring muted-8 over vivid-8 - Highlighted recurring themes and critiques from five independent reviews - Recommended next steps for palette optimization and semantic picking
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related changes bundled (separate commits for review):
1. Pipeline grid token harmonization (10 prompt files)
ax.yaxis.grid(…, alpha=0.2)already followed the verbal rule, leave as-is; its rcParams default bumped 0.10 → 0.15 for spec-alignmentplots/*/implementations/keep their baked 0.10 values until the next regen run (same staleness pattern as palette changes)2. Palette variants v1 — new diagnostic under
docs/reference/palette-variants-v1/v0 (variants A–F) compared against Okabe-Ito; v1 measures everything against live D, the palette currently shipping in
core/images.py.Five-card lineup:
#AE3030within corridor#7981FDlands diametrically opposite tan#BA843E— both wheel gaps filled, no swap needed#B71D27pinnedKey finding: live D's first-4 worst-CVD ΔE = 15.61 is at the Petroff "optimal" ceiling — every D-family micro-tweak lands at the same metric. D3 expand-8 is the only candidate that adds capability without a metric trade-off (strict superset of live D); adopting it in
core/images.pywould be a non-breaking append, separate PR.New v1 generator (
scripts/palette-variants-v1.py):select_palettewithforbidden_hue_bands+warm_bonusknobsd-tight-chroma,d-expand-8,tetradic,warm-pole) in_strategy_bandsn_huesparameter onVariant(D3 uses 8)render_color_wheel— pre-rendered CAM02-UCS PNG disk (perceptually honest chroma fade from neutral grey at centre to L=60/C=40 at rim, no slice seams), with palette dots placed at their actual (C, H) coords. Chroma-corridor toggle, overlay-live-D toggle on detail pages, hero wheel with candidate-overlay buttons on indexShared rendering helper (
scripts/_palette_common.py):--rulevariable bumped 0.10 → 0.15 (same consistency)v0 untouched —
scripts/palette-variants.pyanddocs/reference/palette-variants/preserved as historical record. Replaces what was previously parked onfeature/color-optimization(old branch deleted).Test plan
docs/reference/palette-variants-v1/index.html: hero wheel renders, candidate-overlay buttons swap overlay dots correctly, Δ-vs-D coloring shows green/red against the baselineD3-expand-8.html: 8 palette dots + chroma-corridor rings visible on the wheel, overlay-live-D toggle shows live D dots as outlined circles, theme toggle (◐ dark) still worksD1-tight-chroma.html: red#AE3030sits clearly inside the corridor rings, every dot inside corridorgrep -nE "0\.10" prompts/library/*.md prompts/plot-generator.md prompts/default-style-guide.mdreturns no grid-context hits (only seaborn's panel_grid_minor or non-grid uses)🤖 Generated with Claude Code