Files
civilyticsR/inst/quarto/typst/typst-template.typ
T
jared 4fb5c488f8
R-CMD-check / R CMD check (pull_request) Successful in 4m27s
fix(brand): use real company name "Civilytics Consulting" in templates
The title-page kicker in the Typst and LaTeX templates was hardcoded to
"Civilytics Research", which is not a real entity — the company is
Civilytics Consulting. Fix the kicker in both templates, and update the
example report/slides that echoed the same non-real name. Bump to 0.3.1.
2026-07-09 16:41:55 -04:00

238 lines
6.3 KiB
Typst

// =============================================================
// Civilytics — Typst template partial for Quarto PDF (typst-template.typ).
// Shipped as a Quarto template-partial (paired with typst-show.typ) rather
// than a full `template:` so Quarto keeps its own `definitions` partial —
// which defines Skylighting/token functions needed for syntax-highlighted
// code blocks. See civilyticsR issue #12.
// Usage in YAML:
// format:
// typst:
// template-partials:
// - quarto/typst/typst-template.typ
// - quarto/typst/typst-show.typ
// =============================================================
#let paper-bg = rgb("#FAF7F2")
#let ink = rgb("#0E1A2B")
#let ink-2 = rgb("#2B3A52")
#let ink-3 = rgb("#5A6A82")
#let rule = rgb("#D6CEBD")
#let rule-strong = rgb("#B8AE97")
#let navy = rgb("#22406A")
#let ember = rgb("#C25311")
#let ember-dark = rgb("#923D00")
#let plum = rgb("#6B3A5E")
#let teal = rgb("#1F6F70")
#let serif-stack = ("Source Serif 4", "Source Serif Pro", "Georgia", "Times New Roman")
#let sans-stack = ("Inter", "Helvetica Neue", "Arial")
#let display-stack = ("Libre Franklin", "Inter", "Helvetica Neue")
#let mono-stack = ("JetBrains Mono", "Menlo", "Consolas")
#let civilytics(
title: none,
subtitle: none,
authors: (),
date: none,
abstract: none,
toc: true,
doc,
) = {
// Page setup
set page(
paper: "us-letter",
margin: (top: 1in, bottom: 1in, left: 1.1in, right: 1.1in),
fill: paper-bg,
header: context {
if counter(page).get().first() > 1 {
set text(font: sans-stack, size: 8pt, fill: ink-3, tracking: 0.06em)
upper[Civilytics Consulting]
h(1fr)
upper(if title != none { title } else { "" })
v(2pt)
line(length: 100%, stroke: 0.4pt + rule)
}
},
footer: context {
set text(font: sans-stack, size: 8pt, fill: ink-3)
line(length: 100%, stroke: 0.4pt + rule)
v(4pt)
grid(
columns: (1fr, auto, 1fr),
align: (left, center, right),
[civilytics.com],
counter(page).display("1 / 1", both: true),
[© 2026]
)
},
)
// Body text
set text(font: serif-stack, size: 10.5pt, fill: ink, lang: "en")
set par(justify: false, leading: 0.6em, first-line-indent: 0pt)
// Headings
show heading.where(level: 1): it => {
v(1.4em)
block[
#set text(font: display-stack, weight: 900, size: 22pt, fill: ink, tracking: -0.025em)
#it.body
]
v(0.2em)
line(length: 60pt, stroke: 3pt + ember)
v(0.6em)
}
show heading.where(level: 2): it => {
v(1.1em)
line(length: 100%, stroke: 0.5pt + rule)
v(0.5em)
block[
#set text(font: display-stack, weight: 800, size: 16pt, fill: ink, tracking: -0.02em)
#it.body
]
v(0.3em)
}
show heading.where(level: 3): it => {
v(0.8em)
block[
#set text(font: display-stack, weight: 700, size: 13pt, fill: ink, tracking: -0.015em)
#it.body
]
v(0.2em)
}
show heading.where(level: 4): it => block[
#set text(font: sans-stack, weight: 600, size: 11pt, fill: ink-2)
#it.body
]
// Links
show link: it => text(fill: navy, underline(it))
// Inline code
show raw.where(block: false): it => box(
fill: rgb("#F2EDE4"),
inset: (x: 3pt, y: 1pt),
outset: (y: 2pt),
radius: 1pt,
text(font: mono-stack, size: 0.92em, fill: ember-dark)[#it]
)
// Code blocks
show raw.where(block: true): it => block(
fill: ink,
width: 100%,
inset: 12pt,
radius: 4pt,
)[
#set text(font: mono-stack, size: 8.5pt, fill: paper-bg)
#it
]
// Block quote
show quote.where(block: true): it => block(
stroke: (left: 2pt + ember),
inset: (left: 14pt, top: 4pt, bottom: 4pt),
spacing: 1.2em,
)[
#set text(font: serif-stack, size: 13pt, style: "italic", fill: ink)
#it.body
]
// Figures
show figure: it => block(spacing: 1.4em)[
#it.body
#v(4pt)
#line(length: 100%, stroke: 0.4pt + rule)
#v(2pt)
#set text(font: sans-stack, size: 8.5pt, fill: ink-3)
#it.caption
]
// Tables
set table(
stroke: (x, y) => (
top: if y == 0 { 1.5pt + ink } else if y == 1 { 0.6pt + rule-strong } else { 0pt },
bottom: if y == 0 { 0pt } else { 0.4pt + rule },
),
inset: (x: 8pt, y: 6pt),
)
show table.cell.where(y: 0): set text(
font: sans-stack, size: 8pt, weight: 600, fill: ink-3, tracking: 0.06em
)
// ----- Title block -----
if title != none {
block[
#set text(font: sans-stack, size: 8pt, weight: 600, fill: ember, tracking: 0.1em)
#upper[— Civilytics Consulting]
]
v(8pt)
block[
#set text(font: display-stack, weight: 900, size: 32pt, fill: ink, tracking: -0.035em)
#set par(leading: 0.4em)
#title
]
if subtitle != none {
v(8pt)
block[
#set text(font: serif-stack, style: "italic", size: 14pt, fill: ink-2)
#set par(leading: 0.55em)
#subtitle
]
}
v(18pt)
line(length: 100%, stroke: 3pt + ink)
v(2pt)
line(length: 100%, stroke: 0.4pt + rule)
v(8pt)
// Authors + date row
grid(
columns: (1fr, 1fr),
align: (left, right),
[
#set text(font: sans-stack, size: 7.5pt, fill: ink-3, tracking: 0.08em)
#upper[Authors] \
#set text(font: sans-stack, size: 10pt, fill: ink, weight: 500, tracking: 0em)
#if authors.len() > 0 {
authors.map(a => if type(a) == str { a } else { a.name }).join(", ")
}
],
[
#set text(font: sans-stack, size: 7.5pt, fill: ink-3, tracking: 0.08em)
#upper[Published] \
#set text(font: sans-stack, size: 10pt, fill: ink, weight: 500, tracking: 0em)
#if date != none { date }
]
)
v(24pt)
if abstract != none {
block(
stroke: (left: 2pt + ember),
inset: (left: 14pt),
)[
#set text(font: serif-stack, style: "italic", size: 12pt, fill: ink)
#set par(leading: 0.55em)
#abstract
]
v(20pt)
}
if toc {
block[
#set text(font: sans-stack, size: 7.5pt, fill: ink-3, tracking: 0.08em)
#upper[Contents]
]
v(4pt)
line(length: 100%, stroke: 0.4pt + rule)
v(8pt)
outline(title: none, indent: auto, depth: 3)
v(20pt)
pagebreak()
}
}
doc
}