Skip to content

ENG-10444: Add high-level theme presets to the declarative API#114

Open
FarhanAliRaza wants to merge 6 commits into
mainfrom
farhan/eng-10444-add-high-level-themes-to-the-declarative-xy-api
Open

ENG-10444: Add high-level theme presets to the declarative API#114
FarhanAliRaza wants to merge 6 commits into
mainfrom
farhan/eng-10444-add-high-level-themes-to-the-declarative-xy-api

Conversation

@FarhanAliRaza

Copy link
Copy Markdown
Contributor

Summary

Adds an ergonomic high-level theming layer to xy.theme() while keeping the existing token-based escape hatches and export behavior intact:

xy.theme(
    preset="dashboard",       # xy | minimal | dashboard | publication | high_contrast
    color_scheme="dark",      # light | dark | system
    palette="vibrant",        # xy | okabe_ito/colorblind | vibrant | muted
    accent="#8b5cf6",         # selection / focus / active emphasis
    contrast="normal",        # normal | high (CVD-safe palette + stronger chrome)
)

Design

  • Everything resolves Python-side (new pure python/xy/_themes.py: immutable preset catalog + resolver). Presets emit only the implemented --chart-* token vocabulary plus root background, and a default mark palette threaded through Figure.default_palette into mark building — so browser, standalone HTML, Python SVG, native PNG, and Chromium export all agree by construction. No new JS token vocabulary.
  • color_scheme="system" ships both variants on the wire (dom.style light + dom.styleDark); the render client swaps them on the existing prefers-color-scheme listener. Browser-free exports (SVG, native PNG) and headless Chromium deterministically use the light variant — pass color_scheme="dark" to export dark. Documented.
  • Resolution order (later wins), per the issue and now documented in docs/styling/themes-and-tokens.md: engine defaults → preset → color scheme → palette/accent/contrast → named low-level args → theme(style={}) → chart style={} → explicit mark colors.
  • Validation & discovery: unknown preset/palette/scheme/contrast/accent values raise ValueError listing the valid options; xy.theme_presets() / xy.theme_palettes() enumerate the catalog. Matplotlib-compatible stylesheet names stay in xy.pyplot.style.
  • Presets always set root background in both schemes, preserving the data-xy-own-bg / VS Code white-card behavior.

Docs

  • "Built-in presets" section in Themes and Tokens (knobs, precedence, export behavior).
  • New Theme Preset Gallery page (docs/styling/theme-presets.md) rendering all five presets in light and dark, wired into the docs nav.

Testing

  • New tests/test_themes.py: catalog integrity (implemented-tokens-only, hex palettes), resolver purity/immutability, actionable validation errors, backward compat, full precedence chain, system dual-variant wire spec, palette→marks/categorical with explicit colors winning.
  • PNG/SVG export tests assert dark preset output and the deterministic light fallback for system.
  • Full suite: 1877 passed, 66 skipped (reflex-dependent skips); ruff check/format clean; node js/build.mjs reproduces the committed bundles byte-identically; scripts/render_smoke_nonumpy.py passes.
  • Browser-verified the live system toggle with a headless-Chromium probe (stubbed matchMedia): tokens flip light→dark→light on the change event with no errors and no MutationObserver feedback loop.

Closes ENG-10444.

xy.theme() gains preset/color_scheme/palette/accent/contrast on top of
the existing token escape hatches. Presets resolve Python-side into the
implemented --chart-* vocabulary and a default mark palette threaded
through Figure, so browser, standalone HTML, SVG, native PNG, and
Chromium exports all agree by construction. color_scheme="system" ships
both variants (dom.style + dom.styleDark) and the client swaps them on
prefers-color-scheme; browser-free exports deterministically use light.
Unknown names fail with actionable errors; xy.theme_presets() and
xy.theme_palettes() cover discovery. Includes docs, a preset gallery
page, and catalog/precedence/wire/export tests.
@linear-code

linear-code Bot commented Jul 20, 2026

Copy link
Copy Markdown

ENG-10444

@codspeed-hq

codspeed-hq Bot commented Jul 20, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 94 untouched benchmarks
⏩ 1 skipped benchmark1


Comparing farhan/eng-10444-add-high-level-themes-to-the-declarative-xy-api (a83f244) with main (c2a61d8)

Open in CodSpeed

Footnotes

  1. 1 benchmark was skipped, so the baseline result was used instead. If it was deleted from the codebase, click here and archive it to remove it from the performance reports.

@masenf masenf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after rebasing on main, which had the light/dark on html root fix, we are rendering the chart area with the outer page's color mode class rather than the chart-specific one

in light mode:

Image

in dark mode:

Image

Scheme-pinned presets set the figure background inline but left
--chart-bg, --chart-legend-bg, and --chart-modebar-bg inheritable, so a
host page defining those tokens per color mode (the docs styling surface
and app-level chart style both do) repainted a pinned chart's plot rect
and legend/modebar chips to the page's mode — dark plot areas inside
pinned-light cards and unreadable legend text.

Every preset variant now pins all three surfaces with the engine's own
scheme defaults (plot rect transparent so the figure background shows
through), which changes nothing outside token-setting hosts. The SVG
exporter treats fully transparent background paints as omitted so
exports don't gain dead transparent rects.
@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds high-level theme presets to the declarative chart API. The main changes are:

  • Built-in presets, palettes, contrast, accent, and color-scheme options.
  • System light and dark token switching in browser charts.
  • Theme palette propagation across marks and static exports.
  • Public preset and palette discovery APIs.
  • Documentation, gallery examples, and export coverage.

Confidence Score: 5/5

No blocking issue was identified.

Accent alpha handling preserves accepted eight-digit colors, and no separate issue qualifies for a comment.

T-Rex T-Rex Logs

What T-Rex did

  • Ran light and dark theme transitions to verify rendering across three canvases with the context state 'ready', and confirmed there were no page or console errors.
  • Observed that each transition incurred eight style mutations on the root and that the count remained eight during an additional one-second dark-state observation, indicating no MutationObserver feedback loop.
  • Verified that the initial light values were restored after the dark toggle.
  • Compared the pre-feature baseline to the post-transition state, noting the pre-feature remained light through the same toggle sequence.
  • Cataloged and reviewed visual and video artifacts (two transition videos and several chart frames) to support inspection of rendering and state changes.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
python/xy/_themes.py Defines the preset catalog, palette catalog, accent expansion, validation, and theme resolver.
python/xy/components.py Extends the public theme API and applies resolved theme state when building figures.
js/src/50_chartview.js Applies system light and dark tokens and refreshes charts when the preferred scheme changes.
python/xy/_figure.py Carries theme palettes and system dark styles into marks and the browser payload.
tests/test_themes.py Covers theme validation, resolution, precedence, wire output, palettes, and accent behavior.

Reviews (2): Last reviewed commit: "fix(themes): honor accent alpha instead ..." | Re-trigger Greptile

Comment thread python/xy/components.py
Comment thread python/xy/_themes.py Outdated
…high-level-themes-to-the-declarative-xy-api

# Conflicts:
#	examples/demo-advance.ipynb
#	python/xy/_svg.py
_normalize_accent advertised #rrggbbaa in its error message and accepted
it, then truncated to the opaque base — so accent="#8b5cf680" rendered
selection, zoom, focus, and modebar-active at full opacity. The solid
tokens now keep the caller's alpha; the faint fills still derive from
the opaque base so appending the fixed fill alpha stays a valid 8-digit
hex.
@FarhanAliRaza

FarhanAliRaza commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Fixed
image

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants