Custom themes
Using one of the built-in themes is a great way to get started with Bookly, but you may want to create your own theme to match your branding or personal style. This section explains how Bookly’s theming system works, how to create a custom theme and how to apply it to your book.
Theming system
In Bookly, a theme is a dictionary of standardized functions that define the visual appearance of the key elements of a book. This common interface allows themes to be used interchangeably and to be combined.
Common interface
Every theme exposes the same interface, which is a dictionary defined as:
#let custom = (
theme: custom-theme,
part: custom-part,
minitoc: custom-minitoc,
custom-box: custom-custom-box,
boxeq: custom-boxeq,
)The elements of the dictionary are:
theme(function): show rule defining the theme style (headings, outline entries, footnotes, headers and footers, …). It receives the colors of the book through itscolorsargument.part(function): function defining the style of the part titles, used by#part.minitoc(content): mini table of contents displayed by#minitoc.custom-box(function): function defining the style of the callout boxes (info-box,tip-box,warning-box,proof-box, …).boxeq(function): function defining the style of the equation boxes, used by#boxeq.
For instance, the fancy theme is defined as:
#let fancy = (
theme: fancy-theme,
part: fancy-part,
minitoc: fancy-minitoc,
custom-box: fancy-custom-box,
boxeq: fancy-boxeq,
)Thanks to this interface, high-level functions such as #part, #minitoc, #boxeq or #info-box work whatever the theme is. They simply call the corresponding function of the current theme, stored in states.theme:
#let part(title) = context (states.theme.get().part)(title)
#let boxeq(content) = context (states.theme.get().boxeq)(content)Partial theme definitions
A custom theme does not need to implement the complete interface. The theme you provide is merged with the classic theme, so any key that is not defined in your theme falls back to the classic implementation. This makes it straightforward to customize individual elements without redefining an entire theme.
Setting up a custom theme
To implement a custom theme, you have to define a function that includes the show and set rules defining the style of the document (headings, footnotes, references, …). Basically, a theme should be structured as follows:
// my-theme.typ
#import "@preview/bookly:5.3.0": *
#let my-theme(colors: default-colors, it) = {
// Heading level 1 style (chapters)
show heading.where(level: 1): it => context {
// Page break before each chapter
if not states.open-right-chapter.get() { pagebreak(weak: true) }
...
}
// Heading level 2 style
show heading.where(level: 2): it => { ... }
// Heading level 3 style
show heading.where(level: 3): it => { ... }
// Outline entry style
show outline.entry: it => { ... }
// Other show and set rules
...
it
}
// Custom part function
#let my-part(title) = { ... }
// Custom minitoc
#let my-minitoc = { ... }
// Custom box style
#let my-custom-box(title: none, icon: "info", color: rgb(29, 144, 208), body) = { ... }
// Custom equation box style
#let my-boxeq(body) = { ... }
// Theme dictionary
#let custom-theme = (
theme: my-theme,
part: my-part,
minitoc: my-minitoc,
custom-box: my-custom-box,
boxeq: my-boxeq,
)Then, you can initialize the template with your custom theme as follows:
#import "@preview/bookly:5.3.0": *
#import "my-theme.typ": custom-theme
#show: bookly.with(
theme: custom-theme,
)Theme composition
Because a theme is a plain dictionary, you can reuse and combine elements of the existing themes in the definition of your own theme. The functions of a theme are accessible using the pattern theme-name.key. For example:
#import "@preview/bookly:5.3.0": *
#let custom = (
theme: pretty.theme,
part: obook.part,
minitoc: modern.minitoc,
custom-box: orly.custom-box,
boxeq: pretty.boxeq,
)
#show: bookly.with(
theme: custom,
)Combined with the fallback to classic, this also lets you change a single element of a theme. For instance, to use the classic theme with the boxes of the fancy theme:
#show: bookly.with(
theme: (custom-box: fancy.custom-box),
)Template states
Bookly provides some states that are useful when designing a custom theme. They store information about the current state of the document and are collected in a dictionary named states. The following states are available:
Indicates whether the margins are alternated for odd and even pages when the tufte layout is enabled.
Author of the document.
Counter for parts.
Indicates whether the current entry is a part in the table of contents.
Indicates whether the current section is an appendix.
Indicates whether the current section is front matter.
Indicates whether headings are justified.
Dictionary of terms used in the document (e.g., “chapter”, “part”, …) in the selected language.
To use a language that is not supported by default, you can modify the states.localization dictionary after initializing the template. For instance, to add support for Dutch:
#states.localization.update(json("path_to_file/dutch.json"))For the English version, the JSON file is as follows:
{
"and": " and ",
"appendix": "Appendix",
"authored": "authored by",
"chapter": "Chapter",
"committee": "Defense committee",
"cosupervisor": "Co-supervisor:",
"cosupervisors": "Co-supervisors:",
"defended": "defended on",
"discipline": "Discipline:",
"doctoral-school": "DOCTORAL SCHOOL",
"habilitation": "French Habilitation to supervise research",
"lof": "List of figures",
"lot": "List of tables",
"minitoc": "Content",
"note": "Note",
"part": "Part",
"phd": "Doctoral thesis",
"proof": "Proof",
"specialty": "Specialty:",
"sponsor": "Sponsor:",
"sponsors": "Sponsors:",
"supervisor": "Supervisor:",
"supervisors": "Supervisors:",
"tip": "Tip",
"toc": "Table of contents",
"version-usage": "This version can be viewed and downloaded free of charge for personal use only. It must not be redistributed, sold, or used in derivative works.",
"warning": "Warning"
}
Numbering pattern for headings.
Numbering pattern for sections.
Numbering pattern for equations.
Numbering pattern for figures.
Numbering pattern for subfigures.
Indicates whether the document is laid out for two-sided printing (true unless the open-right option is false).
Indicates whether chapters start on a right-hand page.
Indicates whether parts start on a right-hand page.
Numbering pattern for pages.
Size of the paper.
Indicates whether paragraphs are indented.
Amount of the paragraph indentation.
Numbering pattern for parts.
Counter for side notes.
Title of the document.
Indicates whether the current layout is the Tufte layout.