Christian Heilmann

Quick Tip: How to capture and replace newlines in liquid for Jekyll/GitHub Pages

Thursday, December 17th, 2020 at 1:40 pm

As part of the re-write of The Developer Advocacy Handbook I needed to have blockquotes with different classes.

Fact display in the book

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 :)

Share on Mastodon (needs instance)

Share on Twitter

Newsletter

Check out the Dev Digest Newsletter I write every week for WeAreDevelopers. Latest issues:

Dev Digest 146: 🥱 React fatigue 📊 Query anything with SQL 🧠 AI News

Why it may not be needed to learn React, why Deepfake masks will be a big problem and your spirit animal in body fat! 

Dev Digest 147: Free Copilot! Panel: AI and devs! RTO is bad! Pi plays!

Free Copilot! Experts discuss what AI means for devs. Don't trust containers. Mandated RTO means brain drain. And Pi plays Pokemon!

Dev Digest 148: Behind the scenes of Dev Digest & end of the year reports.

In 50 editions of Dev Digest we gave you 2081 resources. Join us in looking back and learn about all the trends this year.

Dev Digest 149: Wordpress break, VW tracking leak, ChatGPT vs Google.

Slowly starting 2025 we look at ChatGPT vs Google, Copilot vs. Cursor and the state of AI crawlers to replace web search…

Dev Digest 150: Shifting manually to AI.

Manual coding is becoming less of a skill. How can we ensure the quality of generated code? Also, unpacking an APK can get you an AI model.

My other work: