Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:48:07 +08:00
commit 36855a0177
8 changed files with 1698 additions and 0 deletions

View File

@@ -0,0 +1,409 @@
# Content Guidelines for Release Posts
General best practices for release post content, regardless of blog platform. These guidelines are based primarily on Tidyverse blog conventions, which provide a principled approach to announcing package updates.
## Table of Contents
1. [Post Structure](#post-structure)
2. [Opening Style](#opening-style)
3. [Section Organization](#section-organization)
4. [Tone and Voice](#tone-and-voice)
5. [Code Examples](#code-examples)
6. [Acknowledgments](#acknowledgments)
## Post Structure
Standard structure for release posts:
```markdown
[Frontmatter - see formatting guides]
# Package Version
[Opening paragraph with package description]
[Installation instructions]
[Overview paragraph about the release]
## Major Feature 1
[Content...]
## Major Feature 2
[Content...]
## Minor improvements
[Bulleted list...]
## Acknowledgements
[Contributor list]
```
## Opening Style
### Opening Paragraph Pattern
Start with an announcement that establishes the package's core purpose:
> "We're [pleased/chuffed/stoked/thrilled/delighted/excited/...] to announce the release of [package] [version]. [Package] [core purpose description in one sentence]."
**Examples:**
- "We're chuffed to announce the release of testthat 3.3.0. testthat makes it easy to turn your existing informal tests into formal, automated tests"
- "We're pleased to announce the release of pkgdown 2.2.0. pkgdown is designed to make it quick and easy to build a beautiful and accessible website"
- "We're excited to announce the new Shiny extension for VS Code!"
**Vocabulary variations:**
- Choose an upbeat and random adjective with connotations of excitement.
- Be interesting and bold, but above all positive and fun.
### Installation Instructions
Follow the opening with installation instructions as a code block:
**R packages:**
```r
install.packages("packagename")
```
For multiple packages:
```r
install.packages(c("shiny", "bslib"))
```
**Python packages:**
```bash
pip install packagename
```
With extras:
```bash
pip install "packagename[extra]"
```
### Overview Paragraph
Brief overview of the release following installation:
- Link to full release notes when available
- Highlight focus areas: "This release focuses on [key themes]"
- Set expectations: "This is a [major/minor] release with [X main features]"
- Acknowledge breaking changes: "This release includes a number of new features... Some of these changes may require you to update your existing code"
## Section Organization
### Standard Section Hierarchy
Organize content in this order:
1. **Migration guide** (if breaking changes)
- Put this first when there are breaking changes
- Use clear before/after examples
- Explain the rationale for changes
2. **Lifecycle changes** (if applicable but not breaking)
- Soft deprecations
- Deprecations
- Defunct (removed) functions
- Non-breaking behavioral changes
3. **Major new features** (primary content)
- One section per major feature or theme
- Use descriptive headings
- Lead with what the feature does and why it matters
- Include examples
4. **Minor improvements** or **Other new features**
- Bulleted list of smaller enhancements
- Brief explanations
- Can group related items
5. **Acknowledgements** (always final)
- Thank contributors
- List all contributors with GitHub links
### Section Heading Style
- Use sentence case: "Lifecycle changes" not "Lifecycle Changes"
- Be descriptive: "Easier `in_parallel()`" not just "New feature"
- Include function names in backticks when relevant
- Make headings scannable and informative
### Content Organization Within Sections
**For migration guides:**
```markdown
## Migrating to vX.Y.Z
### Setting the app theme
Prior to vX.Y.Z, you could...
[Before example]
**With packagename vX.Y.Z,** you now need to...
[After example]
Read about [the feature](#feature) below to learn why this change was needed.
```
**For lifecycle changes:**
```markdown
## Lifecycle changes
* **Fully removed** after 5+ years of deprecation:
* `function1()` - use `replacement1()` instead
* `function2()` - use `replacement2()` instead
* **Newly deprecated** (soft-deprecation):
* `function3()` is superseded by `function4()`
* `function5()` is no longer recommended
* **Breaking changes**:
* Brief description of the change and impact
```
**For feature sections:**
- Lead with a clear description of what the feature does
- Provide context on why it's useful or what problem it solves
- Include code examples showing usage
- Explain configuration options if relevant
- Link to relevant documentation
### Deep Dives on Features
Don't just list features from NEWS—research them and provide comprehensive explanations:
**Transform NEWS bullets into complete sections:**
1. **Research the feature**:
- Read function documentation
- Check related PRs and issues for context
- Understand the motivation and use cases
2. **Add complete code examples**:
- Show realistic usage, not just function signatures
- Include comments explaining what's happening
- Demonstrate the full workflow, not just isolated calls
3. **Explain the context**:
- Why was this feature added?
- What problem does it solve?
- What are the practical use cases?
- How does it fit into typical workflows?
4. **Show use cases and applications**:
- Provide examples of when users would need this
- Explain different configuration options
- Show how features work together
**Example transformation:**
**NEWS bullet approach:**
```markdown
## Bookmarking
- Added `chat_restore()` for bookmarking support (#82)
```
**Deep dive approach:**
```markdown
### Bookmarking support
shinychat now supports Shiny's bookmarking feature for saving and restoring chat sessions.
The new `chat_restore()` function saves the chat client state and restores the message history:
[Complete code example showing actual usage]
By default, `chat_restore()` automatically updates the bookmark when users submit messages
and when the LLM completes its response. This means users can refresh the page or share a
URL and pick up right where they left off.
For apps with large chat histories, you can use `enableBookmarking = "server"` to store
state server-side without URL size limitations.
```
**Research sources:**
- Use `r-btw` tools to read R function documentation
- Read GitHub PRs/issues linked in NEWS
- Check package vignettes and articles
- Look at example apps in the repository
**For improvements:**
```markdown
## Minor improvements
* Brief description of improvement with function name in backticks
* Another improvement with relevant technical details
* Performance gains quantified when possible (e.g., "2x faster")
```
## Tone and Voice
### Writing Style
- **Conversational but professional**: "We're excited to announce" rather than "It is announced"
- **Inclusive**: Use "we" and "you" rather than passive voice
- **Enthusiastic but authentic**: Avoid over-the-top marketing language
- **Technical precision**: Use exact function names, parameter names, and technical terms
- **Focus on benefits**: Explain the "why" not just the "what"
### Avoiding Marketing Speak
Release posts should be friendly and instructional, not advertising. Watch for and remove:
**Superlatives and promotional adjectives:**
- ❌ "powerful set of features", "rich, interactive displays", "beautiful interface"
- ✅ "set of features", "interactive displays", "interface"
**Exaggerated benefit claims:**
- ❌ "handles all the complexity of persisting state"
- ✅ "saves the chat client state and restores the message history"
**Generic enthusiasm:**
- ❌ "opens up exciting possibilities for building transparent, user-friendly AI applications"
- ✅ Direct description of what the feature does
**Over-hyped capability statements:**
- ❌ "give you fine-grained control over every aspect"
- ✅ "lets you control the chat interface"
**Tone review checklist:**
- [ ] Remove words like "powerful", "robust", "seamless", "elegant", "revolutionary"
- [ ] Replace "makes it easy to" with direct descriptions of functionality
- [ ] Prefer descriptive statements focusing on what the function does
- [ ] Avoid overuse of phrases that sound like product marketing ("best-in-class", "production-ready")
### Common Phrases
- "We're [emotion] to announce..."
- "A big thank you to all the folks who helped make this release happen"
- "You can see a full list of changes in the release notes"
- "This makes it easier to..."
- "We've improved..."
- "This release focuses on..."
- "Prior to vX.Y.Z, you had to..."
- "With this release, you can now..."
### Technical Voice
- Use backticks for all code elements: `` `function()` ``, `` `argument` ``, `` `"value"` ``
- Link to relevant documentation when helpful
- Be specific about behavior: "returns `NULL`" not "returns nothing"
- Explain technical decisions when they impact users
- Acknowledge tradeoffs when present
## Code Examples
### Explain Concepts Before Code
For features that introduce new or unfamiliar concepts, explain the concept first before showing code:
**Pattern: Concept → How it works → Why it matters → Code**
1. **What is the feature/concept?** (1-2 sentences)
2. **How does it work?** (Especially non-obvious aspects)
3. **Why does it matter?** (Practical implications)
4. **Show the code** (With examples)
**Key principles:**
- Assume readers may not be familiar with the concept
- Explain non-obvious aspects clearly
- Connect concepts to practical outcomes
- Use plain language before technical language
### Inline Code
- Function names: `` `map_chr()` ``
- Arguments: `` `na.rm = TRUE` ``
- Values: `` `NULL` ``, `` `TRUE` ``, `` `"string"` ``
- File paths: `` `app.py` ``
### Code Block Principles
- Use realistic but simple examples
- Include expected output when relevant
- Show error messages for examples of better error handling
- Format multi-line function calls with proper indentation
- Add comments for clarity when needed: `# This does X`
- Keep examples focused on the feature being demonstrated
### Before/After Examples
When showing improvements, use clear before/after structure:
```markdown
Previously, you had to write:
[Code block showing old approach]
Now you can write:
[Code block showing new approach]
```
Or for migration guides:
```markdown
**Before:**
[Old code]
**After:**
[New code]
```
## Acknowledgments
### When to Include
- **Always include** for package releases with external contributors
- Optional for internal tooling announcements
- Include at the end as the final section
### Section Format
```markdown
## Acknowledgements
A big thank you to all [the folks/everyone] who [helped make this release happen/contributed to this release]:
[@username1](https://github.com/username1), [@username2](https://github.com/username2), ...
```
### Fetching Contributors
Use `usethis::use_tidy_thanks()` in R:
```r
# From last release to current
usethis::use_tidy_thanks("owner/repo")
# From specific tag/SHA
usethis::use_tidy_thanks("owner/repo", from = "v1.0.0")
```
This generates properly formatted markdown for the contributor list that can be included verbatim.
## Transforming NEWS to Blog Content
When converting NEWS.md entries to blog post format:
1. **Expand context**: Technical bullets become paragraphs explaining why changes matter
2. **Add examples**: Include code demonstrating new features or changes
3. **Group thematically**: Combine scattered NEWS items into coherent sections
4. **Use conversational tone**: Transform terse bullets into readable prose
5. **Link proactively**: Add links to relevant documentation and resources
6. **Focus on user impact**: Explain how changes affect typical usage
7. **Highlight breaking changes**: Make migration paths clear and prominent
## Release Notes Link
Always include a link to full release notes:
```markdown
You can see a full list of changes in the [release notes](url).
```
This allows readers to find exhaustive details while keeping the blog post focused and readable.

View File

@@ -0,0 +1,606 @@
# Shiny Blog Formatting Conventions
Shiny-specific formatting requirements for blog posts on shiny.posit.co. These conventions are for the Quarto-based Shiny blog.
## Frontmatter Format
Shiny blog posts use Quarto YAML frontmatter with this structure:
```yaml
---
title: Package Name Version
description: &desc |
Brief description of the package and release.
author: "Author Name"
date: "YYYY-MM-DD"
image: &img feature.png
open-graph:
image: *img
description: *desc
twitter-card:
image: *img
description: *desc
---
```
### Required Fields
- **`title`**: Display title (no specific format required, flexible)
- Can be: `"packagename version"`, `"Package Name x.y.z"`, or descriptive
- Examples:
- `"shinyswatch 0.7.0"`
- `"Reintroducing the Shiny Extension for VS Code"`
- `"Branded theming for Shiny for Python apps"`
- **`description`**: Brief summary with YAML anchor for reuse
- Use `&desc` anchor and `|` for multi-line
- Reuse in social media cards with `*desc`
- **`author`**: Author name in quotes (can be single string or array)
- Single: `"Garrick Aden-Buie"`
- Multiple: `["Author One", "Author Two"]`
- **`date`**: ISO format in quotes `"YYYY-MM-DD"`
- **`image`**: Path to featured image (relative to post directory)
### Social Media Cards
Use YAML anchors to avoid repeating the description:
```yaml
description: &desc |
Your description here that will be reused.
open-graph:
description: *desc
twitter-card:
description: *desc
```
The `&desc` creates an anchor, and `*desc` references it.
### Optional But Common Fields
- **`filters`**: Quarto filters to use
- `shinylive` - For embedding Shinylive apps
- `line-highlight` - For highlighting specific lines
- **`engine`**: Quarto engine (`knitr`, `markdown`, `jupyter`)
- **`freeze`**: Set to `true` if the post includes *any* computational output
- **`format`**: HTML format options
- `code-link: true` - Enable code linking
- `anchor-sections: true` - Enable section anchors
- `reference-location: document` or `section` - Where to place footnotes
- **`code-annotations`**: Set to `hover` for hover annotations
- **`editor`**: Editor options like `render-on-save: true`
### Image Options
Additional image control fields:
```yaml
image: feature.svg
imagealt: "Alternative text for image"
image-header-disable: true # Don't show image in header
```
### What's NOT Included
Unlike Tidyverse posts, Shiny posts do **not** include:
- `output: hugodown::hugo_document`
- `slug` (filename determines URL)
- `photo.url` and `photo.author` (different image structure)
- Consistent `categories` field (optional, varies by post)
## Title Format
The main title can be flexible:
```markdown
# packagename version
```
Or more descriptive:
```markdown
# Reintroducing the Shiny Extension for VS Code
```
There's less rigid formatting for titles in Shiny posts.
## Lead Paragraphs
Shiny posts may use a lead paragraph div for emphasis:
```markdown
::: lead
**We're excited to announce the new Shiny extension for VS Code!**
:::
```
This creates a larger, emphasized opening paragraph.
## Code Formatting
### For Python Packages
Use bash code blocks for installation:
````markdown
```bash
pip install packagename
```
````
With extras:
````markdown
```bash
pip install "packagename[extra]"
```
````
### For R Packages
Use R code blocks:
````markdown
```r
install.packages("packagename")
```
````
For multiple packages:
````markdown
```r
install.packages(c("shiny", "bslib"))
```
````
### Code Block Attributes
Shiny posts use Quarto code block attributes:
````markdown
```{.python filename="app.py"}
from shiny import App
# code here
```
````
````markdown
```{r install-bslib}
#| eval: false
install.packages("bslib")
```
````
### Line Highlighting
Use `# <<` comments to highlight lines (requires `line-highlight` filter):
````markdown
```{.python filename="app.py"}
from shiny.express import ui
import shinyswatch
ui.page_opts(theme=shinyswatch.theme.darkly) # <<
# rest of code...
```
````
### Code Annotations
With `code-annotations: hover` in frontmatter, you can add annotations:
```python
result = some_function() # <1>
```
```markdown
1. This annotation explains the line above
```
## Tabsets for Multiple Variants
Shiny posts frequently use tabsets to show Express vs Core mode or Python vs R:
````markdown
::: {.panel-tabset .shiny-mode-tabset group="shiny-app-mode"}
#### Express
```{.python filename="app.py"}
from shiny.express import ui
# Express code
```
#### Core
```{.python filename="app.py"}
from shiny import ui
# Core code
```
:::
````
### Nested Tabsets
You can nest tabsets for Before/After within Express/Core:
````markdown
::: {.panel-tabset group="shiny-app-mode"}
#### Express
::: {.panel-tabset}
##### Before
```python
# old code
```
##### After
```python
# new code
```
:::
#### Core
::: {.panel-tabset}
##### Before
```python
# old code
```
##### After
```python
# new code
```
:::
:::
````
## Callouts
Quarto callouts for notes, tips, warnings:
```markdown
::: {.callout-tip title="Writing brand.yml with the help of an LLM"}
We know that writing YAML isn't everyone's cup of tea!
...
:::
```
Types: `note`, `tip`, `warning`, `caution`, `important`
## Embedded Media
### Videos
````markdown
::: column-page
```{=html}
<video controls>
<source src="videos/demo.webm" type="video/webm">
<source src="videos/demo.mp4" type="video/mp4">
</video>
```
:::
````
### Images with Attributes
```markdown
![The task button showing a busy indication](task-button.gif){.shadow}
```
Or with more attributes:
```markdown
![Alt text](image.png){fig-alt="Detailed alt text" fig-align="center" width="100%"}
```
## Shinylive Examples
With the `shinylive` filter, you can embed live examples:
````markdown
```{shinylive-python}
#| standalone: true
#| components: [editor, viewer]
## file: app.py
from shiny.express import input, render, ui
# App code here
```
````
Or link to examples:
```markdown
We've added a [complete branded theming example](https://shinylive.io/py/examples/#branded-theming) to shinylive.io.
```
## Acknowledgements Section
Less consistent in Shiny posts. When present, variations include:
### Variation 1: Simple Thanks
```markdown
## Thanks!
Thank you for trying out the Shiny extension for VS Code!
If you find it helpful, please rate the extension on [the marketplace][Shiny extension].
```
### Variation 2: Multiple Package Releases
```markdown
## Release notes
**Big shout out to everyone involved!** 💙
We'd want to extend a huge thank you to everyone who contributed pull requests, bug reports and feature requests.
#### bslib [v0.7.0](https://rstudio.github.io/bslib/news/index.html#bslib-070)
[List of contributors]
#### shiny [v1.8.1](https://shiny.posit.co/r/reference/shiny/1.8.1/upgrade.html)
[List of contributors]
```
### Variation 3: Omitted
Some Shiny posts don't include acknowledgements, especially for announcements of tools rather than package releases.
### When Acknowledgements Are Included
Use similar format to Tidyverse:
```markdown
## Acknowledgements
We'd like to thank everyone who contributed to this release:
[@user1](https://github.com/user1), [@user2](https://github.com/user2), and [@user3](https://github.com/user3).
```
Or just reference the generator in code:
```markdown
```{r}
#| echo: false
#| eval: false
usethis::use_tidy_thanks("rstudio/bslib", from = "v0.6.1")
```
```
## CSS Customization
Shiny posts may include custom CSS in HTML blocks:
````markdown
```{=html}
<style>
img { border-radius: 8px; }
video {
max-width: 100%;
margin-bottom: 1rem;
}
</style>
```
````
## Column Layouts
Use Quarto's column classes for wider content:
```markdown
::: column-page
[Wide content here]
:::
::: column-body-outset
[Slightly wider content]
:::
```
## Footnotes
Use standard markdown footnotes:
```markdown
Text with a footnote[^footnote-key].
[^footnote-key]: This is the footnote text.
```
With `reference-location: document` or `section` in frontmatter to control placement.
## Links and References
Define link references at the top or bottom of the file:
```markdown
[brand.yml]: https://posit-dev.github.io/brand-yml
[quarto]: https://quarto.org
[shiny]: https://shiny.posit.co
```
Then use them inline:
```markdown
We're excited about [brand.yml] support!
```
## Multi-Language Support (R and Python)
The Shiny blog often covers packages released for both R and Python (e.g., shiny, shinychat). Unlike Tidyverse posts which are language-specific, Shiny posts should show examples in both languages using Quarto tabsets.
### Use Tabsets Consistently
Every code example should include both R and Python variants using Quarto tabsets:
```markdown
::: {.panel-tabset group="language"}
## R
```r
# R code here
```
## Python
```python
# Python code here
```
:::
```
**Guidelines:**
- Use `group="language"` to sync all language tabsets on the page
- Provide equivalent functionality in both languages
- Don't show R-only or Python-only examples unless the feature is language-specific
- Keep examples parallel—if the R example shows 5 lines, the Python example should be similar
### Installation Instructions
Always show installation for both languages at the start of the post:
```markdown
::: {.panel-tabset group="language"}
## R
```r
install.packages("packagename")
```
## Python
```bash
pip install packagename
```
:::
```
### Language-Specific Features
When features differ between languages, be explicit about the differences:
```markdown
::: {.panel-tabset group="language"}
## R
In R, use `tool_annotations()` to customize display:
```r
tool_annotations(title = "My Tool", icon = bsicons::bs_icon("star"))
```
## Python
In Python, use the `._display` attribute:
```python
my_tool._display = {"title": "My Tool", "icon": "star"}
```
:::
```
### Version Information
Package versions often differ between R and Python. Be explicit:
- "Available in shinychat for R (v0.3.0) and shinychat for Python (v0.2.0 or later)"
- Link to both language-specific documentation
- Include separate release notes links:
```markdown
You can see the full list of changes in the [R release notes](url) and [Python release notes](url).
```
### When to Use Multi-Language Tabsets
- **Always use** for packages that have both R and Python versions (shiny, shinychat, querychat, etc.)
- **Don't use** for Python-only packages (shinyswatch) or R-only packages
- **Don't use** for Tidyverse blog posts (which are built with hugodown/Hugo, not Quarto)
## Example Complete Frontmatter
### Python Package
```yaml
---
title: shinyswatch 0.7.0
description: &desc Customizable shinyswatch themes and an improved theme picker round out shinyswatch v0.7.0.
author: "Garrick Aden-Buie"
date: "2024-07-19"
image: feature.jpg
open-graph:
image: feature.jpg
description: *desc
twitter-card:
image: feature.png
description: *desc
filters:
- line-highlight
---
```
### R Package with Full Options
```yaml
---
title: "Shiny for R updates: Extended tasks, JavaScript errors, and many bslib improvements"
description: &desc |
An overview of recent Shiny for R updates, including extended tasks, JavaScript errors, and many bslib improvements.
author:
- Carson Sievert
date: "2024-03-27"
image: feature.png
open-graph:
image: feature.png
description: *desc
twitter-card:
image: feature.png
description: *desc
editor:
render-on-save: true
engine: knitr
filters:
- shinylive
freeze: true
format:
html:
code-link: true
anchor-sections: true
reference-location: section
code-annotations: hover
---
```
## Examples of Well-Formatted Posts
Reference these posts for formatting examples:
- [shinyswatch 0.7.0](https://shiny.posit.co/blog/posts/shinyswatch-0.7.0/)
- [Shiny Extension for VS Code 1.0.0](https://shiny.posit.co/blog/posts/shiny-vscode-1.0.0/)
- [Shiny for R 1.8.1](https://shiny.posit.co/blog/posts/shiny-r-1.8.1/)
- [Branded theming for Shiny for Python](https://shiny.posit.co/blog/posts/shiny-python-1.2-brand-yml/)

View File

@@ -0,0 +1,275 @@
# Tidyverse Blog Formatting Conventions
Tidyverse-specific formatting requirements for blog posts on tidyverse.org. These conventions are for the hugodown-based Tidyverse blog.
## Workflow for tidyverse.org Blog
When creating a blog post for the official `tidyverse/tidyverse.org` repository, follow these steps:
1. **Install hugodown** (if not already installed):
```r
pak::pkg_install("r-lib/hugodown")
```
2. **Create a new post**:
```r
hugodown::use_tidy_post("short-name")
```
This creates `content/blog/short-name/` containing an `index.Rmd` file.
Common patterns for `"short-name"`:
- Package release: `lifecycle-1-0-0`, `parsnip-0-1-2`
- Package release with specific topic: `dplyr-1-0-0-rowwise`, `parsnip-adjacent`, `dplyr-1-0-4-if-any`
- Topic only: `self-cleaning-test-fixtures`, `taking-control-of-plot-scaling`
3. **Write and knit**:
- Edit the generated `index.Rmd` file
- Knit `index.Rmd` to generate `index.md`
- Note: `.Rmd` files are only rendered when you explicitly knit them
4. **Preview the site**:
```r
hugodown::hugo_start()
```
This runs once per session and continues in the background to turn `.md` into `.html`.
5. **Check for outdated files** (if concerned):
```r
hugodown::site_outdated()
```
Lists all `.Rmd`s that need to be re-rendered.
6. **Add a photo**:
Every blog post must be accompanied by a photo. If you don't have one in mind, try:
- <https://unsplash.com>
- <https://pexels.com>
- Jenny Bryan's [free photo](https://github.com/jennybc/free-photos) link collection
7. **Submit PR**:
- Every PR gets an automatic live preview via Netlify
- Once merged, the preview becomes the live site
### Important Notes
- The site uses **hugodown** (not blogdown), which separates building into two steps:
- hugodown generates `.md` from `.Rmd`
- hugo generates `.html` from `.md`
- Use `.Rmd` files (not `.Rmarkdown`)
- Output should be `output: hugodown::hugo_document`
- If updating an old post to use hugodown:
- Rename from `.Rmarkdown` to `.Rmd`
- Delete the `.markdown` file
- Set `output: hugodown::hugo_document` in YAML metadata
### For Additional Context
If you need more details about the workflow or encounter issues, consult the `README.md` in the `tidyverse/tidyverse.org` repository.
## Frontmatter Format
Tidyverse blog posts use YAML frontmatter with this structure:
```yaml
---
output: hugodown::hugo_document
slug: package-name-version
title: package-name version
date: YYYY-MM-DD
author: Author Name
description: >
Brief description of the package and release
photo:
url: https://unsplash.com/photos/photo-id
author: Photographer Name
categories: [package-name]
tags: [package-name, category]
---
```
### Required Fields
- **`output`**: Always `hugodown::hugo_document`
- **`slug`**: URL slug using hyphens
- Format: `packagename-x-y-z` (e.g., `ellmer-0-4-0`)
- Replace dots with hyphens in version numbers
- **`title`**: Display title with spaces
- Format: `packagename x.y.z` (e.g., `ellmer 0.4.0`)
- **`date`**: ISO format `YYYY-MM-DD`
- **`author`**: Full name of primary author
- **`description`**: Brief summary (can use `>` for multi-line)
- **`photo`**: Featured image with attribution
- `url`: Full URL to image (often Unsplash)
- `author`: Photographer name for attribution
- **`categories`**: Array with package name
- **`tags`**: Array with package name and related tags
### Slug vs Title Convention
The slug is used in URLs and must be URL-safe:
- Slug: `purrr-1-2-0` (hyphens, no dots)
- Title: `purrr 1.2.0` (space, dots in version)
### Image Attribution
Featured images are typically from Unsplash with proper photographer attribution:
```yaml
photo:
url: https://unsplash.com/photos/abc123
author: John Doe
```
## Title Format
The main title uses a simple format with space between package name and version:
```markdown
# packagename 1.2.0
```
No "released" or "version" prefix. Just the package name and version number.
## Code Formatting
### Language Identifiers
Use triple backticks with `r` language identifier for R code:
````markdown
```r
library(packagename)
result <- function_name(
arg1 = "value",
arg2 = TRUE
)
```
````
For installation:
````markdown
```r
install.packages("packagename")
```
````
### Inline Code Elements
- Function names: `` `function()` ``
- Packages: `` `{packagename}` `` when emphasizing it's a package
- Arguments: `` `arg = value` ``
- Values: `` `NULL` ``, `` `TRUE` ``, `` `"string"` ``
### Function Links
When linking to function documentation, use markdown links:
```markdown
[`function_name()`](https://url-to-docs)
```
## Section Structure
### Lifecycle Section Format
When including lifecycle changes, use this structure:
```markdown
## Lifecycle changes
* **Fully removed** after 5+ years of deprecation:
* `function1()` - use `replacement1()` instead
* `function2()` - use `replacement2()` instead
* **Newly deprecated** (soft-deprecation):
* `function3()` is superseded by `function4()`
* `function5()` is no longer recommended
* **Breaking changes**:
* Description of what changed and why
```
Use bold for the lifecycle stage labels.
### Feature Sections
Use sentence case in headings:
```markdown
## Easier `in_parallel()`
[Description of the feature...]
```
Include function names in backticks when they're part of the heading.
## Acknowledgements Section
Always include as the final section:
```markdown
## Acknowledgements
A big thank you to all the folks who helped make this release happen:
[@username1](https://github.com/username1), [@username2](https://github.com/username2), [@username3](https://github.com/username3), and [@username4](https://github.com/username4).
```
### Formatting Rules
- Single paragraph format (not bulleted list)
- GitHub handles as markdown links
- Alphabetical order
- Comma-separated with "and" before the last name
- Period at the end
### Generating the List
Use `usethis::use_tidy_thanks()`:
```r
# Fetch contributors since last release
usethis::use_tidy_thanks("tidyverse/packagename")
# Or from specific tag
usethis::use_tidy_thanks("tidyverse/packagename", from = "v1.0.0")
```
This function outputs properly formatted markdown that can be copied directly into the blog post.
## Release Notes Link
Include a link to the full release notes (typically on pkgdown site):
```markdown
You can see a full list of changes in the [release notes](https://packagename.tidyverse.org/news/).
```
## Example Complete Frontmatter
```yaml
---
output: hugodown::hugo_document
slug: purrr-1-2-0
title: purrr 1.2.0
date: 2025-11-04
author: Hadley Wickham
description: >
purrr 1.2.0 includes deprecations and minor enhancements to
functional programming tools in R.
photo:
url: https://unsplash.com/photos/xyz789
author: Jane Photographer
categories: [purrr]
tags: [purrr, tidyverse]
---
```
## Examples of Well-Formatted Posts
Reference these posts for formatting examples:
- [pkgdown 2.2.0](https://www.tidyverse.org/blog/2025/11/pkgdown-2-2-0/)
- [testthat 3.3.0](https://www.tidyverse.org/blog/2025/11/testthat-3-3-0/)
- [purrr 1.2.0](https://www.tidyverse.org/blog/2025/11/purrr-1-2-0/)
- [ellmer 0.4.0](https://www.tidyverse.org/blog/2025/11/ellmer-0-4-0/)