- 3 Minutes Wednesdays
- Posts
- Lua Filters for Quarto's HTML Output
Lua Filters for Quarto's HTML Output
Remember how I bamboozled Excel users with my cool PDF automation?
If you want to pull that off too, check out my freshly released "Report Automation With Typst" course.
Sers! š
Many greetings from Munich, Germany. Last week, we started talking about Lua filters and their interaction with Quarto. Today, we want to continue on that journey and make our filters actually do something meaningful.
After all, we ended last week with our filter just doing boring logs. Letās dive in and change that.
A recap
Letās recap where we left things last time. We had this Quarto file:

And it produced this HTML output:

Now, this looks suuuuper boring. Letās use Lua filters to bring in some colors. We can use our existing Lua code as a starting point. In case you donāt remember, this is how it looked:

Changing colors
Letās start by doing something simple: We can make our headline have a red color. The way to do that is to first add a bit these CSS style instructions at the bottom of our Quarto file:

This will add the style classes red, col-inner and bg-alice to our document. We want to use Lua to attach those classes to elements inside of our document. Once attached, the output will reflect the style changes.
As you probably recall, āHeaderā elements have attributes. After all, thatās what we saw in the Quarto logs we created.

So, to add a class to the header we just have to modify the class attribute and set it to the red class. Letās do that inside our Lua function:

And once weāve done this, our style instructions that are associated with the āredā class become visible. Tadaa:

Exciting, isnāt it? Well, probably not. In fact, this is boring as hell. But as always in life, this boring stuff just taught you an important technique.
Think about it: You can now let Lua assign classes to any element you want. For example, we could make the div elements that have the class column have different spacing and background color. Weāll just use the two classes col-inner and bg-alice weāve created earlier:

Nice! This worked. Sort of.

Due to the extra spacing of the col-inner class, the second column was moved to the next row. Thatās unfortunate. But weāll fix that in a second. For now letās ponder on some other thing.
Class or Attribute?
Have you notice that I used el.classes in my previous code snippet? First, I used that array and its :includes() method to check whether an element has the column class. And then I used the :insert() method to insert two classes into the existing classes of the element.
So you may rightfully wonder: āWhy are we doing things differently compared to what weāve done before with the header?ā
The reason for that is somewhat arbitrary. You see, normally I prefer working on el.classes instead of el.attributes["class"]. But with headers, thereās a catch. Check out what happens if I use el.classes in the Header() filter instead:

Yikes! Everything became red. Thatās not what we want.
The reason why this happens is that Pandoc has this option to
wrap headers and the following content into a
<section>tag andtransfer all the class attributes to that section.
Quarto has that enabled. And when that happens, our red class is applied to the full section instead of just the headline.
The way to avoid that is to insert an attribute called āclassā instead of modifying the designated classes field. Functionally, this gets the job done without the side effect. Hooray!
Making the columns nicer
Alright, letās revert our header function to the way it was before and come back to the columns. The reason why the row space does not suffice for both columns is because the col-inner class adds extra padding and margin. We want those to have some gutter between the columns.
Now, to fix our mess we can try to wrap our column content into another div container. And then on these div containers we can attach the style classes instead of targeting the column containers directly.
To see how to do that, we first need to get an idea of the structure of the content. Logging is our friend here.

Now we can see that the Div containers have these content attribute.

So all we have to do is to take that content and wrap it into another div container. The pandoc.Div() function helps us with that:

Et voilĆ , we have our desired layout.

Congrats! Weāve made it through another lesson on Lua filters. All of that was tailored to the html output so far. Next week, Iāll show you how to set up our Lua filter so that the columns also show up in our PDF output.
Hope you enjoyed this weekās newsletter. As always, feel free to hit the reply button to share your thoughts.
Happy Coding and see you next week,
Albert
Whenever youāre ready, there are three I can help you:
Automate Your Data Reports: This course helps data analysts eliminate manual copy-paste reporting by automating PDF reports end-to-end, saving hours every cycle and preventing costly mistakes. (Using the lovely Typst language š)
Generate Insights in Minutes, not Hours: This comprehensive course teaches you to handle data faster, smarter, and more efficiently.
Bespoke Data Science Solutions: Iāve helped clients build their own data science solutions. Whether building custom web apps, PDF reports, AI automations or teaching workshops, Iāve got you covered. You can reach out to me via this form (or simply hit reply to this email)
Reply