Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e0027c6
add blog post writing skill
cderv Apr 13, 2026
3344860
draft blog post: chrome-headless-shell announcement
cderv Apr 13, 2026
6f661d5
add thumbnail design guide to blog post skill
cderv Apr 13, 2026
5d0fa3a
add thumbnail and icon attribution to blog post
cderv Apr 13, 2026
a8d7a00
update thumbnail guide with HTML/CSS creation method
cderv Apr 13, 2026
bc057f5
show draft pages in PR deploy previews
cderv Apr 13, 2026
5294bd1
detect draft pages in PR previews and warn before merge
cderv Apr 13, 2026
156ed2b
extract draft detection into reusable composite action
cderv Apr 13, 2026
958b0eb
simplify draft warning in PR comment to avoid duplicate links
cderv Apr 13, 2026
1211644
document draft-check workflow and detect-drafts action in README
cderv Apr 13, 2026
9a28438
document pr-preview profile in README
cderv Apr 13, 2026
87d53c8
fix review findings: shell injection, POSIX grep, .md coverage, CI wo…
cderv Apr 13, 2026
a003539
update thumbnail background to Quarto blue (#5286ab)
cderv Apr 13, 2026
112a051
shorten blog post description for listing card
cderv Apr 13, 2026
51e909e
address review feedback: light thumbnail, remove platform examples
cderv Apr 14, 2026
b19127a
fix file:// example in thumbnail guide to include --allow-file-access
cderv Apr 14, 2026
0e3048f
add prerequisites section to thumbnail guide for tool availability
cderv Apr 14, 2026
bf8d21b
remove draft: true from chrome-headless-shell blog post
cderv Apr 14, 2026
c07f2f5
note in thumbnail guide: always use PNG, never SVG directly
cderv Apr 14, 2026
80ca8f1
update blog post date to 2026-04-14 and rename directory to match
cderv Apr 14, 2026
b007ec6
add publish-date script for blog post date + directory rename
cderv Apr 14, 2026
d88e5c4
add README to _tools/ documenting project scripts
cderv Apr 14, 2026
c571ff2
note renv/pipenv setup in _tools README
cderv Apr 14, 2026
eeff9ff
fix publish-date: rename dir even when date matches, rename before write
cderv Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions .claude/skills/blog-post/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
name: quarto-blog-post
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.
---

# Quarto Blog Post

Write blog posts for `quarto.org/blog` matching the voice, structure, and conventions
from 40+ existing posts.

## Setup

1. `ls docs/blog/posts/` — browse existing posts for reference
2. Read `docs/blog/posts/_metadata.yml` — inherited by all posts (Giscus comments,
title-block-banner, left TOC, signup widget, `search: false`)
3. If the post covers a Quarto feature, read the relevant docs page for accurate
terminology and linking.

## File Structure

```
docs/blog/posts/YYYY-MM-DD-slug/
index.qmd # The post (required)
thumbnail.png # Listing card image (required)
*.png, *.jpg # Additional images
_contribs.md # Contributor list (release posts only)
```

Directory name: `YYYY-MM-DD-slug` — date matches frontmatter, slug is short kebab-case.

## Frontmatter

```yaml
---
title: "Post Title"
description: |
One to three sentences for listing cards and social sharing.
author: Author Name
date: "YYYY-MM-DD"
categories:
- Category1
- Category2
image: thumbnail.png
image-alt: "Descriptive alt text for the thumbnail."
---
```

**title**: Short. Release posts: `"Quarto X.Y"`. Backtick code spans OK.

**description**: Self-contained summary (makes sense without the title). Always `|` block scalar.

**author**: Plain string for staff (`Charlotte Wickham`). Two authors: `Name and Name`.
Guest authors use structured form with `name:` and optional `url:`.

**date**: ISO 8601 `"YYYY-MM-DD"`. Must match directory name.

**categories**: 2-3 per post. Use existing values — scan recent posts to check. Common:
`Releases`, `Quarto X.Y`, `Features`, `Authoring`, `Learn`, `Workshop`, `Conference`,
`Tip`, `Extensions`, `Tables`, `Teaching`, `Jupyter`.

**image / image-alt**: `thumbnail.png` preferred. `image-alt` is mandatory.
See `references/thumbnail-guide.md` for dimensions, visual style, and design patterns.

**Optional**: `lightbox: true` (many screenshots), `draft: true` (while developing).
Do not use `subtitle:` (phased out after 2023).

## Post Types

Identify the type before writing — it determines structure, opening, and closing.
Read `references/post-types.md` for detailed structure guidance per type.

| Type | When | Key trait |
|------|------|-----------|
| **Release** | New Quarto version ships | Most structured: features → Other Highlights → Acknowledgements |
| **Feature** | Spotlight a specific capability | Concept-driven sections, docs links |
| **How-to** | Tutorial or walkthrough | Problem → solution, sequential steps |
| **News** | Short announcement, roundup | Very short, layout-heavy, minimal prose |

## Writing Voice

**First-person plural**: "We" for team work, "you" for the reader.

**Warm, not marketing**: "We're excited about this feature" — good.
"This groundbreaking capability" — bad. Collegial tone, sharing with practitioners.

**Direct openers**: Get to the point immediately. No "In this blog post, we will..."
preambles. State what happened or what the feature does, then elaborate.

**Technical accuracy**: Use exact terminology from the docs. Link to docs rather than
trying to replicate them — the post introduces, the docs page is the reference.

**Thank contributors**: Call out external contributors warmly inline.

## Technical Conventions

### Images

Every image must have `fig-alt=` text — non-negotiable accessibility standard.

```markdown
![](screenshot.png){fig-alt="Description of what the screenshot shows."}
```

Multi-image layouts use Quarto's layout system (`{layout-ncol="2"}`).
For many images, add `{.lightbox group="name"}`.

### Code Blocks

Always specify language. Use `filename=` labels for file content or terminal commands:

````markdown
```{.yaml filename="_quarto.yml"}
project:
type: website
```
````

### Links

Every feature mentioned links to its docs page. Pattern: explain briefly, show example,
then link. Use site-root-relative paths: `[Feature](/docs/path.qmd)`.

### Callouts

Use sparingly: `.callout-tip` for post origin context, `.callout-warning` for caveats,
`.callout-note` for prerequisites. Release posts typically skip callouts.

### Shortcodes

- `{{< prerelease-callout X.Y type="blog" >}}` — pre-release banner (auto-disappears)
- `{{< video URL >}}` — video embed
- `{{< include file.md >}}` — include generated content

## Workflow

1. **Identify post type** → read `references/post-types.md` for that type
2. **Create directory**: `docs/blog/posts/YYYY-MM-DD-slug/`
3. **Write frontmatter** per schema above
4. **Draft body** following type-specific structure
5. **Add images** with `fig-alt=` on every one
6. **Link to docs** for every feature mentioned
7. **Create thumbnail** (or note one is needed)
8. **Review**: direct opener? code blocks fenced with language? all images alt-texted?
docs links present? closing matches type convention? categories correct?

## PR Workflow

PR to `main` first. On merge, auto-backport creates cherry-pick PR to `prerelease`.
Push branches to `upstream` remote (quarto-dev/quarto-web), not `origin`.
99 changes: 99 additions & 0 deletions .claude/skills/blog-post/references/post-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Post Type Reference

Detailed structure guidance for each blog post type. Read the section that matches
your post — you rarely need all four.

## Release Announcements

The most structured type. Readers expect consistency across releases.

**Opening**: One direct sentence announcing the release, immediately followed by
download and changelog links. No preamble.

```markdown
Quarto 1.9 is out! You can get the current release from the
[download page](/docs/download/index.qmd). Read the full
[changelog](https://github.com/quarto-dev/quarto-cli/releases/tag/v1.9)
for a complete list of changes.
```

**Feature sections**: Each major feature gets a `##` heading named after the feature
(not "What's New"). Order by importance. Each section:
- Explain in 2-3 sentences
- Code example or screenshot (or both)
- Link to docs: "Learn more at [Feature Name](/docs/path)."

**Other Highlights**: `## Other Highlights` bundles smaller improvements. Format:

```markdown
- [Feature Name](/docs/path)---Short description of what it does.
```

Note the em-dash (`---`) between link and description.

**Dependency updates**: List bundled tool updates (Pandoc, Typst, Deno) after Other
Highlights if applicable.

**Closing**: Always `## Acknowledgements`. Thank contributors. Recent releases use
`{{< include _contribs.md >}}`. Release posts with emoji thumbnails include OpenMoji
attribution at the very end.

**Categories**: Always `Quarto X.Y` + `Releases`.

---

## Feature Announcements

Spotlight a specific feature, often published before the corresponding release. More
varied structure than release posts.

**Opening**: If unreleased, add prerelease callout at the very top:

```markdown
{{< prerelease-callout 1.10 type="blog" >}}
```

Then a direct statement of what the feature does. Get to the point — readers clicked
because the title caught their interest.

**Body**: Organize around the feature's concepts, not a "what's new" list. `##`
headings name the concept being explained.

Problem/Solution framing works well for features that address a pain point: explain
the problem first, then show how the feature solves it.

**Closing**: Link to the documentation. "Learn more on the [Feature Name](/docs/path)
page." No acknowledgements section.

---

## Technical How-to Posts

Tutorial-style walkthroughs.

**Opening**: If based on a talk or repost, start with a `.callout-tip` providing context
and linking the original source. Then frame the use case — what problem does the reader
have?

**Body**: Walk through steps sequentially. Heading names can be conversational — verbs
and questions are fine ("Create the content", "Why are we doing this?"). Use numbered
sub-steps when sequence matters.

Show before/after comparisons with `{layout-ncol="2"}` divs.

**Closing**: Brief summary of what was covered, or a "Learn more" section with resource
links. No acknowledgements.

---

## News/Community Posts

Short announcements, conference roundups, workshop materials. The lightest type.

**Opening**: One direct summary sentence.

**Body**: Often visual grids rather than prose. Conference roundups use
`{layout="[70,30]"}` divs pairing descriptions with thumbnails. Video posts embed
with `{{< video >}}`.

**Closing**: May have no explicit closing — the content structure speaks for itself.
84 changes: 84 additions & 0 deletions .claude/skills/blog-post/references/thumbnail-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Thumbnail Guide

Design conventions for blog post listing card images, derived from 40 existing posts.

## Dimensions and Format

- **Size**: 1200x630 px (Open Graph / social card standard)
- **Format**: PNG preferred (34 of 40 posts use PNG)
- **HiDPI**: Some posts use 2400x1260 (2x) — same aspect ratio, sharper on retina

The 1200x630 ratio (~1.9:1) is the current standard for all post types from 2024 onward.
Earlier posts used inconsistent sizes — don't follow those as examples.

## Visual Style by Post Type

### Release posts

All use the same template:

- Steel blue solid background (~#4d6e8e)
- Centered Quarto logo (circle-crosshair) + wordmark in white
- Large bold version number in white (e.g., "1.9")
- One small thematic emoji-style illustration (unique per release)

The only creative variable is the illustration — background, typography, logo placement,
and layout are identical across 1.4 through 1.9. Release thumbnails that use emoji
illustrations include an OpenMoji attribution at the end of the post.

### Feature and how-to posts

Same steel blue palette as release posts. Content varies:

- **Icon compositions**: White outline icons arranged on the blue background
(e.g., PDF accessibility post uses Quarto icon + PDF + accessibility + shield icons)
- **Diagrams/explainers**: Simple visual showing the concept (e.g., notebook → reports
with arrow for parameterized reports post)
- **Screenshots**: Cropped screenshot of the feature output (older convention, less common now)

### Conference and workshop posts

Use Posit conference brand templates rather than the Quarto steel blue:

- Dark navy or teal backgrounds with 3D isometric block illustrations
- Conference logo placement (posit::conf branding)
- Speaker/instructor names in white text

### Partner/integration posts

Use the partner's own logo on a white or neutral background (e.g., Hugging Face logo,
Confluence logo). No Quarto branding needed — the partner identity is the visual.

## Creating Thumbnails

### HTML/CSS + screenshot approach (recommended)

Create an HTML file sized to 1200x630 with the design, then screenshot it:

1. **Source SVG icons** — good free sources:
- [svgrepo.com](https://www.svgrepo.com) — many Public Domain (PD) icons, no attribution needed
- [icon-icons.com](https://icon-icons.com) — CC BY 4.0 icons, attribution required
2. **Build an HTML file** — set `body` to 1200x630 with the steel blue background,
embed SVG icons inline (recolor fills to white as needed), use flexbox for layout
3. **Screenshot at 1200x630** — use `agent-browser` with viewport set to 1200x630:
```bash
agent-browser set viewport 1200 630
agent-browser open file:///path/to/thumbnail.html
agent-browser screenshot /path/to/thumbnail.png
```
4. **Clean up** — delete the HTML source, keep only the PNG

The Quarto logo SVG is at the repo root: `quarto-icon.svg`.

### Attribution

When using third-party icons, add an attribution line at the very end of the post
(after the last content section). Match the format used in existing posts:

```markdown
The [icon description] in the [listing and social card image](thumbnail.png) for this
post is by [Author](url){.external} via [source](url){.external}.
License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/){.external}
```

Public Domain icons (e.g., from svgrepo.com PD collection) need no attribution.
13 changes: 13 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Note that technically, <prerelease.quarto.org> is also a deploy preview on Netli
- They are automically created and updated when the PR is created and updated by a user with Contributor role.
- For external PR, the preview can be triggered by adding a comment `/deploy-preview` on the PR.
- Any rendering to prerelease also uses a specific profile at render time, set in action using `QUARTO_PROFILE` environment variable.
- Deploy previews use the `pr-preview` profile (`_quarto-pr-preview.yml`) which sets `draft-mode: visible` so that draft blog posts and pages are rendered and visible in PR previews. This is needed because `quarto render` (used in CI) hides drafts by default, unlike `quarto preview` (used locally) which always shows them.

- `update-downloads.yml` - This workflow is triggered by a cron schedule. It retrieves information about latest release and prerelease on `quarto-dev/quarto-cli` repository and updates the download links on the website.
- If there is a new version detected, it will commit the modified files and trigger a deploy of the website calling `publish.yml` workflow with `workflow_call` event trigger.
Expand All @@ -34,12 +35,24 @@ Note that technically, <prerelease.quarto.org> is also a deploy preview on Netli
- This index file is retrieved on deployed website to be updated on Algolia.
- Both `quarto.org` and `prerelease.quarto.org` indexes are updated in the same run - they each use one specific algolia index

- `draft-check.yml` - A lightweight workflow that detects `draft: true` pages among changed `.qmd` files. Runs on all PRs to `main` and `prerelease` with no `paths-ignore`, so it always creates a status check.
- Uses the reusable composite action at `.github/workflows/actions/detect-drafts/`.
- Configured as a required status check on `main` (`check-drafts` job), blocking merge until `draft: true` is removed.
- Admin override is available for intentional draft merges.

- `port-to-prerelease.yml` - This workflow is used to sync changes made to main for quarto.org to prerelease branch for prerelease.quarto.org.
- It is triggered when a PR is merged in to `main`. It can also be triggered manually by adding a comment `/sync-prerelease` on a merged PR.
- This workflow uses [`korthout/backport-action`](https://github.com/korthout/backport-action) to create a PR with the changes merged into `main` branch to be synced to `prerelease` branch.
- It will also write a new `/deploy-preview` comment in the new PR to trigger the preview deployment from `preview.yml`.
- This is possible because it uses a fine-grained PAT token which allows a workflow to trigger another using usual event (GITHUB_TOKEN does not allow that usually). This is configured in repo secrets.

## Reusable Composite Actions

Local composite actions live in `.github/workflows/actions/`:

- `detect-drafts/` - Scans changed `.qmd` files for `draft: true` in YAML frontmatter. Outputs `found` (true/false) and `files` (newline-separated paths). Used by both `preview.yml` (to tag drafts in the PR comment) and `draft-check.yml` (to fail the required check).
- `release-info/` - Retrieves release information from the quarto-cli repository.

## Netlify Configurations

- This repo has a `_redirects` file in the root directory. Otherwise, configuration are made in NETLIFY UI. Quarto website is inside Posit Netlify account.
Expand Down
Loading
Loading