CSS for custom widget

I am in the process of building a custom widget for audio annotation.

Now that I have something working, I want to make it pretty :slight_smile:
How does one package a CSS style sheet into a custom widget?


PS: I used GitHub - jupyter-widgets/widget-cookiecutter: A cookiecutter template for creating a custom Jupyter widget project. as my starting point.

As custom style seems a requirement for 80% of custom widgets, that seems like an issue to raise on that repo.

You may also want to take a look at widget-ts-cookiecutter.

Looking at that, you can see some of the patterns it provides out of the box for getting style into the page:

So you could theoretically run that cookiecutter in another folder, giving it your package name, etc. as input, and comparing the output, copying some of those lines over, and re-running your build until it works.

Or, copy your new widget into that newly generated one, and update the parts of the code that you added to be .ts. Yes, the learning curve of TypeScript is a little higher. Yes, many parts of the backbonejs integration are still quite untyped without some work. But getting a build working with more strict tools will almost certainly find issues before they break in your user’s browser, and make adapting to inevitable upstream API changes simpler over time.

I am new to this Javascript/TypeScript world…
Are you saying that TypeScript will eventually make my life easier?
If so, I will make the switch while the codebase it still relatively small…

1 Like


Working in TypeScript, your editor will give you a lot of feedback, per keystroke, about what you’re doing, and using the typescript compiler adds a very negligible amount of overhead for small codebases, while finding very common issues (the dreaded “null pointer exception” being one of them). It costs about 25mb of extra packages to be installed at development time, but crucially has almost zero overhead for the end user.

But the big motivator: the actual knowledge of how modern jupyter frontends work is encoded directly in the type system, and is guaranteed to reflect the current intent, while the docs, examples, tutorials, etc. you find may well be outdated.

When it comes time to upgrade to a new version in a few months, the types will be updated as well, and your code will fail to compile at build time when it’s cheap to fix, instead of fail to run on a user’s computer when the cost gets a lot higher.


Thanks for the advice. It did make the whole process more robust.

And now I have CSS!

Looking great!

While you’re still working, the cookiecutter should include a .binder directory, which means you already can share a free, live demo which will be hosted in the cloud. You might want to create, e.g. examples/my-widget.ipynb and then your demo URL (with a shiny badge) would open right to a Lab or Notebook of your choosing.

Slightly more expensive: with that generated .whl, you can host a live demo with JupyterLite (disclaimer: maintainer) which can be a very nice way to let prospective users play with the content, instead of just seeing screenshots. Some heavy python dependencies don’t work out of the box, though!

The two most recommendable paths are:

  • use github actions with actions/upload-pages-artifact
    • this would give you https://hbredin.github.io/some-widget on merges to main
  • use readthedocs (needs free account)
    • this would give you https://some-widget.rtfd.io
      • and can optionally give you per-PR-push previews, which are very nice
    • also… docs! docs are great, and can do a lot to help others both find and customize your widget

The last piece is distributing your releases to PyPI and npmjs.org:

  • .whl files uploaded to PyPI are for end users, so they only need to pip install some-widgetor add some-widget to their requirements.txt (better)
  • .tgz files uploaded to npmjs are for folk that might want to remix your widget, and provide additional features, or otherwise integrate with it
1 Like

Thanks @bollwyvl for your help along the way.

I just released a first version: pip install pyannotebook

Announcement tweet and Github repository.

1 Like

Looking great! Some quick demo hits:

  • a binder badge is quick and easy to build
    • it’s also good for shaking down on someone else’s computer which is clutch for new stuff
  • once you have RTD up, getting jupyterlite into the build is really pretty easy

Good luck!