Skip to content

Enable theming / custom styling#1

Draft
batpad wants to merge 2 commits into
mainfrom
custom-styling
Draft

Enable theming / custom styling#1
batpad wants to merge 2 commits into
mainfrom
custom-styling

Conversation

@batpad

@batpad batpad commented Jun 7, 2026

Copy link
Copy Markdown
Member

Just leaving as a draft to think about how one might enable custom styling of widgets and having "themes".

Goal: Users should have some way to customize styling of widgets: at least things like colours, fonts and some sizing / spacing.

Here, we specify a bunch of CSS variables for things like colours, fonts, etc. that are then used consistently across widgets. We then allow these to be over-ridden either on a per-widget, per-property basis or by creating a theme object to apply a bunch of over-rides consistently.

So, you can create a theme like:

dark = Theme(
    color_accent="#2f81f7",
    color_accent_hover="#4493f8",
    color_text="#e6edf3",
    color_text_muted="#8b949e",
    color_border="#30363d",
    color_border_strong="#444c56",
    color_surface="#0d1117",
    color_on_accent="#ffffff",
    color_code_bg="#161b22",
    color_positive="#3fb950",
    color_negative="#f85149",
    chart_palette=[
        "#58a6ff", "#ff7b72", "#3fb950", "#d29922", "#bc8cff",
        "#39c5cf", "#ffa657", "#f778ba", "#a5d6ff", "#7ee787",
    ],
)

And then, pass this theme object to widgets to set the theme accordingly. This requires us to define a set of CSS variables, and then use them consistently across widgets.

While I like the usage ergonomics and this seems all nice, there's a few problems that I want to think through before blindly merging:

  • This imposes potentially unknown constraints on widget authors to create their widgets so that they are "themable" / makes it harder to test across all possible thematic combinations, etc. You can already see with the Chart.js widgets where we are using an external library, this starts to get awkward, and adds a fair bit of complexity to the widget.
  • We theme the widgets, but don't have control of the theme of the container application - i.e. JupyterLab environment or MyST theme - this can cause dissonance - for eg. in a dark mode for widgets, won't look alright if rendered on a page that is light mode. This will probably require a bit of reading of theming in JupyterLab and MyST to figure how to architect in a way that works well with theming possibilities in the container application.

I was really keen on figuring out some solution / pathway to a solution so users have some flexibility with styling of widgets - I find it really frustrating when I have to use a widget that I can't customize styling of at all to fit my needs. It's possible that the better solution for now is a bit of a lower level interface, allowing to over-ride a Style object or so, instead of trying to abstract this with themes and force all widgets to use common CSS variables.

Definitely need to think about this a bit, but thought it useful to put up a sketch - this PR does work and allow theming, but does also introduce a lot of complexity.

@dzole0311 if you are getting into this, I'd really appreciate your thoughts if you're able to give this a quick skim to get an idea of the implementation and we can discuss approaches.

@batpad

batpad commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

@dzole0311 tagged you in the PR, but while editing the comment, so not sure you'd have got a notification, tagging you again just in case :) (and to emphasize how desperate I am to get another set of 👀 and someone to talk to about this haha )

@dzole0311

Copy link
Copy Markdown
Member

@batpad I think this is already moving in a good direction. One possible approach we could let guide this work is design-token based theming for the widgets and static exports.

I think Radix is a good example for this, it exposes a small-ish theme config and shared tokens but also discourages really heavy overriding of styles per-component. If someone needs lots of overrides ideally they would use tokens, props/variants or they just build a more custom component from lower-level component primitives.

For manywidgets each widget will have its own CSS generally for layout and structure and any visual details like colors, font, spacing, radius, borders, etc. would come from shared --mw-* tokens (this PR already moves in that direction). Component-specific tokens can still exist but I see them more as escape hatches when needed.

Also, have you considered making the default --mw-* values fall back to JupyterLab’s --jp-* variables where available? I read through JL's docs a bit and found: https://jupyterlab.readthedocs.io/en/stable/developer/css.html#naming-of-css-variables. I probably wouldn’t use the --jp-* prefix for our own variables since thats a JupyterLab namespace but using them as fallbacks might make the widgets feel "native" in JupyterLab.

@batpad

batpad commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

Wow, thanks much for the thoughtful notes @dzole0311

Yes, I think the key is to look at the JupyterLab theming (did not know about the --jp-* stuff, that's very useful), as well as the MyST theming, and figure out how best this sits in between and potentially plays well with both of them - I really like something like a default ability to just inherit the theme of its container application (so JupyterLab when rendered in a notebook and the MyST theme when rendered statically). I'll try and find some time to look at both those theming conventions.

I don't think there's any rush to merge this - I think I mostly just wanted to get a good sense of what possibilities are here and how one would architect this in a hypothetical future. If you ever want to take this for a ride and implement some ideas, please go ahead. I think I'll deprioritize this until we have a real use-case - though I think it is really cool to allow custom theming, so would definitely like to think this through a bit properly and do at some point.

This is definitely one of those things I want to think through and not rush though, and all your comments make a lot of sense - thanks much 🙏

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