shiroa

Built-in Themes

Shiroa publishes two site themes as separate Typst packages:

  • @preview/shiroa-starlight:0.4.0@preview/shiroa-starlight:0.4.0 provides a native HTML documentation layout inspired by Astro Starlight.
  • @preview/shiroa-mdbook:0.4.0@preview/shiroa-mdbook:0.4.0 provides an mdBook-style layout and the HTML wrapper used by Shiroa’s default dyn-paged output.

Both packages expose one main show rule. The page template passes the complete book.typbook.typ as the first argument and the current chapter body as the second. The theme then reads book metadata, builds the page chrome, and places the chapter in the main content area.

See Rendering Targets and Inputs and Reading Metadata for the helpers used below, or Custom Theme to build a theme without either package.

Choosing a theme

The initialized templates/page.typtemplates/page.typ makes the choice per compilation target:

#import "@preview/shiroa:0.4.0": is-html-target
#let web-theme = if is-html-target(exclude-wrapper: true) {
"starlight"
} else {
"mdbook"
}
#assert(web-theme == "starlight" or web-theme == "mdbook")
#import "@preview/shiroa:0.4.0": is-html-target
#let web-theme = if is-html-target(exclude-wrapper: true) {
"starlight"
} else {
"mdbook"
}
#assert(web-theme == "starlight" or web-theme == "mdbook")

This selects Starlight for native HTML and mdBook for the HTML wrapper and paged phases. It gives each CLI mode a consistent implementation:

  • --mode=static-html--mode=static-html: Use Starlight for the native HTML page.
  • --mode=dyn-paged--mode=dyn-paged: Use mdBook for html-wrapperhtml-wrapper; its paged web-*web-* phases return the Typst chapter body that the wrapper loads.
  • --mode=static-html-dyn-paged--mode=static-html-dyn-paged: Native HTML uses Starlight while Typst frames still use the shared paged styling from the page template.
  • PDF or editor preview: The theme package returns or preserves the Typst body; page, text, equation, and code styling remains the template’s responsibility.

You can force mdBook for native HTML if you prefer its layout. Do not force Starlight for a dyn-paged build: its web-*web-* phase is not supported.

Shared show-rule pattern

A chapter imports one project show rule:

#import "template.typ": project
#show: project.with(title: "Getting Started")
= Getting Started
Chapter content.
#import "template.typ": project
#show: project.with(title: "Getting Started")
= Getting Started
Chapter content.

The project show rule passes the current page and the book description to a theme:

#import "@preview/shiroa-mdbook:0.4.0": mdbook
#let selected-theme = mdbook
#let project(
title: "",
description: auto,
body,
) = {
show: selected-theme.with(
include "book.typ",
title: title,
description: description,
plain-body: body,
)
body
}
#import "@preview/shiroa-mdbook:0.4.0": mdbook
#let selected-theme = mdbook
#let project(
title: "",
description: auto,
body,
) = {
show: selected-theme.with(
include "book.typ",
title: title,
description: description,
plain-body: body,
)
body
}

bodybody is supplied by Typst when projectproject is used as a show rule. Passing it as plain-bodyplain-body lets the theme derive a semantic description before other show rules replace the visible content.

Starlight

Import and signature

// Compile this example with Typst's HTML target.
#import "@preview/shiroa-starlight:0.4.0": starlight
// Compile this example with Typst's HTML target.
#import "@preview/shiroa-starlight:0.4.0": starlight

The exported function has the signature starlight(book, body, ...)starlight(book, body, ...); its named parameters are described below.

Starlight is intended for native Typst HTML output. Calling it for a paged web-*web-* or PDF target raises an error. Use the target selector above so editor preview and dyn-paged builds use mdBook.

Positional arguments

  • bookbook (content): The included book.typbook.typ. Starlight emits it so <shiroa-book-meta><shiroa-book-meta> is available to the sidebar and header.
  • bodybody (content): The current chapter after the theme’s show rules are installed.

