Creating Custom Themes
Relevant source files
The following files were used as context for generating this wiki page:
The theme system in Writer is designed to provide a consistent visual experience across the live preview, exported HTML, and generated documents (PDF/RTF). It centers around the PreviewTheme structure, which encapsulates typography and color palettes, and a rendering pipeline that transforms Markdown into styled HTML via CSS injection.
Themes affect the application's presentation layer by defining how Markdown elements—such as headings, blockquotes, and code blocks—are rendered within the WebView and during export operations.
Sources: Writer/PreviewTheme.swift, CLAUDE.md:21-35
Theme Architecture and Data Structures
Themes are defined as immutable PreviewTheme instances that store configuration for fonts and colors. The ThemeStore manages the active selection, while the PreviewTheme itself is responsible for generating the CSS representation used by the rendering engine.
The PreviewTheme Structure
The core of the theme system is the PreviewTheme struct. It defines a "neutral palette" and specific typographic families.
| Property | Type | Description |
|---|---|---|
name | String | Display name for the theme picker |
bodyFontFamily | String | CSS font-family string for body text |
headingFontFamily | String | CSS font-family string for headers (h1-h6) |
codeFontFamily | String | CSS font-family string for inline code and code blocks |
baseFontSize | Int | Default font size in pixels |
lineHeight | Double | Text line spacing multiplier |
backgroundColor | String | Hex color for the document background |
textColor | String | Hex color for primary text |
linkColor | String | Hex color for hyperlinks |
codeBackgroundColor | String | Hex color for code block backgrounds |
borderColor | String | Hex color for borders and blockquote accents |
secondaryTextColor | String | Hex color for muted text (h6, blockquotes) |
Sources: Writer/PreviewTheme.swift:5-20
CSS Generation Logic
The PreviewTheme contains a computed property cssStyles that maps internal properties to a standardized CSS template. This template applies global resets (e.g., box-sizing: border-box) and specific styles for Markdown elements. For example, headings use the headingFontFamily and specific em-based scaling:
- h1: 2em, 600 weight, 0.3em padding-bottom.
- h6: 0.85em, uses
secondaryTextColor. - blockquote: Uses
borderColorfor a 4px left border.
Sources: Writer/PreviewTheme.swift:65-171
Rendering Pipeline
The rendering process follows a specific flow where Markdown text is converted to HTML and then wrapped in a document structure that includes the theme's CSS.
The MarkdownRenderer uses a custom HTMLVisitor (implementing MarkupVisitor) to traverse the Markdown AST and generate valid HTML tags. The wrapInHTMLDocument method then injects the theme's CSS into the <head> of a new HTML document.
Sources: Writer/MarkdownRenderer.swift:7-133, Writer/MarkdownRenderer.swift:163-178
Theme Integration in UI and Exports
Themes are applied dynamically within the application and are persisted during file exports.
Preview and Selection
The WebView provides a UI overlay for switching themes. It uses the ThemeStore to update the active previewTheme, which triggers a reload in the WKWebView.
Exporting Styled Documents
When exporting to HTML, PDF, or RTF, the system retrieves the current theme from the ThemeStore and uses it to style the output.
- HTML Export: The
HTMLExportergenerates a standalone file where the CSS is inlined directly in the<style>block. - PDF Export: The
PDFExporterrenders the themed HTML in a hiddenWKWebViewbefore capturing the PDF data. - RTF Export: The
RTFExporterconverts the themed HTML into anNSAttributedStringusingNSAttributedString.DocumentType.html, which attempts to preserve visual styles in the Rich Text output.
Sources: Writer/HTMLExporter.swift:13-16, Writer/PDFExporter.swift:25-28, Writer/RTFExporter.swift:20-23
Predefined Theme Constants
The project includes two default theme presets defined as static constants in PreviewTheme.
// Example: The Serif Theme definition
static let serif = PreviewTheme(
name: "Serif",
bodyFontFamily: "Georgia, 'Times New Roman', Times, serif",
headingFontFamily: "Georgia, 'Times New Roman', Times, serif",
codeFontFamily: "'SF Mono', Menlo, Monaco, 'Courier New', monospace",
baseFontSize: 16,
lineHeight: 1.6,
backgroundColor: "#ffffff",
textColor: "#24292e",
linkColor: "#0366d6",
codeBackgroundColor: "#f6f8fa",
borderColor: "#e1e4e8",
secondaryTextColor: "#6a737d"
)
Sources: Writer/PreviewTheme.swift:45-58
Summary
Custom themes in Writer are unified through a central PreviewTheme model that generates standardized CSS. This architecture ensures that the "distraction-free" environment remains visually consistent whether the user is typing in the editor, reviewing in the live preview, or distributing documents via PDF or HTML exports. Implementation of new themes requires adding a new PreviewTheme instance to the allThemes array.