From a1e7f7cdbc6ceeac0c80ddf960c770e33a94f61d Mon Sep 17 00:00:00 2001 From: zmoon Date: Fri, 10 Apr 2026 09:49:02 -0500 Subject: [PATCH 01/10] Get the df styling back --- docs/_static/custom.css | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 9dbd6d5..3355d3d 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -1,6 +1,48 @@ +/* Pandas tables. Pulled from the Jupyter / nbsphinx CSS + Included with MyST-NB until removal in v1.4.0 (Mar 2026). +*/ +div.cell_output table { + border: none; + border-collapse: collapse; + border-spacing: 0; + color: black; + font-size: 1em; + table-layout: fixed; +} + +div.cell_output thead { + border-bottom: 1px solid black; + vertical-align: bottom; +} + +div.cell_output tr, +div.cell_output th, +div.cell_output td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} + +div.cell_output th { + font-weight: bold; +} + +div.cell_output tbody tr:nth-child(odd) { + background: #f5f5f5; +} + +div.cell_output tbody tr:hover { + background: rgba(66, 165, 245, 0.2); +} + table.dataframe { font-size: 0.8em !important; } + figcaption p { font-size: 85%; line-height: 1.3; From 757c235351e3b3ea0dfb1102e74a6c3122933fb7 Mon Sep 17 00:00:00 2001 From: zmoon Date: Fri, 10 Apr 2026 11:58:36 -0500 Subject: [PATCH 02/10] Add responsive setting current on by default, but may change that --- pyabc2/abcjs/widget/__init__.py | 7 +++++++ pyabc2/abcjs/widget/index.css | 4 ++++ pyabc2/abcjs/widget/index.js | 15 +++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/pyabc2/abcjs/widget/__init__.py b/pyabc2/abcjs/widget/__init__.py index 9ddb999..2d91df5 100644 --- a/pyabc2/abcjs/widget/__init__.py +++ b/pyabc2/abcjs/widget/__init__.py @@ -82,6 +82,13 @@ class ABCJSWidget(anywidget.AnyWidget): 740, help="Width of the staff in pixels.", ).tag(sync=True) + responsive = traitlets.Bool( + True, + help=( + "Whether the rendering should be responsive to container width " + "(up to `staff_width` + some padding)." + ), + ).tag(sync=True) transpose = traitlets.Integer( 0, help="Visual transpose in half steps.", diff --git a/pyabc2/abcjs/widget/index.css b/pyabc2/abcjs/widget/index.css index 5a5f1bd..3392b5d 100644 --- a/pyabc2/abcjs/widget/index.css +++ b/pyabc2/abcjs/widget/index.css @@ -1,3 +1,7 @@ +div.container { + max-width: var(--staff-max-width, none); +} + div.container.debug { color: forestgreen; border: 1px solid forestgreen; diff --git a/pyabc2/abcjs/widget/index.js b/pyabc2/abcjs/widget/index.js index 7ec5050..372075e 100644 --- a/pyabc2/abcjs/widget/index.js +++ b/pyabc2/abcjs/widget/index.js @@ -53,6 +53,7 @@ function render({ model, el }) { let showDebugInput = () => model.get('debug_input'); let showLogo = () => model.get('logo'); let staffwidth = () => model.get('staff_width'); + let doResize = () => model.get('responsive'); let visualTranspose = () => model.get('transpose'); let active_music_ids = model.get("_active_music_ids"); @@ -113,6 +114,19 @@ function render({ model, el }) { if (showDebugBox()) {showDebug.push('box')}; if (showDebugGrid()) {showDebug.push('grid')}; + // Responsive setting + let responsive = doResize() ? "resize" : undefined; + // We drive max-width via a CSS custom property so ABCJS's async ResizeObserver + // (which may clear inline max-width) cannot override it. + // ABCJS docs say left and right padding in the SVG are 15 and 50, + // so we try to give enough space for that, but note that in practice + // the results are not identical to responsive-off. + if (doResize()) { + container.style.setProperty('--staff-max-width', (staffwidth() + 65) + 'px'); + } else { + container.style.removeProperty('--staff-max-width'); + } + // NOTE: doesn't work with `music_id` passed as target, // even though it should, still not sure why let tunes = ABCJS.renderAbc( @@ -121,6 +135,7 @@ function render({ model, el }) { { foregroundColor: foregroundColor(), lineThickness: lineThickness(), + responsive: responsive, scale: scale(), showDebug: showDebug, staffwidth: staffwidth(), From 248f8e3ad19bd28dc9c697fda29f3368e3e408d7 Mon Sep 17 00:00:00 2001 From: zmoon Date: Fri, 10 Apr 2026 17:27:28 -0500 Subject: [PATCH 03/10] Default to non-responsive but enable scroll --- pyabc2/abcjs/widget/__init__.py | 2 +- pyabc2/abcjs/widget/index.css | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pyabc2/abcjs/widget/__init__.py b/pyabc2/abcjs/widget/__init__.py index 2d91df5..38a0e12 100644 --- a/pyabc2/abcjs/widget/__init__.py +++ b/pyabc2/abcjs/widget/__init__.py @@ -83,7 +83,7 @@ class ABCJSWidget(anywidget.AnyWidget): help="Width of the staff in pixels.", ).tag(sync=True) responsive = traitlets.Bool( - True, + False, help=( "Whether the rendering should be responsive to container width " "(up to `staff_width` + some padding)." diff --git a/pyabc2/abcjs/widget/index.css b/pyabc2/abcjs/widget/index.css index 3392b5d..5eb553a 100644 --- a/pyabc2/abcjs/widget/index.css +++ b/pyabc2/abcjs/widget/index.css @@ -1,5 +1,6 @@ div.container { max-width: var(--staff-max-width, none); + overflow-x: auto; } div.container.debug { @@ -7,6 +8,12 @@ div.container.debug { border: 1px solid forestgreen; } +div.music { + /* ABCJS seems to set `overflow: hidden` on the music div, + which prevents the container's auto overflow from working. */ + overflow: visible !important; +} + div.music.debug { border: 1px dashed grey; } From bca1ff9fc46d8132bc091b3ce229df5cf0421237 Mon Sep 17 00:00:00 2001 From: zmoon Date: Fri, 10 Apr 2026 17:30:51 -0500 Subject: [PATCH 04/10] Make our widget container class name more specific to avoid selecting divs we don't mean to --- pyabc2/abcjs/widget/index.css | 6 +++--- pyabc2/abcjs/widget/index.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyabc2/abcjs/widget/index.css b/pyabc2/abcjs/widget/index.css index 5eb553a..c90755d 100644 --- a/pyabc2/abcjs/widget/index.css +++ b/pyabc2/abcjs/widget/index.css @@ -1,9 +1,9 @@ -div.container { +div.abcjs-widget-container { max-width: var(--staff-max-width, none); overflow-x: auto; } -div.container.debug { +div.abcjs-widget-container.debug { color: forestgreen; border: 1px solid forestgreen; } @@ -18,6 +18,6 @@ div.music.debug { border: 1px dashed grey; } -div.container code { +div.abcjs-widget-container code { white-space: pre-wrap; } diff --git a/pyabc2/abcjs/widget/index.js b/pyabc2/abcjs/widget/index.js index 372075e..7d72eb5 100644 --- a/pyabc2/abcjs/widget/index.js +++ b/pyabc2/abcjs/widget/index.js @@ -61,7 +61,7 @@ function render({ model, el }) { console.log(`first_load ${first_load}`); let container = el; - container.classList.add('container'); + container.classList.add('abcjs-widget-container'); if ((first_load || showLogo()) && !hide()) { let logo = document.createElement('img'); From cfc2ffa57ce8936c9f9fc65cb06ae99a648706b7 Mon Sep 17 00:00:00 2001 From: zmoon Date: Sun, 12 Apr 2026 17:33:54 -0500 Subject: [PATCH 05/10] Use Furo variables mainly so it looks better in dark mode but keep the odd/even background thing for now (note Furo's table style doesn't do this) --- docs/_static/custom.css | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 3355d3d..c311e6d 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -5,16 +5,14 @@ div.cell_output table { border: none; border-collapse: collapse; border-spacing: 0; - color: black; + color: var(--color-foreground-primary); font-size: 1em; table-layout: fixed; } - div.cell_output thead { - border-bottom: 1px solid black; + border-bottom: 1px solid var(--color-table-border); vertical-align: bottom; } - div.cell_output tr, div.cell_output th, div.cell_output td { @@ -26,19 +24,19 @@ div.cell_output td { max-width: none; border: none; } - div.cell_output th { font-weight: bold; + /* background: var(--color-table-header-background); */ } - div.cell_output tbody tr:nth-child(odd) { - background: #f5f5f5; + background: var(--color-background-secondary); } - div.cell_output tbody tr:hover { - background: rgba(66, 165, 245, 0.2); + background: var(--color-background-hover); } +/* Overrides */ + table.dataframe { font-size: 0.8em !important; } From 02b7ab14dc0b9ab5fbdd3a6788031e37da9a615c Mon Sep 17 00:00:00 2001 From: zmoon Date: Sun, 12 Apr 2026 18:24:07 -0500 Subject: [PATCH 06/10] Style the ipywidgets, including in dark mode --- docs/_static/custom.css | 80 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/docs/_static/custom.css b/docs/_static/custom.css index c311e6d..29247e0 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -35,7 +35,85 @@ div.cell_output tbody tr:hover { background: var(--color-background-hover); } -/* Overrides */ +/* ipywidgets dark mode support. + The widgets CSS uses --jp-* variables throughout, but Furo doesn't define them. + The embed bundle injects the --jp-* defaults in :root *after* page load, so + remapping them is unreliable. Instead we directly override the widget selectors + with Furo's --color-* variables, which already adapt to light/dark mode. + noUiSlider (used for sliders) has entirely hardcoded colors, so those are + also overridden here. +*/ + +/* Text / label colors */ +.jupyter-widgets, +.widget-label, .jupyter-widget-label, +.widget-label-basic, .jupyter-widget-label-basic, +.widget-readout, .jupyter-widget-readout { + color: var(--color-foreground-primary) !important; +} + +/* Input / textarea / select backgrounds */ +.jupyter-widgets input[type="text"], +.jupyter-widgets input[type="number"], +.jupyter-widgets input[type="password"], +.jupyter-widgets textarea, +.jupyter-widgets select { + background-color: var(--color-background-secondary) !important; + color: var(--color-foreground-primary) !important; + border-color: var(--color-background-border) !important; +} + +/* Checkboxes: use brand color for the check/tick instead of bright white/blue */ +.jupyter-widgets input[type="checkbox"] { + accent-color: var(--color-brand-primary) !important; +} + +/* Checkbox body: render native checkbox in dark style in dark mode */ +body[data-theme="dark"] .jupyter-widgets input[type="checkbox"] { + color-scheme: dark; +} +@media (prefers-color-scheme: dark) { + body:not([data-theme="light"]) .jupyter-widgets input[type="checkbox"] { + color-scheme: dark; + } +} + +/* Color picker: tone down the bright border */ +.jupyter-widgets input[type="color"] { + background-color: var(--color-background-secondary) !important; + border-color: var(--color-background-border) !important; +} + +/* Buttons */ +.jupyter-button { + background-color: var(--color-background-secondary) !important; + color: var(--color-foreground-primary) !important; + border-color: var(--color-background-border) !important; +} +.jupyter-button.mod-primary { + background-color: var(--color-brand-primary) !important; + color: white !important; +} + +/* noUiSlider track, handle, and active range */ +.widget-slider .noUi-target, +.jupyter-widget-slider .noUi-target { + background: var(--color-background-secondary) !important; + border-color: var(--color-background-border) !important; + box-shadow: none !important; +} +.widget-slider .noUi-handle, +.jupyter-widget-slider .noUi-handle { + background: var(--color-background-primary) !important; + border-color: var(--color-background-border) !important; + box-shadow: none !important; +} +.widget-slider .noUi-connect, +.jupyter-widget-slider .noUi-connect { + background: var(--color-brand-primary) !important; +} + +/* Misc. overrides */ table.dataframe { font-size: 0.8em !important; From 516484985b30eca55260f0a2720ce456c0670ed2 Mon Sep 17 00:00:00 2001 From: zmoon Date: Thu, 16 Apr 2026 13:22:47 -0500 Subject: [PATCH 07/10] Add responsive setting to the interactive widget --- pyabc2/abcjs/widget/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pyabc2/abcjs/widget/__init__.py b/pyabc2/abcjs/widget/__init__.py index 38a0e12..9e47a90 100644 --- a/pyabc2/abcjs/widget/__init__.py +++ b/pyabc2/abcjs/widget/__init__.py @@ -122,6 +122,11 @@ def interactive(abc: str = "", **kwargs) -> "ipywidgets.Widget": # pragma: no c description="Staff width (px)", **slider_kws, ) + responsive_cbox = ipw.Checkbox( + value=False, + description="Responsive", + indent=True, + ) line_thickness_slider = ipw.FloatSlider( min=-0.4, max=2, @@ -159,6 +164,7 @@ def interactive(abc: str = "", **kwargs) -> "ipywidgets.Widget": # pragma: no c ipw.link((w, "abc"), (input_box, "value")) ipw.link((w, "staff_width"), (width_slider, "value")) + ipw.link((w, "responsive"), (responsive_cbox, "value")) ipw.link((w, "scale"), (scale_slider, "value")) ipw.link((w, "line_thickness_increase"), (line_thickness_slider, "value")) ipw.link((w, "transpose"), (transpose_slider, "value")) @@ -203,6 +209,7 @@ def save(_): [ input_box, width_slider, + responsive_cbox, scale_slider, line_thickness_slider, transpose_slider, From c49121f2b1f23c403085ee2e64ebee54f31ab685 Mon Sep 17 00:00:00 2001 From: zmoon Date: Thu, 16 Apr 2026 13:31:19 -0500 Subject: [PATCH 08/10] Prevent responsive toggling from adding extra space below music --- pyabc2/abcjs/widget/index.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pyabc2/abcjs/widget/index.js b/pyabc2/abcjs/widget/index.js index 7d72eb5..a8e909b 100644 --- a/pyabc2/abcjs/widget/index.js +++ b/pyabc2/abcjs/widget/index.js @@ -95,6 +95,12 @@ function render({ model, el }) { music.innerHTML = ''; }; + // Clear any inline styles ABCJS may have been set on the music div + // (e.g. height/position from responsive mode's ResizeObserver), + // which would otherwise persist across re-renders and cause + // phantom empty space when toggling responsive on/off. + music.removeAttribute('style'); + head.innerHTML = ''; if (showDebugInput() && !hide()) { let code = document.createElement('code'); From 60ff0aaa30399c65f96cd7ede8af5dadb09d2599 Mon Sep 17 00:00:00 2001 From: Zachary Moon Date: Thu, 16 Apr 2026 13:55:02 -0500 Subject: [PATCH 09/10] Narrow music div selection Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pyabc2/abcjs/widget/index.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyabc2/abcjs/widget/index.css b/pyabc2/abcjs/widget/index.css index c90755d..8e23b7d 100644 --- a/pyabc2/abcjs/widget/index.css +++ b/pyabc2/abcjs/widget/index.css @@ -8,13 +8,13 @@ div.abcjs-widget-container.debug { border: 1px solid forestgreen; } -div.music { +div.abcjs-widget-container div.music { /* ABCJS seems to set `overflow: hidden` on the music div, which prevents the container's auto overflow from working. */ overflow: visible !important; } -div.music.debug { +div.abcjs-widget-container div.music.debug { border: 1px dashed grey; } From 7517f5ca5b1ced0fc571a179b0c5887f25f7b57c Mon Sep 17 00:00:00 2001 From: zmoon Date: Thu, 16 Apr 2026 14:03:35 -0500 Subject: [PATCH 10/10] Add changelog --- docs/changes.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/changes.md b/docs/changes.md index 4b388de..dff3c09 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -1,5 +1,13 @@ # Release notes +## v0.1.3 (unreleased) + +* Improve styling of abcjs containers, pandas dataframes, and ipywidgets + in the docs ({pull}`103`). + You can now activate abcjs responsive mode in + {class}`~pyabc2.abcjs.widget.ABCJSWidget`, + but non-responsive is still the default. + ## v0.1.2 (2026-02-03) * Update Norbeck to the current 2026-01 version ({pull}`96`)