Page metadata

  • titletitle (strstr or content, default """"): Visible page heading and the page part of the HTML <title><title>.
  • descriptiondescription (strstr, autoauto, or nonenone, default nonenone): A string is used verbatim; autoauto derives text from plain-bodyplain-body; nonenone omits the description meta element.
  • plain-bodyplain-body (content or autoauto, default autoauto): Source used when descriptiondescription is autoauto. autoauto uses bodybody.
  • meta-titlemeta-title (function): Receives (title, site-title)(title, site-title) and returns the browser title. The default produces Page -- SitePage -- Site, or whichever non-empty title is available.

The site title comes from book-meta.titlebook-meta.title. A missing site title is supported; the page title is then used on its own.

Search and header

  • enable-searchenable-search (boolbool, default truetrue): Includes or removes the search box and search-results panel.
  • social-linkssocial-links (function): Receives named githubgithub and discorddiscord URLs. By default they come from book-meta.repositorybook-meta.repository and book-meta.discordbook-meta.discord.
  • social-iconssocial-icons (function): Renders the link records produced by social-linkssocial-links. Override it to change icons or link markup.
  • right-groupright-group (content or nonenone): Replaces the entire right side of the header. nonenone keeps social links, theme selection, and the mobile sidebar control. A custom value is responsible for any controls it wants to retain.

Extra assets

extra-assetsextra-assets is an array of rawraw CSS or JavaScript blocks. Starlight embeds them in the HTML head after its own assets.

// Compile this example with Typst's HTML target.
#import "@preview/shiroa-starlight:0.4.0": starlight
#let extra-css = ```css
.site-title {
font-style: italic;
}
```
#let project(title: "", body) = {
show: starlight.with(
include "book.typ",
title: title,
plain-body: body,
extra-assets: (extra-css,),
)
body
}
// Compile this example with Typst's HTML target.
#import "@preview/shiroa-starlight:0.4.0": starlight
#let extra-css = ```css
.site-title {
font-style: italic;
}
```
#let project(title: "", body) = {
show: starlight.with(
include "book.typ",
title: title,
plain-body: body,
extra-assets: (extra-css,),
)
body
}

Use raw(lang: "js", ...)raw(lang: "js", ...) for JavaScript. Assets are inserted without sanitizing their source, so only include code controlled by the book project.

Complete Starlight project rule

// Compile this example with Typst's HTML target.
#import "@preview/shiroa:0.4.0": is-html-target
#import "@preview/shiroa-starlight:0.4.0": starlight
#let project(
title: "",
description: auto,
body,
) = {
if not is-html-target(exclude-wrapper: true) {
return body
}
show: starlight.with(
include "book.typ",
title: title,
description: description,
plain-body: body,
enable-search: true,
)
body
}
// Compile this example with Typst's HTML target.
#import "@preview/shiroa:0.4.0": is-html-target
#import "@preview/shiroa-starlight:0.4.0": starlight
#let project(
title: "",
description: auto,
body,
) = {
if not is-html-target(exclude-wrapper: true) {
return body
}
show: starlight.with(
include "book.typ",
title: title,
description: description,
plain-body: body,
enable-search: true,
)
body
}

In a multi-target project, prefer the shared selector shown later instead of returning early, so the same template also applies paged text styling.

mdBook

Import and signature

#import "@preview/shiroa-mdbook:0.4.0": mdbook
#import "@preview/shiroa-mdbook:0.4.0": mdbook

The exported function has the signature mdbook(book, body, ...)mdbook(book, body, ...); its named parameters are described below.

mdBook builds HTML chrome for htmlhtml and html-wrapperhtml-wrapper. For paged web-*web-* and PDF targets it returns bodybody, allowing the rest of the page template to render the chapter normally.

Positional arguments

  • bookbook (content): Included book.typbook.typ; supplies sidebar, repository, and edit metadata.
  • bodybody (content): Current chapter. In html-wrapperhtml-wrapper, mdBook replaces it with paged-load-trampoline()paged-load-trampoline() so the browser loads the separately compiled paged artifact.

Page metadata

  • titletitle (strstr or content, default [Shiroa Site][Shiroa Site]): Page title used by the menu and HTML title.
  • descriptiondescription (strstr, autoauto, or nonenone): Same semantics as Starlight.
  • plain-bodyplain-body (content or autoauto): Source for an automatic description.
  • meta-titlemeta-title (function): Combines the page and site titles. Its default has the same behavior as Starlight’s default.

Repository links

The header reads book-meta.repositorybook-meta.repository and book-meta.repository-editbook-meta.repository-edit:

#import "@preview/shiroa:0.4.0": book-meta
#book-meta(
repository: "https://github.com/example/my-book",
repository-edit: "https://github.com/example/my-book/edit/main/docs/{path}",
summary: [],
)
#import "@preview/shiroa:0.4.0": book-meta
#book-meta(
repository: "https://github.com/example/my-book",
repository-edit: "https://github.com/example/my-book/edit/main/docs/{path}",
summary: [],
)

{path}{path} is replaced with x-currentx-current after a leading slash is removed. Set an explicit repository-editrepository-edit template when the source is not stored at the theme’s conventional GitHub path.

Extra assets

extra-assetsextra-assets has the same CSS and JavaScript raw-block format as Starlight:

#import "@preview/shiroa-mdbook:0.4.0": mdbook
#let project(title: "", body) = {
show: mdbook.with(
include "book.typ",
title: title,
plain-body: body,
extra-assets: (
```css
:root {
--content-max-width: 72rem;
}
```,
),
)
body
}
#import "@preview/shiroa-mdbook:0.4.0": mdbook
#let project(title: "", body) = {
show: mdbook.with(
include "book.typ",
title: title,
plain-body: body,
extra-assets: (
```css
:root {
--content-max-width: 72rem;
}
```,
),
)
body
}

Reserved parameters in 0.4.0

The mdbookmdbook signature currently contains three parameters that are not consumed by its entry-point implementation:

  • enable-searchenable-search does not toggle the search markup.
  • social-linkssocial-links is not called.
  • right-groupright-group does not replace the header controls.

They are reserved for theme API alignment. Do not depend on them changing the 0.4.0 output. extra-assetsextra-assets, meta-titlemeta-title, repository metadata, and description handling are implemented.

Complete mdBook project rule

#import "@preview/shiroa-mdbook:0.4.0": mdbook
#let project(
title: "",
description: auto,
body,
) = {
show: mdbook.with(
include "book.typ",
title: title,
description: description,
plain-body: body,
)
body
}
#import "@preview/shiroa-mdbook:0.4.0": mdbook
#let project(
title: "",
description: auto,
body,
) = {
show: mdbook.with(
include "book.typ",
title: title,
description: description,
plain-body: body,
)
body
}

One template for both themes

The following is the minimal selection pattern used by an initialized book. Page dimensions, fonts, markup rules, equations, and code styling can be added around it without changing the theme calls.

#import "@preview/shiroa:0.4.0": (
get-page-width,
is-html-target,
is-pdf-target,
is-web-target,
)
#let project(
title: "",
description: auto,
body,
) = {
set page(
width: get-page-width(),
height: auto,
) if is-pdf-target() or is-web-target()
show: if is-html-target(exclude-wrapper: true) {
import "@preview/shiroa-starlight:0.4.0": starlight
starlight.with(
include "book.typ",
title: title,
description: description,
plain-body: body,
)
} else {
import "@preview/shiroa-mdbook:0.4.0": mdbook
mdbook.with(
include "book.typ",
title: title,
description: description,
plain-body: body,
)
}
body
}
#import "@preview/shiroa:0.4.0": (
get-page-width,
is-html-target,
is-pdf-target,
is-web-target,
)
#let project(
title: "",
description: auto,
body,
) = {
set page(
width: get-page-width(),
height: auto,
) if is-pdf-target() or is-web-target()
show: if is-html-target(exclude-wrapper: true) {
import "@preview/shiroa-starlight:0.4.0": starlight
starlight.with(
include "book.typ",
title: title,
description: description,
plain-body: body,
)
} else {
import "@preview/shiroa-mdbook:0.4.0": mdbook
mdbook.with(
include "book.typ",
title: title,
description: description,
plain-body: body,
)
}
body
}

Keep plain-body: bodyplain-body: body even when another show rule will style or replace the visible chapter. It gives both themes a stable source for automatic metadata.