expressive-code / expressive-code

A text marking & annotation engine for presenting source code on the web.

Home Page:https://expressive-code.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Remark Plugin] CSS and JS only injected on first tab

atilafassina opened this issue · comments

Hello,
I believe I found a second case of #108
I'm currently at v0.32.4 of remark-expressive-code.

Now, the plot thickens because I'm not on a Astro website, it's a SolidStart app. (docs.solidjs.com) and if you change tabs, you'll see it breaks the styling of every code-snippet in that view.

I believe it has to do with the fact the <style> and <script> tags are only presented on the first selected tab.

First of all, hello and welcome! :)

I think this is a separate issue. The issue you linked describes a case where the CSS files are not making it into the build output directory. Your issue is likely caused by something else:

To reduce the size of the rendered HTML, Expressive Code only emits its styles when rendering the first code block of each page. Unfortunately, as the first code block on your page is nested inside your tab component, and this tab component seems to remove the contents of any non-active tabs from the DOM, the styles get removed, too.

It should be possible to work around this issue by changing your tab component's behavior so that it just sets display: none on the non-active tabs instead of completely removing them from the DOM. Could you give this approach a try to see if that's the issue?

Sorry, bad copy/paste, I meant this one: #135

It should be possible to work around this issue by changing your tab component's behavior so that it just sets display: none on the non-active tabs instead of completely removing them from the DOM. Could you give this approach a try to see if that's the issue?

I'll give this one a go and report back! Thanks for the quick feedback!

Ah, no worries! Just for completeness, #135 was actually a race condition bug caused by parallel rendering of multiple blocks, and that has been fixed in the meanwhile. Your issue is still a very proud separate one. :)

Good! Makes me feel less bad for creating one.

Though, your suggestion works.
Instead of replacing the DOM elements, I can render all tabs and just display: none the ones that aren't selected.

Thanks a bunch!!

I'm happy to hear that!

I'm currently wondering if I could offer an alternative solution for sites to integrate EC's global styles and JS modules that doesn't have the drawback that they are tied to the first code block on a page, so that the workaround wouldn't even be necessary.

I currently see two possible approaches:

  1. I could offer a config option that injects the styles in a different location than inside the first code block (e.g. at the beginning or end of the page). This should be pretty straightforward.

  2. Alternatively, I could try to find a way to offer the styles as separate CSS and JS assets in the remark integration as well, similar to what the Astro integration does: It extracts the config-dependent generated styles from the renderer and creates hashed CSS and JS assets from them, which can be served separately and cached across pages. Then, instead of inlining the assets into the first code block, it inserts stylesheet & JS module links to these assets. As these links are cheap and browsers will not load the same assets multiple times, I could also add them on each code block, which would get rid of this issue.

    The reason why the remark integration doesn't do this yet is that remark plugins are usually not aware of the build context they are running in and have no concept of things like a build output directory. I would need to add config options to specify where CSS & JS module assets should be emitted to and which base URL to use to link to them on the page.

I'd be happy to hear your thoughts on that!

Considering my own use-case, option 1 would probably suffice because as a docs website, I have code snippets pretty much everywhere, so I'd mostly inject that code in the my root and go my merry way.

Though, option 2 would probably address other use-cases much better. I'm a bit out of my depth here, I think this would probably need a different plugin for each framework though to provide such configuration.

If you decided to go option 2 and play around with SolidStart for that, I'm happy to help test things out and connect you with the people building the bundling layer

Thanks a lot for your input and also offering to help with testing. I appreciate it!

I'll close this issue for now as we found a short-term solution, but I'll keep this in mind and let you know when one of the other options becomes available for testing.