Quick Tip: How to capture and replace newlines in liquid for Jekyll/GitHub Pages
Thursday, December 17th, 2020 at 1:40 pmAs part of the re-write of The Developer Advocacy Handbook I needed to have blockquotes with different classes.
With GitHub Pages markdown you can do that using the > notation:
> **Fact:** There are no bad students or a bad audience -- only bad workshops and talks. Your mood, dedication and enthusiasm do become those of the audience -- if you are not happy, they won\'t be happy. |
The problem was that I couldn’t add CSS classes to the blockquote elements so I can show them in different styles. I wanted four of them: example, fact, warning and tip.
The good news is that even without any styling using the strong name should make it obvious. But as there is no text “contains” selector in CSS I couldn’t rely on that to change the blockquote element.
First solution: JavaScript
The first thing I thought was to use JavaScript to apply the classes, which is pretty straight forward:
let bqs = document.querySelectorAll('blockquote'); bqs.forEach(b => { b.className = b. querySelector('strong'). innerText. toLowerCase(). replace(':',''); }); |
This, however, felt dirty and I wanted to use the system itself to do that task.
Moving to liquid
So I wrote a liquid include to convert the HTML before rendering. In my layout template, this works by replacing the {{ content }} with {% include blockquotes.html html=content %}.
In the blockquotes.html, I thought it’d be easy to do a search and replace. Alas, there is the issue that liquid only does string replacement and doesn’t know any regular expressions.
The HTML generated from the markdown has a line-break in it:
<blockquote> <p><strong>Fact:</strong> There are no bad students … </p> </blockquote> |
This is where it didn’t get fun. The replace filter doesn’t allow you to concatenate strings and doesn’t know the concept of n. So, I tried to use the newline_to_br together with strip_newlines filters and then replace the br but it was messy.
Turns out, the main trick was to capture a newline in liquid and assemble the string to replace using that one.
{% capture newline %} {% endcapture %} {% capture tip %}<blockquote>{{newline}} <p><strong>Tip:</strong>{% endcapture%} {% capture example %}<blockquote>{{newline}} <p><strong>Example:</strong>{% endcapture%} {% capture warning %}<blockquote>{{newline}} <p><strong>Warning:</strong>{% endcapture%} {% capture fact %}<blockquote>{{newline}} <p><strong>Fact:</strong>{% endcapture%} {% assign newhtml = include.html | replace: tip, '<blockquote class="tip"><p><strong>Tip:</strong>' | replace: example, '<blockquote class="example"><p><strong>Example:</strong>' | replace: warning, '<blockquote class="warning"><p><strong>Warning:</strong>' | replace: fact, '<blockquote class="fact"><p><strong>Fact:</strong>' %} {{ newhtml }} |
This works fine. Seems superfluous, but it is a way. It might be that there is a much simpler way as I am new to this world, having used PHP before the build the old version of the book. Got a better option? Tell me :)