Template specification
Templar Template Specification v1
Section titled “Templar Template Specification v1”Overview
Section titled “Overview”A Page Style is structured visual data plus optional advanced CSS. Library exports use a top-level templar-template mapping. Notes use a top-level templar mapping and additionally store their page mode. For the current implementation’s command/settings/source map, see DEVELOPER_REFERENCE.md.
Unknown fields are discarded during import. Missing v1 fields receive defaults. Current import supports only explicit version 1; an unsupported or missing note-style version fails closed instead of being reinterpreted as a styled note.
The highlight color pair and template folder were added compatibly within v1: older v1 styles that omit them receive safe defaults, while every new export writes the fields explicitly. Folder names are portable display labels rather than filesystem paths; separators and reserved/control characters are flattened during import.
The 1.1 feature batch extends the same compatibility rule: h5/h6, heading letter spacing and text transform, the lists/watermark sections, the new paper patterns and pattern controls, image float/object-fit/duotone, and the expanded block palette are all optional v1 additions. Old styles import unchanged; new exports write the new fields explicitly.
Versioning
Section titled “Versioning”The current template and note-style format is version 1. Unknown future versions are rejected and protected rather than default-filled. Supported older versions may be migrated by the registry in a future release; exports always use the current version.
Template versus note data
Section titled “Template versus note data”A reusable template does not contain page; the user chooses paged or pageless when creating/applying it. A note contains the full template copy plus:
page: mode: pageless size: a4 width: 794 height: 1123 gap: 32 scale-to-fit: trueThis separation is intentional: all templates must support both modes.
Applied notes may also contain implementation-owned provenance:
provenance: source-snapshot: # complete normalized template as it existed when applied/synchronized version: 1 style-name: Classic Ruled template-id: classic-ruled # ...all template-derived sections... applied-by-rule: id: research-notes name: Research Notessource-snapshot makes update status and three-way merging deterministic while the current style remains fully self-contained. applied-by-rule is informational. Neither field is included in a reusable .templar export, and note-only page and attachments are always excluded from source comparisons.
Complete export shape
Section titled “Complete export shape”templar-template: version: 1 style-name: Classic Ruled template-id: classic-ruled source-template-id: classic-ruled metadata: author: Templar description: Warm ruled paper with a measured baseline. folder: Essentials tags: [journal, ruled, warm] paper: color: "#fffdf4" pattern: ruled pattern-color: "rgba(107, 155, 190, 0.43)" major-pattern-color: "rgba(79, 125, 160, 0.55)" pattern-opacity: 1 pattern-scale: 1 dot-radius: 1 graph-major-interval: 5 margin-line: true margin-color: "rgba(210, 92, 92, 0.62)" margin-offset: 72 baseline: enabled: true mode: strict unit: 30 snap-images: true typography: body-font: 'Georgia, "Times New Roman", serif' body-size: 18 body-weight: 400 body-line-height: 0 first-line-indent: 0 drop-cap: false text-color: "#302e2b" muted-color: "#706c66" headings: h1: font: 'Georgia, "Times New Roman", serif' size: 42 weight: 700 color: "#302e2b" decoration: none letterSpacing: 0 textTransform: none h2: font: 'Georgia, "Times New Roman", serif' size: 31 weight: 700 color: "#393631" decoration: none letterSpacing: 0 textTransform: none h3: font: 'Georgia, "Times New Roman", serif' size: 24 weight: 700 color: "#46413b" decoration: none letterSpacing: 0 textTransform: none h4: font: 'Georgia, "Times New Roman", serif' size: 20 weight: 700 color: "#514b44" decoration: none letterSpacing: 0 textTransform: none h5: font: 'Georgia, "Times New Roman", serif' size: 17 weight: 700 color: "#5a534b" decoration: none letterSpacing: 0 textTransform: none h6: font: 'Georgia, "Times New Roman", serif' size: 15 weight: 700 color: "#635c53" decoration: none letterSpacing: 0 textTransform: none lists: marker-style: disc marker-color: "#706c66" indent-guides: false indent-guide-color: "rgba(48, 46, 43, 0.18)" nested-indent: 0 layout: max-width: 820 padding-top: 60 padding-right: 72 padding-bottom: 120 padding-left: 96 page-radius: 0 page-shadow: none images: frame: polaroid border-width: 10 border-color: "#ffffff" bottom-border-width: 34 corner-radius: 0 rotation: -1.2 shadow: "0 8px 20px rgba(69, 58, 42, 0.18)" max-width: 100 top-spacing: 30 bottom-spacing: 30 opacity: 1 sepia: 0 grayscale: 0 saturation: 1 contrast: 1 float: none object-fit: contain duotone: none blocks: link-color: "#315f86" quote-accent: "#9fb8ca" quote-background: "rgba(159, 184, 202, 0.12)" quote-text-color: "#302e2b" code-background: "rgba(48, 46, 43, 0.08)" code-text-color: "#302e2b" code-font: '"SFMono-Regular", Consolas, "Liberation Mono", monospace' code-size: 16 table-border: "rgba(48, 46, 43, 0.24)" table-border-width: 1 table-font-size: 15 table-text-color: "#302e2b" table-header-text-color: "#302e2b" table-padding: 8 table-striped: false table-stripe-color: "rgba(48, 46, 43, 0.045)" table-header-background: "rgba(48, 46, 43, 0.07)" checkbox-accent: "#507b5c" divider-color: "rgba(48, 46, 43, 0.35)" divider-width: 1 divider-style: solid callout-accent: "#9fb8ca" callout-background: "rgba(159, 184, 202, 0.12)" callout-text-color: "#302e2b" callout-title-color: "#302e2b" callout-icon-color: "#9fb8ca" callout-border-width: 3 callout-radius: 8 callout-variants: warning: accent: "#c98b2e" background: "rgba(201, 139, 46, 0.12)" textColor: "#302e2b" titleColor: "#7a4d12" iconColor: "#c98b2e" embed-background: "rgba(48, 46, 43, 0.06)" embed-accent: "#9fb8ca" embed-radius: 10 highlight-background: "rgba(244, 210, 83, 0.48)" highlight-text-color: "#302e2b" watermark: text: "" color: "rgba(48, 46, 43, 0.1)" size: 96 rotation: -30 opacity: 0.5 css: | .page h1 { letter-spacing: -0.025em; }Field constraints
Section titled “Field constraints”| Field | Constraint |
|---|---|
| version | exactly 1 |
| template-id | lowercase letters/digits and single hyphens |
| metadata.folder | single-level display label, sanitized and capped at 80 characters; missing values become Unfiled |
| paper.pattern | blank, ruled, ledger, dot-grid, graph, cross-hatch, diagonal, hex, scallop |
| paper.pattern-opacity | 0–1 |
| paper.pattern-scale | 0.25–4 |
| paper.dot-radius | 0.5–6px |
| paper.graph-major-interval | 2–10 cells |
| baseline.mode | strict, balanced, free |
| baseline.unit | 12–96px |
| body-size | 8–72px |
| body-line-height | 0 (automatic) or 16–120px |
| first-line-indent | 0–120px (reading view only) |
| drop-cap | boolean |
| weights | 100–900 |
| heading decoration | none, underline, rule, highlight |
| heading text-transform | none, uppercase, lowercase, capitalize |
| heading letter-spacing | 0–10px |
| list marker-style | disc, circle, square, none (unordered lists) |
| list nested-indent | 0–120px (reading view only) |
| layout max-width | 320–2400px |
| layout padding | 0–400px (bottom up to 600px) |
| combined padding | leaves at least 240×240px on the minimum 480×640px custom page |
| image frame | none, thin, photo, polaroid, scrapbook, rounded, technical, dark, vintage |
| image float | none, left, right |
| image object-fit | contain, cover, fill, scale-down |
| image duotone | a hex color or none |
| image rotation | -15–15 degrees |
| image max-width | 10–100 percent |
| opacity/sepia/grayscale | 0–1 |
| saturation/contrast | 0–4 |
| block colors | valid CSS colors, including both highlight background and highlighted text |
| divider style | solid, dashed, dotted, double, fade |
| divider width | 1–20px |
| code size | 8–48px; baseline measured independently from body text |
| table border width | 0–12px |
| table font size | 8–48px |
| table padding | 0–40px |
| callout border width | 0–12px |
| callout/embed radius | 0–60px |
| callout variant key | letters, digits, and hyphens; at most 64 variants; each may override accent, background, text, title, and icon colors |
| watermark size | 24–240px |
| watermark rotation | -45–45 degrees |
| watermark opacity | 0.05–1 |
| custom CSS | maximum 50 KB and safe virtual selectors only; generated CSS is capped at 1 MB before installation |
Template pack format
Section titled “Template pack format”A .templar-pack file is YAML with a top-level templar-pack mapping:
templar-pack: version: 1 # pack format identifier name: Academic Writing description: Portable research and publishing styles. author: Example Author tags: [academic, writing] templates: - version: 1 style-name: Thesis Draft template-id: thesis-draft # ...complete normalized template...Folder values inside templates remain display metadata and never create vault folders. The raw YAML import is capped at 8 MB and a pack at 256 template members before detailed validation. Every member is normalized and validated independently through the standalone import path. Duplicate IDs inside one pack are explicit member errors. An invalid member is blocked without invalidating otherwise valid members. Built-in IDs cannot be replaced by imported content; conflicts must be kept or imported as a custom copy. Normalized note styles cap callout variants at 64, attachment overrides at 512 (UTF-8 filenames at 512 bytes), tags at 64 entries with 80-byte values, style rules at 128 with 32 conditions each, and the serialized note at 512 KB.
Baseline behavior
Section titled “Baseline behavior”strict: body, blocks, headings, and image exits snap to the grid.balanced: body and list baselines remain aligned, but blocks are packed on consecutive grid rows instead of reserving an empty row.free: no baseline enforcement; best for blank/sketch layouts.
Font size and grid unit are independent. Templar measures the requested font stack after loading. Its inline probe ends exactly at the browser’s text baseline, so the probe’s bottom border-box edge is the baseline measurement (not its top edge). It also records the rendered line-box height when a font’s ascent/descent forces the browser to exceed the requested line-height. For every active Source, Live Preview, or Reading content root, Templar finds the first real rhythmic text target outside frontmatter/Properties and independently snapped widgets, then calculates:
paper phase = (target top - content top + target border/padding top + target font baseline) modulo grid unitThe calculation is normalized for CSS zoom. The compiler emits one absolute grid origin from the configured page top padding and measured body baseline; BaselineGridController never follows the first attached viewport block and never re-anchors on scroll. Ruled, dot-grid, and graph paper use this fixed origin and the effective content-left padding as their horizontal origin. A ruled stroke starts at the alphabetic baseline and extends one CSS pixel downward. Ordinary glyph bodies therefore rest on the ink while descenders such as g, p, q, and y naturally cross it. Dot and graph intersections coincide with the text grid rather than an unrelated page origin, and switching Source/Live Preview/Reading or showing/hiding Properties does not introduce a ruled-row shift.
The additional patterns are decorative overlays, not baseline rules: ledger adds a second margin line to the ruled pattern, diagonal tiles one complete 45-degree stroke, cross-hatch tiles both crossing strokes, hex tiles a six-layer isometric lattice, and scallop staggers outlined semicircles on the baseline. Tile dimensions use the grid unit multiplied by pattern-scale. Each visual and optional margin line owns matching image/size/position/repeat entries, so adding a margin cannot change how another layer tiles. pattern-opacity fades every pattern color by mixing toward transparent; dot-grid uses dot-radius for the dot size; graph uses graph-major-interval for the heavy lines.
typography.body-line-height overrides the automatic rhythm (1.55 × body size, minimum 22px) when gridded modes are off. first-line-indent and nested-indent only apply in Reading View because Live Preview wraps every paragraph line as its own element. drop-cap floats the first letter of the first paragraph after a heading. Older v1 styles that omit H5/H6 receive the safe v1 defaults for those levels; a complete export writes all six heading levels.
watermark.text renders behind the page content (above the paper) as a rotated centered label at watermark.size, rotation, and opacity. Empty text hides it. The watermark sits below the content plane in both modes, so it never intercepts pointer events or selection.
blocks.callout-variants is keyed by Obsidian callout type (for example, warning for > [!warning]). A variant can override any subset of accent, background, textColor, titleColor, and iconColor; omitted values inherit the base callout palette. Because this is a complete palette rendered on an isolated page surface, Templar uses normal compositing rather than inheriting a host theme’s callout blend mode. These nested keys are camelCase even though top-level persisted field names use kebab-case. The six nested heading objects also use the internal camelCase keys letterSpacing and textTransform because the canonical serializer preserves their object shape; normalizeTemplate() accepts the same internal form.
In both gridded modes, every inter-block offset is a whole multiple of the grid unit. Reading View list items and Live Preview list lines explicitly use the body line-height, with Obsidian’s theme list padding removed. This prevents paragraphs, bullets, and later blocks from drifting between Graph Paper lines.
Horizontal rules are rhythm blocks in strict and balanced modes. The complete Reading <hr> or Live Preview horizontal-rule line occupies exactly one baseline unit, external theme margins are removed, and the configured stroke is centered inside the row. Solid, dashed, dotted, double, and fade variants have identical vertical footprints. If the stored divider width is too large, rendering clamps only the visible stroke to at most one third of the active unit; the persisted value is not rewritten. Free mode and disabled baselines keep normal divider spacing.
Other variable-height renderers keep their natural content height. In strict and balanced modes, Templar measures the renderer-owned outer block used by Reading or Live Preview and appends only enough generated space to make its complete border-box-plus-external-margins footprint reach the closest following grid boundary. This applies to tables, rendered fenced-block output such as Mermaid, callouts, embeds, iframes, video, audio, and canvas content. The whole Reading document and frontmatter UI are never measurement owners. Precise resizes are animation-frame coalesced, Templar’s previous tail is removed from the natural-height calculation, and sub-pixel values already at a boundary add no row. Wrappers own a trailing pseudo-element, while direct table/replaced elements extend their captured natural end margin. Stored template fields and Markdown are untouched, and explicit empty source lines after the block remain additional whole baseline rows. Free mode and disabled baselines do not receive this correction.
Reading View preserves source blank-line counts with plugin-owned, grid-sized spacer blocks. The Markdown remains unchanged; removing Templar returns to standard Markdown whitespace behavior. Fenced-code blank lines are ignored by the spacer parser because they already render inside the code block.
Fenced code uses its own measured font baseline and actual rendered line-box height. Reading View receives calculated top/bottom padding whose complete footprint is a grid multiple, while every code line uses the body grid line-height. This keeps the first code baseline, all following code lines, and the block after the fence on the paper ruling.
Heading line boxes round up to grid multiples. Extra top/bottom padding aligns the heading baseline while preserving a grid-multiple total.
Live Preview must not apply vertical margins to CodeMirror .cm-line elements. CodeMirror’s pointer hit-testing and height map require measurable line boxes; Templar therefore uses border-box padding for editor heading space and reserves ordinary block margins for Reading View. Source blank lines remain ordinary CodeMirror lines, so their visible height and click coordinates stay identical.
The blocks.highlight-background and blocks.highlight-text-color fields always render together for ==highlighted text== in Reading and Live Preview. Templates should choose the pair as one palette decision and maintain readable contrast; Templar does not inherit Obsidian’s theme highlight colors.
Virtual CSS vocabulary
Section titled “Virtual CSS vocabulary”Supported roots include:
.page.page-content.page h1.page h2.page h3.page h4.page h5.page h6.page p.page ul.page ol.page li.page blockquote.page img.page table.page code.page pre.page hr.page a.page mark.page input[type="checkbox"]Every selector must start with .page or .page-content. Descendants and pseudo-classes are allowed:
.page a:hover { ... }.page table th { ... }.page blockquote::before { ... }Selectors whose subject is the virtual root itself, or whose final subject is
the universal descendant (.page *, .page > :is(*), and equivalent
:where(*) forms), are treated as whole-page coverage. Availability-affecting
properties such as display, visibility, content-visibility, opacity,
pointer-events, filters, masks, clipping, transforms, scale, and zoom
are rejected for that coverage. Narrow selectors such as .page img remain
available for decorative effects.
Invalid examples:
body .page { ... }.workspace h1 { ... }h1 { ... }Viewport media queries are prohibited because paged notes must not reflow when the window changes. Preference media queries for reduced motion, color scheme, and contrast are allowed.
Viewport/container units, env() lengths, @container queries, !important, and private .templar-* runtime classes are also prohibited. Structured CSS values are self-contained: var(), env(), and attr() are rejected in colors and interpolated scalar fields so a pasted template cannot depend on a host stylesheet or element attribute. Physical newlines/controls inside CSS strings, unterminated strings/comments, and malformed constructs that PostCSS would recover differently from a browser are rejected before compilation. Templar reserves geometry and root typography declarations on .page and .page-content (width, height, padding, margin, overflow, positioning, transforms, zoom, font, and line height), because those declarations define the fixed canvas and measured baseline. When baseline alignment is active, descendants that participate in document rhythm also cannot override vertical box/font geometry; use structured fields for those dimensions and keep custom CSS decorative.
Attachment overrides
Section titled “Attachment overrides”A note may override individual attachments without changing Markdown:
attachments: mountains.jpg: frame: polaroid rotation: -2 width: 420Current overrides support frame metadata, rotation, and pixel width. The renderer matches encoded attachment filenames in image sources; original files remain untouched.
Compatibility and migrations
Section titled “Compatibility and migrations”Template exports identify v1. H4, the expanded quote/code/table palette, and the 1.1 feature batch (h5/h6, heading letter spacing and text transform, lists and watermark, new paper patterns, image float/object-fit/duotone, and the extended block palette) are backward-compatible v1 additions: older styles receive defaults during normalization. Note provenance and .templar-pack are wrappers around normalized v1 templates, not a reinterpretation of their design fields. Notes without provenance remain renderable and receive explicit legacy choices during synchronization. A future v2 importer must explicitly migrate known v1 fields before normalization. Never silently reinterpret a v1 field with new units or semantics.