Loading static CSS in JupyterLab

Hi everyone !

I’m trying to write a small reporting functionality in python that can display in JupyterLab (and in the browser). Essentially, for our python tool, we want to dynamically generate a web page (using only CSS and HTML), and then allow users to display it in a notebook cell. Here’s what it currently looks like:


This works, which is exciting ! The problem comes in, though, if I try to ship the CSS myself – rather than fetching it from the web. In this case, it seems to find the style sheets (i.e., the paths are correct when I inspect them in the web console), but it won’t recognize any of the rules they contain. What’s puzzling to me is why this fails, but why fetching the style sheets from the internet works just fine.

I notice there are similar keywords between this question and Static content for jupyterlab extension, but I couldn’t see the immediate connection.

Any insight would be really appreciated ! :yellow_heart::bouquet:

1 Like


Would it be possible to embed the CSS file in a < style > tag, or using a data base64 encoding schema?

For the actual problem, is it in a < iframe > ? Btw I guess some browsers might not render CSS files that the server provides without a “Content-type: text/css” response header, so you may want to check this.

Thanks, @anibalsolon ! To answer some of your questions:

I don’t want to embed the CSS in a <style> tag, unfortunately, as there’s quite a lot of it.

The HTML isn’t in an iframe…

re browser-specific issues: I’m able to load it in the browser (so outside of JupyterLab) and it recognizes all the CSS rules either way; that is, either shipping the style sheets with the python code or pulling them in from the web.

You can see exactly how I’m defining the CSS styling here. I’m loading in a local copy, but the online versions would be called with:

<!-- CSS for the report -->
<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css" integrity="sha384-nn4HPE8lTHyVtfCBi5yW9d20FjT8BJwUXyWZT9InLYax14RDjBj46LmSztkmNP9w" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/grids-responsive-min.css">
<style type="text/css">

It’s only this second “online call” that seems to work !

Thanks again for thinking on this ! :sparkles:

And just to be a little more concrete about what I mean by “in browser,” here’s the HTML object in the browser, with 164 and 4 CSS rules found and interpreted for pure-min.css and grids-responsive-min.css, respectively:

The same HTML rendered in the notebook, with neither pure-min.css nor grids-responsive-min.css respected (i.e., each showing 0 rules):

I know the files exist because they’re found and recognized outside of Jupyter. Could it be as simple as Jupyter updating the path so it can’t find them, for some reason ? I’m passing only absolute paths, so I’m not sure how to be more specific, there…

Cool! Now I can see better the problem.

When I run your code from the PR, it tries to load:

that does not exist. I run this to get the error:

from nilearn import datasets, input_data
mni = datasets.load_mni152_template()
mask = input_data.NiftiMasker(verbose=True, mask_strategy='template')
html = mask.generate_report()

so the problem is that, when accessing via browser, it will try to load the absolute path as a path inside the jupyter server, that will not access directly your filesystem. (at least I am getting error 404, let me know if I need an extra step to make it actually find the correct path, besides pip install your branch)

Your open_in_browser() works because it creates a temp file and accesses it outside HTTP realm (file://tmp/yada), so it can actually see your filesystem and open the CSS files.

What exactly is the CSS URL it loads when inside the notebook? It is strange that some rules appear for you.

1 Like

In the terminal it’s showing this warning:

[W 17:13:42.765 NotebookApp]  404 GET /home/emdupre/Desktop/open_source/nilearn/nilearn/reporting/data/css/pure-min.css ( 7.57ms referer=http://localhost:8888/notebooks/Untitled.ipynb

Which is the correct path, locally. And when inspecting the element it also shows the correct local path. But maybe I’m just looking in the wrong place ?

Any recommendations for where to look re what it’s seeing as the path, or how to set it correctly ?

Thanks so much again !

A little bit of digging and it looks like the problem might be because I’m trying to access files outside of the root for the notebook server (reading into this issue, in particular).

I can’t seem to find any good way around this – which is probably a good thing for a security-motivated design decision ! But I would (as always) very much appreciate feedback.

1 Like

Yes, the problem is somehow related to this. Jupyter server does not provide endpoints to access resources from python packages or from the file system. There are some solutions for Jupyter Extensions, but I find it really clunky, e.g.: https://github.com/ipython-contrib/jupyter_contrib_nbextensions/tree/master/src/jupyter_contrib_nbextensions/nbextensions/highlighter

To keep separate CSS files & circumvent the loading problem, my suggestion would be to load the CSS file on Python and add it to a < style > tag, unfortunately. It will not mix your CSS & Python code, and it will be transparent to the final user.

Also, be aware that open_in_browser() will only work if you run your Jupyter Notebook locally. When accessing a notebook in another machine (like a server), it will create the temporary files within this other machine, that will not be possible to access it from your machine.

1 Like

Thanks, @anibalsolon ! :tada: