Fenced code tabs

Hello,

I was wondering if someone already did something like that in zola?
https://yassir.dev/markdown-fenced-code-tabs/

Since I didn’t find anything I thought I would implements it myself using shortcodes but I just don’t get how it works. My idea was to use a shortcode with a body and iterates over each blocs in the body to give each blocs and ID and be able to customize it later with CSS.
But sadly I can’t iterate over the body and I can’t find any example of the body being utilized :pensive:

Help would be greatly appreciated, I’ve been using zola for two days and still quite lost!

1 Like

That’s the kind of stuff that should probably be done in JS so probably out of scope by Zola. I’m not sure how I would implement that in Zola tbh, need to think about it for a while.

Well as you can see on the link I put before, there is no need for JS.
Also here is a small example working without JS: https://jsfiddle.net/2zag6nk9/

1 Like

@irevoire a shortcode with a body has a special body variable as explained here, though you cannot really iterate over it.

What you can do though in the shortcode: either load the test cases from “data files” with the load_data function, or split the body following thematic breaks like i’m doing here except you need to either match the raw markdown separators or build the HTML output directly in the template to split by <hr />.

So then your shortcode only needs to take an id parameter and an ordered list of languages so that it can build the buttons, assuming the codeblock IDs and language human representation (i.e. you can’t display bash if the lang declared in the codeblock is shell).

Let me know if you need help with this, i’d be interested to have this in my theme as well :slight_smile:

1 Like

Sorry I meant CSS/JS (depending on the template implementation).

Thank you, yeah some help would be appreciated :sweat:
I love your idea, sadly for now when I’m splitting on the


tag it does not split on — as you said but that’s not a big deal.

The real problem is that when I try to output the bloc-code they the markdown to html conversion stops:

Here is the code if you want to take a look: GitHub - irevoire/fenced_code_tabs: A shortcode for zola

Thanks

1 Like

So i’m not entirely sure (i don’t really use shortcodes at the moment) but it appears Markdown is not rendered in shortcodes and we need to do that with {{ body | markdown }} then outputting it with the safe filter so HTML tags are not escaped.

In exploring this, i believe i encountered two bugs:

  • comment syntax doesn’t work around shortcodes {#{% fenced_code_tab() %}#} produces an undesired result (i.e. something)
  • i have no clue why or how it happens, but apparently the safe filter places the closing </pre> tag from the first codeblock at the end of the entire shortcode output?!?!

So i tried both with Markdown shortcodes and HTML shortcodes. Markdown shortcode works perfectly. HTML shortcode, as explained above, misplaces a </pre> tag when used with the safe filter. Calling the content without safe does not seem to produce this? I may be missing something, any ideas @Keats? @irevoire you can find my little modification of your experiment here. Inspecting the source of the HTML shortcode example with Firefox will highlight in red where the problem is.

Note that despite the tag being misplaced, it still works perfectly in major browsers who will correct the wrong HTML when building the DOM. So you can move on to the next stage! If you only want one tabbed codeblock per page, then you can use numeric IDs as you have started. However, if you have multiple codeblocks on the same page, the IDs will collide and you will need to introduce an ID “namespace” for every tabbed codeblock (eg #example1-1, #example1-2, #example2-1…), passed as argument to the shortcode.

Also as i explained before if you want to display the language of each tab in the buttons, this is an information we cannot extract from the template itself, so it needs to be passed to the shortcode as an argument. A list is passed as argument like this [ "foo", "bar", "baz" ].

Let me know if you need more information. Happy hacking :slight_smile:

Thanks for everything now I have a first working version :smiling_face_with_three_hearts:

There is still some ugly hacky things though.
First, I don’t know how to add css in the header of each posts.
So what I’m doing is including the css at the first call of the shortcode here
And the second thing is right here
I didn’t found how to embed an if in the middle of the html tag.

If you know how to do any of this or want to update the code do not hesitate to create a PR.

Also is there a way to include a shortcode with it’s css easily? Like a package manager but for shortcodes?

1 Like

Nice that was quick!

For CSS there are a few ways:

  • you can package the CSS and the shortcode in your theme
  • you can declare a {% block head_extra %} in your theme, then override your theme’s block in your own index.html with {% extends "sam/templates/index.html"%}{% block head_extra %}<link rel="stylesheet" ...>{% endblock %}
  • you can, as you are doing, inline styles within the HTML body, with style/link tags: style tags in body has been deprecated along with the scoped attribute, and i don’t think a link tag in body was ever valid (i’m suprised it works at all!)
  • the tags can be inlined within HTML attributes, because the stylesheet isn’t that long, but i’m not a fan of this option because it makes it harder to hack on and debug

For if inside the HTML tag, the problem is markdown is processed after the shortcode, so if you have a dedicated line with an if that does not produce anything, you end up with an empty line which means the next lines are interpreted as a codeblock (an indented text block in CommonMark is also a codeblock).

Here’s my patch to get the if to work, assuming it should auto-check the first tab:

-       id="tab-group-{{ nth }}-{{ i }}_{{ tabs[i] }}"
-       checked="checked"
+       id="tab-group-{{ nth }}-{{ i }}_{{ tabs[i] }}"{% if i == 0 %}
+    checked="checked"{% endif %}

I must say i’m a great fan of javascript-free web experiments and i really like what you did here. It’s super pretty AND functional and will work just fine in your freshly-baked Tor browsers on safest mode :wink:

One little nitpick, i’m not a screen reader user so i wouldn’t know for sure but shouldn’t the input checkboxes be aria-hidden? From my understanding, although the codeblocks are not visible, they are still legible by screen readers, and marking additional buttons for screenreading will only make the whole experience confusing? (advertising a button that does not change anything when you’re not graphically browsing)

Ok thanks I applied your modification and did a little README in case anyone want to use this project.
:+1:

1 Like