11---
22name : quarto-blog-post
3- description : Use when writing, drafting, or editing blog posts for quarto.org , creating Quarto feature or release announcements, or reviewing blog post drafts for the Quarto website.
3+ description : Use when writing, drafting, or editing Quarto blog posts, creating Quarto feature or release announcements, or reviewing Quarto blog post drafts. Quarto posts publish on the Posit Open Source blog (posit-dev/open-source- website), not on quarto.org .
44---
55
66# Quarto Blog Post
77
8- Write blog posts for ` quarto.org/blog ` matching the voice, structure, and conventions
9- from 40+ existing posts.
8+ Write Quarto blog posts matching the voice, structure, and conventions of 40+
9+ prior posts. Since May 2026 the Quarto blog lives on the
10+ [ Posit Open Source website] ( https://opensource.posit.co/blog/q/quarto/ ) :
11+ posts are authored and published in the
12+ [ ` posit-dev/open-source-website ` ] ( https://github.com/posit-dev/open-source-website )
13+ repo, not in quarto-web. This skill carries the Quarto-specific knowledge
14+ (post types, voice, thumbnails); defer to that repo's own guides for its
15+ mechanics.
1016
1117## Setup
1218
13- 1 . ` ls docs/blog/posts/ ` — browse existing posts for reference
14- 2 . Read ` docs/blog/posts/_metadata.yml ` — inherited by all posts (Giscus comments,
15- title-block-banner, left TOC, signup widget, ` search: false ` )
16- 3 . If the post covers a Quarto feature, read the relevant docs page for accurate
17- terminology and linking.
19+ 1 . Locate a local clone of ` posit-dev/open-source-website ` — conventionally a
20+ sibling of this repo (` ../open-source-website ` ). Clone it if missing.
21+ Check ` git status ` before branching.
22+ 2 . Read, in its ` content/blog/ ` directory:
23+ - ` CLAUDE.md ` — frontmatter schema and taxonomies
24+ - ` _authoring-guide.md ` — format choice, rendering, preview, and PR flow
25+ 3 . Browse ` content/blog/ported/quarto/ ` for prior Quarto posts. Recent ones
26+ (e.g. ` 2026-03-24-1.9-release/ ` ) show the current frontmatter and structure
27+ conventions.
28+ 4 . If the post covers a Quarto feature, read its docs from this repo — not
29+ from quarto.org — so drafting works even when the docs aren't live yet.
30+ Docs for an unreleased version live on the ` prerelease ` branch: check the
31+ working tree first, then ` git fetch origin prerelease ` and
32+ ` git show origin/prerelease:docs/<path> ` . quarto.org serves ` main ` , so
33+ anything prerelease-only won't be live until the release-time merge — see
34+ § Links for how to link to it anyway.
1835
1936## File Structure
2037
38+ In the open-source-website clone, on a branch named ` blog/<slug> ` :
39+
2140```
22- docs/blog/posts/YYYY-MM-DD-slug/
23- index.qmd # The post (required)
24- thumbnail.png # Listing card image (required)
41+ content/blog/<slug>/
42+ index.qmd # Source (required)
43+ index.md # Rendered output — always commit alongside the source
44+ thumbnail.png # Hero + listing card image, 1920×1080 (required)
2545 *.png, *.jpg # Additional images
26- _contribs.md # Contributor list (release posts only)
2746```
2847
29- Directory name: ` YYYY-MM-DD-slug ` — date matches frontmatter, slug is short kebab-case.
48+ The folder name is the URL slug: short kebab-case, no date prefix
49+ (e.g. ` quarto-1-10 ` ). The published URL becomes ` /blog/YYYY-MM-DD_<slug>/ `
50+ from the frontmatter ` date ` . Never scaffold under ` content/blog/ported/ ` —
51+ that tree is for migrated legacy posts.
3052
3153## Frontmatter
3254
33- ``` yaml
34- ---
35- title : " Post Title"
36- description : |
37- One to three sentences for listing cards and social sharing.
38- author : Author Name
39- date : " YYYY-MM-DD"
40- categories :
41- - Category1
42- - Category2
43- image : thumbnail.png
44- image-alt : " Descriptive alt text for the thumbnail."
45- ---
46- ```
55+ Don't write the frontmatter from memory — scaffold it from the repo's Hugo
56+ archetype (` archetypes/blog.md ` , the source of truth for the schema; see
57+ Workflow step 2) and fill it in. Field meanings and taxonomies are documented
58+ in open-source-website's ` content/blog/CLAUDE.md ` .
4759
48- ** title ** : Short. Release posts: ` " Quarto X.Y" ` . Backtick code spans OK.
60+ Quarto-post specifics when filling in the scaffold:
4961
50- ** description** : Self-contained summary (makes sense without the title). Always ` | ` block scalar.
62+ ** source: quarto** — required, and not in the archetype. Places the post on
63+ the [ Quarto project listing] ( https://opensource.posit.co/blog/q/quarto/ ) ,
64+ where old quarto.org blog URLs redirect. Set ` software: quarto ` too.
5165
52- ** author** : Plain string for staff (` Charlotte Wickham ` ). Two authors: ` Name and Name ` .
53- Guest authors use structured form with ` name: ` and optional ` url: ` .
66+ ** date** : a future date schedules the post — Hugo hides it until a daily
67+ build (8 AM UTC) on the publish date, so set the date the PR is expected to
68+ merge (or later). No date-fixing script needed.
5469
55- ** date** : ISO 8601 ` "YYYY-MM-DD" ` . Must match directory name.
70+ ** image / image-alt** : set ` image: thumbnail.png ` , 1920×1080 (16:9) — used as
71+ both the hero and the listing card. ` image-alt ` is mandatory.
72+ See ` references/thumbnail-guide.md ` for the decision between the two
73+ production paths (Typst, or HTML+SVG) and the HTML+SVG flow. For the Typst
74+ path, see ` references/typst-thumbnail.md ` .
5675
57- ** categories** : 2-3 per post. Use existing values — scan recent posts to check. Common:
58- ` Releases ` , ` Quarto X.Y ` , ` Features ` , ` Authoring ` , ` Learn ` , ` Workshop ` , ` Conference ` ,
59- ` Tip ` , ` Extensions ` , ` Tables ` , ` Teaching ` , ` Jupyter ` .
76+ ** people** : full names, individuals only — never a team name.
6077
61- ** image / image-alt** : ` thumbnail.png ` preferred. ` image-alt ` is mandatory.
62- See ` references/thumbnail-guide.md ` for the decision between the two production
63- paths (Typst, or HTML+SVG) and the HTML+SVG flow. For the Typst path, see
64- ` references/typst-thumbnail.md ` .
78+ ** tags** : freeform — this is where the old quarto.org ` categories ` vocabulary
79+ lives on (` Releases ` , ` Quarto X.Y ` , ` Features ` , ` Authoring ` , ` Learn ` ,
80+ ` Workshop ` , ` Tip ` , ...; the port moved each post's categories into its tags).
81+ Reuse tags already present in ` content/blog/ported/quarto/ ` sources rather
82+ than coining synonyms, and skip a bare ` Quarto ` tag — ` source ` and ` software `
83+ already cover it.
6584
66- ** Optional** : ` lightbox: true ` (many screenshots), ` draft: true ` (while developing).
67- Do not use ` subtitle: ` (phased out after 2023).
85+ Never add ` ported_from ` or ` port_status ` (migration metadata), and don't carry
86+ over the old quarto.org schema: no ` categories ` , no ` subtitle ` , no
87+ ` _metadata.yml ` inheritance.
6888
6989## Post Types
7090
@@ -103,8 +123,9 @@ Every image must have `fig-alt=` text — non-negotiable accessibility standard.
103123![ ] ( screenshot.png ) {fig-alt="Description of what the screenshot shows."}
104124```
105125
106- Multi-image layouts use Quarto's layout system (` {layout-ncol="2"} ` ).
107- For many images, add ` {.lightbox group="name"} ` .
126+ Multi-image layouts use Quarto's layout system (` {layout-ncol="2"} ` ) — a Lua
127+ filter in open-source-website converts these for Hugo. Don't use ` .lightbox ` ;
128+ that's a quarto.org HTML feature with no equivalent in the Hugo pipeline.
108129
109130### Code Blocks
110131
@@ -119,29 +140,101 @@ project:
119140
120141### Links
121142
122- Every feature mentioned links to its docs page. Pattern: explain briefly, show example,
123- then link. Use site-root-relative paths: ` [Feature](/docs/path.qmd) ` .
143+ ** Docs links must be absolute** : ` https://quarto.org/docs/... ` , never a
144+ ` .qmd ` source path or a site-root-relative path — the post no longer lives on
145+ quarto.org. Derive the URL from the repo path:
146+ ` docs/<path>/<page>.qmd ` → ` https://quarto.org/docs/<path>/<page>.html ` .
147+ Every feature mentioned links to its docs page. Pattern: explain briefly,
148+ show example, then link.
149+
150+ ** Verify every quarto.org link resolves** (e.g.
151+ ` curl -s -o /dev/null -w '%{http_code}' <url> ` ). A page whose docs exist only
152+ on the ` prerelease ` branch will 404 until the release-time
153+ ` prerelease ` → ` main ` merge. Keep the quarto.org URL anyway — never
154+ substitute the prerelease site's domain — and ** flag every such
155+ pending-merge link when handing off the draft** so it's re-checked before the
156+ post publishes.
157+
158+ ** Other blog posts** : link with the permalink pattern ` /blog/YYYY-MM-DD_slug/ `
159+ (see the authoring guide), never content-directory paths.
124160
125161### Callouts
126162
127163Use sparingly: ` .callout-tip ` for post origin context, ` .callout-warning ` for caveats,
128- ` .callout-note ` for prerequisites. Release posts typically skip callouts.
164+ ` .callout-note ` for prerequisites. Quarto callouts render correctly through
165+ the Hugo pipeline.
129166
130167### Shortcodes
131168
132- - ` {{< prerelease-callout X.Y type="blog" >}} ` — pre-release banner (auto-disappears)
133169- ` {{< video URL >}} ` — video embed
134170- ` {{< include file.md >}} ` — include generated content
135171
172+ Both are processed by Quarto at render time and work in open-source-website.
173+
174+ ** Displaying literal shortcode syntax** (showing readers what to type) takes
175+ three ingredients — this is how every such example in the ported Quarto posts
176+ works (verified empirically):
177+
178+ 1 . ** Put the syntax in code** — a fenced block or inline code, never prose.
179+ An escape in prose reaches Hugo as a live shortcode: a name Hugo knows
180+ silently renders its output in place of your example; an unknown name
181+ fails the entire site build.
182+ 2 . ** Escape it so Quarto doesn't execute it** : triple braces
183+ (` {{{< meta state >}}} ` ), or the ` shortcodes="false" ` attribute on a
184+ fenced block (` {.markdown shortcodes="false"} ` ). Unescaped syntax in code
185+ is swallowed at the Quarto stage, leaving empty code.
186+ 3 . ** Opt into the site's escape filter** in the post frontmatter:
187+
188+ ``` yaml
189+ filters :
190+ - escape-shortcodes
191+ ` ` `
192+
193+ The filter (` content/_extensions/escape-shortcodes`) rewrites `{{<` to
194+ Hugo's comment escape in code contexts, so Hugo displays the syntax
195+ instead of executing it. Ported Quarto posts get it automatically via
196+ ` content/blog/ported/quarto/_metadata.yml` ; new posts must opt in.
197+
198+ In prose, refer to shortcodes by name instead ("the `video` shortcode").
199+
200+ **`{{< prerelease-callout >}}` does NOT exist there** — it's a quarto-web
201+ extension. For a post about an unreleased feature, write a static callout
202+ instead, and remove it once the version ships :
203+
204+ ` ` ` markdown
205+ ::: {.callout-note}
206+ This feature is available in the
207+ [pre-release version of Quarto](https://quarto.org/docs/download/prerelease.html)
208+ and will be part of the upcoming X.Y release.
209+ :::
210+ ` ` `
211+
136212# # Workflow
137213
1382141. **Identify post type** → read `references/post-types.md` for that type
139- 2 . ** Create directory** : ` docs/blog/posts/YYYY-MM-DD-slug/ `
140- 3 . ** Write frontmatter** per schema above
141- 4 . ** Draft body** following type-specific structure
215+ 2. **Branch and scaffold** in the open-source-website clone, off up-to-date
216+ `main`, mirroring its `/new-post` command :
217+
218+ ` ` ` bash
219+ git checkout -b blog/<slug>
220+ hugo new blog/<slug>/index.md
221+ mv content/blog/<slug>/index.md content/blog/<slug>/index.qmd
222+ ` ` `
223+
224+ ` hugo new` fills the frontmatter from `archetypes/blog.md`, so the schema
225+ stays in sync with the site. If `hugo` isn't installed, copy the
226+ frontmatter from `archetypes/blog.md` by hand instead — same source of
227+ truth. (In a Claude session started in that repo, the `/new-post` command
228+ runs this flow interactively.)
229+ 3. **Fill in the frontmatter** : replace the placeholder `title`, `people`,
230+ and `description`; trim `topics`, `languages`, and `tags` to what applies;
231+ delete unused optional fields; and set the Quarto specifics above
232+ (`source : quarto`, `image`/`image-alt`, `date`)
233+ 4. **Draft body** following type-specific structure. Body headings start at
234+ ` ##` — the H1 comes from `title`.
1422355. **Add images** with `fig-alt=` on every one
143- 6 . ** Link to docs** for every feature mentioned
144- 7 . ** Create thumbnail** :
236+ 6. **Link to docs** with absolute quarto.org URLs for every feature mentioned
237+ 7. **Create thumbnail** (1920×1080) :
145238 1. Read `references/thumbnail-guide.md` § Choosing your path
146239 2. **Always present both options as a question to the user**, even if one
147240 path is the obvious recommendation. State the recommendation with
@@ -151,21 +244,27 @@ Use sparingly: `.callout-tip` for post origin context, `.callout-warning` for ca
151244 3. Read the chosen path's reference and execute
152245 (`references/typst-thumbnail.md` for Typst, rest of `thumbnail-guide.md`
153246 for HTML+SVG)
154- 8 . ** Review** : direct opener? code blocks fenced with language? all images alt-texted?
155- docs links present? closing matches type convention? categories correct?
247+ 8. **Render** : from the post directory, `quarto render index.qmd` — this
248+ produces the `index.md` Hugo builds from. Commit `index.qmd`, `index.md`,
249+ and any generated outputs together.
250+ 9. **Validate** : from the open-source-website root:
251+
252+ ` ` ` bash
253+ uv run scripts/validate-blog-posts.py content/blog/<slug>/index.md
254+ ` ` `
255+
256+ In a Claude session started in that repo, the `/check-post` and
257+ ` /review-post` commands run validation and a content review interactively.
258+ 10. **Review** : direct opener? code blocks fenced with language? all images
259+ alt-texted? absolute docs links, each verified live or flagged as
260+ pending-merge? closing matches type convention? frontmatter complete
261+ (`people`, `topics`, `source : quarto`)?
156262
157263# # Publishing
158264
159- When ready to publish, set the post date to today and rename the directory:
160-
161- ``` bash
162- quarto run _tools/publish-date.ts docs/blog/posts/YYYY-MM-DD-slug
163- ```
164-
165- This updates ` date: ` in frontmatter and renames the directory to match. Run it
166- on the day you intend to merge — avoids manual date edits if the publish date slips.
167-
168- ## PR Workflow
169-
170- PR to ` main ` first. On merge, auto-backport creates cherry-pick PR to ` prerelease ` .
171- Push branches to ` upstream ` remote (quarto-dev/quarto-web), not ` origin ` .
265+ Open a PR against `main` in `posit-dev/open-source-website` — never push
266+ directly to `main`. Posit org members push branches to the repo itself (no
267+ fork needed); a Netlify preview is posted on the PR automatically, and merging
268+ requires one approving review from someone with Write access. To schedule a
269+ post, set a future `date` and merge — it goes live on that date's morning
270+ build. Details : ` content/blog/_authoring-guide.md` § Publishing your post.
0 commit comments