Keeping Quarto Themes Separate

Guten Tag! šŸ‘‹ 

Many greetings from Munich, Germany. Let’s talk about my preferred way to manage brand styling in Quarto. Better yet, let’s do so in very specific terms.

As you know, in the upcoming workshop we’re going build a monthly fund fact sheet for a fictional firm called BIRD Investment. Rendered to HTML, it’s a clean report page:

The notable features of this can be seen but let’s actually denote what we got:

  • logo in the top left corner,

  • firm name next to it in a bold serif font,

  • a short purple rule under the header,

  • purple headings everywhere, and

  • tables with alternating purple-tinted rows.

So what about the PDF output? Well, the exact same .qmd file produces this:

Look at this! It’s pretty darn close. So close, I just can’t resist to insert this meme here:

So how does that work under the hood? Well, there are actually two separate places where the brand values live, one for each output format.

On the HTML side, a theme.scss

  • declares the brand hex values (as SCSS variables)

  • creates the font stack and

  • plain CSS rules apply those variables to headings, links, and the custom header and table classes.

On the PDF side, typst-template.typ handles all of that.

  • It declares the same hex values and fonts again (this time as Typst constants):

  • It applies a handful of small Typst functions like company-header() and colored-text()

Notice what I did not say: I did not say there’s one magic brand file that both sides read from automatically. There isn’t. At least not in this demo.

The two declarations are kept in sync by hand. I do this on purpose so you can see exactly what each renderer needs and why.

What you get in return for that small bit of duplication is real control. The HTML side gets normal, debuggable CSS. And the PDF side gets normal, debuggable Typst functions. Neither one is fighting some abstraction layer like brand.yml that is built on top.

In the workshop, we build this from scratch on both sides. That way, you get to understand where every color and font decision lives.

Next email, I want to show you the other half of BIRD Investment’s fact sheet: the part where a table gets text to flow around it. Spoiler: This will need a Lua filter.

Best,
Albert

PS: If you haven’t grabbed a seat yet, registration for September 23 or September 25 is still open here:

Whenever you’re ready, there are three I can help you:

  1. 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 šŸ˜)

  2. Generate Insights in Minutes, not Hours: This comprehensive course teaches you to handle data faster, smarter, and more efficiently.

  3. 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

or to participate.