원클릭으로
typst
Write and compile excellent Typst documents — brand-aware, component-driven, data-hydrated
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Write and compile excellent Typst documents — brand-aware, component-driven, data-hydrated
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | typst |
| description | Write and compile excellent Typst documents — brand-aware, component-driven, data-hydrated |
| globs | ["**/*.typ"] |
Use this skill whenever the task touches .typ files or PDF deliverables: proposals, technical reports, research briefs, slide decks, branded collateral, or reusable Typst libraries.
Reach for this skill when you need to:
#let.typ file to PDF and send the result to the userObserved patterns in this workspace:
domain/growth/branding/spell-brand/lib.typ is the aggregation entrypoint for colors, typography, components, and layoutdomain/growth/templates/*.typ uses sys.inputs plus yaml() to hydrate branded reports and proposals/home/user/code/backdesk/.local/pitch-deck.typ uses wide pages, strong visual hierarchy, reusable cards, gradients, and placed decorative shapes for decks/home/user/code/ora/sothema/rapport-technique.typ uses native Typst primitives for polished A4 technical reports/home/user/code/ora/growth/morocco_tech_ecosystem_report.typ uses numbered headings, outline generation, and package imports for research-style documentsDefault behavior: copy the existing local pattern instead of inventing a new Typst style.
#.$.#set text(font: "Inter", size: 11pt)
This is markup.
#let total = 42
The total is #total.
$ a^2 + b^2 = c^2 $
Use #let for both values and reusable components.
#let accent = rgb("#7C3AED")
#let title = "Spell"
#let stat(value, label) = block(
inset: 12pt,
radius: 8pt,
fill: rgb("#F5F3FF"),
)[
#text(size: 20pt, weight: "bold", fill: accent)[#value]
#v(4pt)
#text(size: 10pt)[#label]
]
Prefer named optional parameters with defaults.
#let callout(title, body, fill: rgb("#F8FAFC"), stroke: rgb("#CBD5E1")) = block(
fill: fill,
stroke: 1pt + stroke,
radius: 8pt,
inset: 12pt,
)[
#text(weight: "bold")[#title]
#v(6pt)
#body
]
#import "../branding/spell-brand/lib.typ": *
#import "./components.typ": stat, callout
#import "@preview/tablex:0.0.8": tablex
Use the narrowest import that keeps the file readable.
Set document-wide defaults near the top of the file.
#set document(title: "Client Proposal", author: "Spell")
#set page(paper: "a4", margin: (x: 2.5cm, y: 2.5cm), numbering: "1")
#set text(font: ("Inter", "Helvetica Neue", "Arial"), size: 11pt, fill: rgb("#0F172A"))
#set par(justify: true, leading: 0.8em)
Use show rules for styling by selector or for structural transforms.
#show heading: set text(weight: "bold")
#show heading.where(level: 1): set text(fill: rgb("#7C3AED"), size: 20pt)
For custom heading rendering, transform with a function.
#show heading.where(level: 1): it => [
#text(weight: "bold", fill: rgb("#7C3AED"))[#it.body]
#v(4pt)
#line(length: 100%, stroke: 1.5pt + rgb("#7C3AED"))
]
#let items = ("Research", "Strategy", "Reporting")
#if items.len() > 0 [
#for item in items [
- #item
]
]
Use content blocks for anything that should render as document content.
#block(fill: rgb("#F8FAFC"), inset: 12pt)[
This is a boxed content block.
]
Use Typst primitives directly before adding abstraction.
#grid(
columns: (1fr, 1fr),
gutter: 12pt,
stat("12", "Customers"),
stat("94%", "Retention"),
)
#stack(
spacing: 8pt,
[First block],
[Second block],
)
#align(center)[#text(size: 24pt, weight: "bold")[Centered Title]]
Commonly useful functions:
text()grid()table()block()box()stack()align()pad()place()image()line()circle()rect()ellipse()Prefer explicit records and safe .at() access.
#let data = (
client: "Example Co.",
deliverables: ((item: "Audit"), (item: "Strategy")),
)
#let client = data.at("client", default: "[Client Name]")
#let deliverables = data.at("deliverables", default: ())
#for deliverable in deliverables [
- #deliverable.at("item", default: "—")
]
Useful collection methods:
.map().filter().join().flatten()#let brand-primary = rgb("#7C3AED")
#let brand-soft = brand-primary.lighten(80%)
#v(12pt)
#h(6pt)
#set document(title: "Research Report", author: "Spell")
Prefer consistent spacing increments such as 4pt, 8pt, 12pt, 16pt, 24pt.
Turn repeated layout into a function, not copy-paste.
#let section-header(title) = {
v(16pt)
text(size: 16pt, weight: "bold")[#title]
v(4pt)
line(length: 100%, stroke: 1pt + rgb("#7C3AED"))
v(10pt)
}
Use styled header rows and readable widths.
#table(
columns: (2fr, 3fr),
inset: 8pt,
stroke: 0.5pt + rgb("#CBD5E1"),
fill: (x, y) => if y == 0 { rgb("#0F172A") } else if calc.odd(y) { rgb("#F8FAFC") } else { white },
text(weight: "bold", fill: white)[Item],
text(weight: "bold", fill: white)[Notes],
[Audit], [Current-state review],
[Roadmap], [Execution plan],
)
#let card(title, body) = box(
inset: 16pt,
radius: 10pt,
fill: rgb("#111827"),
stroke: 1pt + rgb("#374151"),
)[
#text(weight: "bold", fill: white)[#title]
#v(8pt)
#text(fill: rgb("#D1D5DB"))[#body]
]
Pitch decks in this workspace use wide pages, visual accents, and sparse text.
#set page(width: 1280pt, height: 720pt, margin: 0pt)
#let bg-dark = rgb("#1C1E26")
#let crimson = rgb("#E95678")
#let coral = rgb("#F09383")
#page(fill: bg-dark)[
#place(top + right, dx: 80pt, dy: -120pt,
circle(radius: 220pt, fill: gradient.radial(
(crimson, 0%),
(coral.transparentize(70%), 40%),
(bg-dark.transparentize(100%), 100%),
)))
#align(center + horizon)[
#text(size: 42pt, weight: "bold", fill: white)[Main message]
#v(12pt)
#text(size: 18pt, fill: rgb("#B0A8A3"))[One supporting sentence]
]
]
For branded work, use the local library first.
branding/<brand>/lib.typ.#import "../branding/spell-brand/lib.typ": *
#report-page("Client Proposal", subtitle: "Growth Strategy")
#section-header("Executive Summary")
#text(font: body-font, size: body-size, fill: spell-gray-800)[
Brand-aligned body copy.
]
Rules:
body-font, heading-font, spell-purple, spell-dark, etc. over literal valuescover-page, section-header, report-pageTemplates in this repo keep placeholder content in-source and hydrate from external data when provided.
#let data = if sys.inputs.at("data", default: none) != none {
yaml("data.yaml")
} else {
(
client: "[Client Name]",
subtitle: "[Subtitle]",
deliverables: (),
)
}
#let client = data.at("client", default: "[Client Name]")
Use this for proposals, briefs, weekly digests, and report generators.
1280pt x 720pt or another 16:9 size)#set heading(numbering: "1.")#outline() for table of contentsUse the existing shell workflow. Do not add a new Typst wrapper tool.
typst compile input.typ output.pdf
typst compile --font-path ./fonts input.typ output.pdf
typst compile --input data=data.yaml input.typ output.pdf
Recommended loop:
.typ sourcesend_fileExample post-compile step:
send_file path="output.pdf"
Before you finish a Typst task, verify:
.at(..., default: ...)If unsure, do this:
lib.typ#let componentsWrite system prompts, tool docs, and agent definitions. Combines research-backed prompt engineering (+15-30% measured improvements) with project XML conventions. Covers tag hierarchy, structural templates, high-impact interventions, anti-patterns.
Interactive wizard to bootstrap a .spell/ directory with spell-server configuration, Telegram bridge, autonomy goals, state stores, and optional memory system. Run in any project directory.
Headless QML integration testing using the bridge's DOM introspection, JS evaluation, and screenshot APIs. Use when writing or debugging QML UI tests.
Spawn a native QML canvas window to display structured data (tables, diffs, trees, markdown, images) and collect user input via prompts. Use when presenting comparisons, tabular data, large diffs, or multi-step decisions.
Voice-to-agent flow: start recording, see live transcript, speak the wake word "spell" followed by a command, and the agent processes it automatically.
Generate brand asset variations (ad creatives, logos, mood boards) using gemini-image, display them in a native Qt QML gallery window, and let users rate/compare/regenerate without touching the terminal.