🏠 back to Observable

import a cell requiring CSS

I would like to import a cell, for example a chart, from another notebook without explicitly requiring its dependent stylesheet again.

Here is the example:

I have a chart defined in the notebook. The chart depends on 3rd party libs and styles.

and I import the chart in the other notebook

First of all, it is not intuitive to me. Why the 3rd party lib is implicitly imported with the chart but not the stylesheet?

it is not rendered correctly unless I explicitly import and evaluate the stylesheet from the source notebook.

If I write something like this in the source notebook. It will still not work. The html function does not put the <link> into current document as I expect.

Chartist = {
  let stylesheet = html`<link rel='stylesheet';
  href='https://unpkg.com/chartist@0.11.0/dist/chartist.min.css' />`;
  return await require('chartist');
}

This will work as I expect, but it is pretty tedious…

Chartist = {
  var link = document.createElement("link");
  link.type = "text/css";
  link.rel = "stylesheet";
  link.href = 'https://unpkg.com/chartist@0.11.0/dist/chartist.min.css';
  document.getElementsByTagName("head")[0].appendChild(link);
  return await require('chartist');
}

looking for advice. Thanks :stuck_out_tongue:

You are totally right that the relationship between CSS and imports (and embeds) is confusing! It trips up a ton of people. I can try to help explain it, but I think in the long run we need a better way of presenting this in the product itself — we can’t expect everyone to come to the forum. : )

The html function creates a DOM node; it doesn’t insert it into the document. When a cell returns a DOM node, the node is inserted into the document. When you have a one-line cell like:

stylesheet = html`<link rel='stylesheet'
  href='https://unpkg.com/chartist@0.11.0/dist/chartist.min.css' />`

The DOM node is the return value, so it’s inserted into the body and the CSS properly takes effect.

In your " :thinking: How to: use libraries that need CSS" notebook, you can add one line to the definition of chart, like in this suggestion:

container.appendChild(stylesheet);

If you didn’t want the separate stylesheet cell, you could also use the html function inline, like this:

container.appendChild(html`<link rel='stylesheet'
  href='https://unpkg.com/chartist@0.11.0/dist/chartist.min.css' />`);

It ain’t perfect, but hopefully a little easier than all the separate statements for creating the element and setting its attributes and appending it!

Why the 3rd party lib is implicitly imported with the chart but not the stylesheet?

You have a cell Chartist, which is like a global reactive variable, and then in the chart cell you reference it: new Chartist.Line(…. Observable tracks references to other cells, so it knows that it has to import Chartist in the background when you import chart to another notebook.

But Observable can’t parse or track what CSS applies to what. It’s rife with side effects, so it can get messy fast. I think it’s conceivable that maybe one day we could detect when an imported DOM element is affected by CSS somewhere else in the original notebook, but it might be hard or impossible to do that correctly and reliably. So, for now, we can’t automatically import the CSS you need. Which is why I typically have CSS in a named cell and then explicitly reference that named cell in any cell that needs it — like we did above.

2 Likes

You can also use shadow DOM if you want to apply CSS to just one cell and not the whole notebook. See:

4 Likes