Build your own theme

Again, there is no right or wrong when it comes to how a Polylux theme works. If you consider building a theme that you would like to contribute to the package (which you are cordially invited to do!), we kindly ask you to follow the convention presented before. In any case, it is probably a good idea to take that as an orientation.

To demonstrate how one would go about defining a custom theme, let us create one together! How about we make one for science slams? If you have ever been to one, you might have noticed that the presenters there love sparse dark-mode slides with huge font sizes.

Imports

Depending on whether this is a theme for yourself or supposed to be part of polylux, you do one of two things: For yourself, you simply import polylux as always:

#import "@preview/polylux:0.3.1": *

A theme that is shipped with polylux doesn't have to do that, and it shouldn't! Otherwise circular imports can occur. Instead, you depend on the two files logic.typ and utils/utils.typ. As your theme file science-slam.typ will be inside the themes directory, the imports will be:

#import "../logic.typ"
#import "../utils/utils.typ"

Additionally, you have to make polylux know about your theme which you do by adding

#import "science-slam.typ"

to themes/themes.typ.

The initialisation function

With that out of the way, we start with the function that sets the scene for everything else to come. By convention, we call it science-slam-theme and it can accept some keyword arguments along a single content argument. The keyword arguments are for configuration options, the content is for the rest of the document (read here if you are unfamiliar with this kind of function, this feature is rather unique to Typst).

#let science-slam-theme(aspect-ratio: "16-9", darkness: "dark", body) = {
  let background-color = if darkness == "dark" {
    navy
  } else if darkness == "very dark" {
    navy.darken(50%)
  } else if darkness == "ultra dark" {
    black
  } else {
    panic("illegal darkness, must be one of dark, very dark, ultra dark")
  }

  set page(
    paper: "presentation-" + aspect-ratio,
    fill: background-color,
  )
  set text(fill: white.darken(10%), size: 40pt, font: "Fira Sans")

  body
}

As you can see, we have two configuration options: One for the aspect ratio (as is convention) and one to determine the background colour — the more serious you are, the darker your background colour is, of course. After we have set the page parameters accordingly, we also define the text to be huge, bright and in a sans serif font.

Using it looks like this:

#show: science-slam-theme.with(darkness: "very dark")

Title slide

Next up, let us define a cool title slide. The only thing you have to keep in mind when defining your own slide functions is that you need to put the content you produce into the #polylux-slide function in the end. It might look as if it works without that as well but it actually breaks when you use #uncover or similar polylux features. If you build a theme as part of polylux and you have followed the import instructions from above, you will qualify the function as logic.polylux-slide.

Our title slide here is very simple, it just makes very sure to let the audience know what the topic is and who is speaking:

#let title-slide(title: [], author: []) = {
  polylux-slide({
    set align(center + horizon)
    smallcaps(strong(title))
    parbreak()
    text(size: .7em, author)
  })
}

You can use it like this:

#title-slide(title: [My awesome topic], author: [A really funny guy])

Note that the user does not actually provide any content themselves to this function. That is a common thing for title slides because their structure is so well-defined that a theme can produce all the content by just asking for a few pieces of information (like the title etc.).

Regular slides

The principle is the same as with the title slide. Define a function, create some content, pass it to polylux-slide. By convention, you should name the function for regular slides slide because it will be used most often. Here you will typically accept arbitrary content as a positional parameter that will make up the main content of the slide.

For example:

#let slide(title: [], body) = {
  polylux-slide({
    strong(title)
    set align(horizon)
    body
  })
}

And you can use it like this:

#slide(title: "A hilarious slide")[
  You didn't expect that!
]

In case you wondered, this is how the theme and the slides we just put together look like:

science-slam

Any number of further variants

Be creative! There are no limits but your own to the slide functions your theme can contain once you grasped the simple structure. For "serious" themes (other than this demo) you will probably want to think about adding headers, footers, slide numbers etc. Why not look into the source code of existing themes to get some inspiration?

The next page also lists some small tools that polylux provides to make common tasks simpler when creating a slide.