Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
212 changes: 212 additions & 0 deletions content/articles/2026-06-23-how-to-write-for-powershell-org.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
---
title: How to Write for PowerShell.org
description: Two ways to submit an article to PowerShell.org, best practices that get you published faster, and why claiming an author page is worth five minutes of your time.
author: Gilbert Sanchez
authors:
- Gilbert Sanchez
date: 2026-06-23T00:00:00+00:00
categories:
- Tutorials
tags:
- contributing
- community
- writing
fmContentType: article
---

There's a thought that stops a lot of good articles: *who am I to write for
PowerShell.org?*

It's our brains safety net from the (highly unlikely) possibility of getting
denied. Or worse! Accepted! And now we're forever on the hook to be an expert.
But it's not true.

You don't need to be an **MVP**. You don't need a **blog**, a **following**, or
a **clever opinion** about the pipeline. You need one thing you figured out that
the documentation didn't explain well. The `-Filter` quirk that cost you and
afternoon. The script that finally tamed a chore you'd been doing by hand for a
year. If it helped you, it'll help someone else who's about to lose the same
afternoon.

> [!IMPORTANT]
> And here's the part that should lower your blood pressure: **nothing you submit
> goes live unreviewed**.

A maintainer reads every submission, helps shape it, and
edits for clarity and formatting before it publishes. You are not flipping a
switch that broadcasts your rough draft to the world. You're starting a
conversation with people who want you to succeed.

So let's get you published. There are two ways in, so pick the one that matches
how comfortable you are with Git because both land in the same place.

## Path A: The GitHub issue (no Git required)

If "fork the repo" already made you tense up, this path is for you. You'll never
touch a command line.

Open the [guest blog post
form](https://github.com/PowerShellOrg/PowerShellOrgWebsite/issues/new?template=guest-blog-post.yml)
and fill it out. The form does the structuring for you. It asks for exactly what
we need and nothing else:

- **Article title** and **your name** as you'd like it displayed.
- **Submission type**, and this is the part people miss: you can choose *"Pitch
-- I'd like feedback before writing."* You don't have to show up with a
finished draft. Float the idea first, and a maintainer will tell you if it's a
fit and help you shape the angle before you spend the writing time.
- **Description**, one or two sentences. This becomes your SEO blurb and social
card, so it earns its keep.
- **Category** and **tags** (more on choosing these well below).
- **Article content**, where you paste your full Markdown if you have a draft.
Leave it blank if you're pitching.

Submit it, and the rest happens in the issue thread. That's the whole path. No
branches, no merge conflicts, no Git vocabulary.

## Path B: The pull request (for the Git-comfortable)

If you already live in Git, you can submit the article directly and watch it
flow through the same review.

1. Fork the repo and create a branch for your article.
2. Add a Markdown file in `content/articles/` using the date-slug naming
convention:

```
content/articles/YYYY-MM-DD-your-article-slug.md
```

3. Start it with this front matter:

```
---
title: "Your Article Title"
description: "A 1-2 sentence summary used for SEO, social cards, and the article list."
author: Your Name
authors:
- Your Name
date: "YYYY-MM-DDT00:00:00+00:00"
categories:
- Category Name
tags:
- tag1
- tag2
---

Your article in Markdown goes here.
```

4. Open a pull request with a short description, and we'll review it there.

> [!TIP] Let VS Code do the boring part!
> Install the [Front Matter CMS](https://frontmatter.codes/) extension, open the
> repo, and run **"Create content"** in the `content/articles` folder. It
> scaffolds the `YYYY-MM-DD-slug.md` filename and every front-matter field for
> you, and it gives you a form for the title, description, category, and tags
> instead of a wall of YAML you can typo. It turns the single most error-prone
> step into a fill-in-the-blanks. If you only adopt one tool from this article,
> make it this one.

## Best practices that get you published faster

None of these are gates. They're the small things that mean a maintainer spends
their time on your ideas instead of your formatting.

- **Open by telling readers what they'll walk away with.** A two-sentence intro
that promises a payoff beats a warm-up paragraph every time.
- **Write in Markdown, and fence your code with a language hint.** Use `
```powershell ` so your samples get syntax highlighting instead of a gray
slab.
- **Run your code before you paste it.** A snippet that works on the first try
is the difference between a reader trusting you and a reader closing the tab.
- **Keep the title concrete.** "Speed up your console with PSReadLine predictive
IntelliSense" tells me what I'm getting. "PowerShell tips" tells me nothing.

That's the bar. It's lower than the one in your head.

## Categories and tags are how people find you

It's tempting to treat these as paperwork and pick whatever's first in the list.
Don't. They're the difference between an article that's read once and one that
keeps getting found.

Pick the single **category** that fits best. The current set:

> Announcements - Books - DevOps - Events - Graph - In Case You Missed It -
> News - PowerShell Summit - PowerShell for Admins - PowerShell for Developers -
> Scripting Games - Tips and Tricks - Tools - Training - Tutorials

Category is the big bucket. It's how someone browsing "PowerShell for Admins"
stumbles onto your piece months from now. **Tags** are the specific hooks: the
cmdlets, modules, and concepts your article actually touches (`psreadline`,
`regex`, `azure`, `pester`). Three to five honest, specific tags beat a dozen
vague ones. Tag what's really in the article, not every PowerShell word you can
think of, and your post surfaces next to its actual neighbors.

## Claim your author page

Once you're credited on an article, you can give yourself a real author page at
`/authors/<your-name>/`: an avatar, a tagline, a short bio, and links back to
your own site and socials. Every article you write points back to it. It's a
small, durable corner of the PowerShell community that's *yours*, and it builds
with each post.

It's opt-in. Skip it and your byline still works exactly as before. But it takes
about five minutes, so why leave it on the table? You can see an example of mine
at the bottom.

Your profile is a single file at `content/authors/<slug>/_index.md`. The one
rule that trips people up: the `<slug>` has to match your byline exactly
(lowercased, spaces to hyphens), or the page attaches to nothing.

So let the helper script handle it:

{{< terminal lang="powershell" >}}
./tools/new-author.ps1 "Jane Doe"
{{< /terminal >}}

That scaffolds `content/authors/jane-doe/_index.md` with every field commented.
Fill in what you want, delete the rest:

```yaml
---
title: "Jane Doe" # required -- keep this as your byline name
preferred_name: "Jane" # optional -- changes only how your name displays
tagline: "Cloud automation, mostly."
gravatar_hash: "..." # MD5 of your lowercased email -- keeps your email private
github: "https://github.com/janedoe"
website: "https://janedoe.dev"
# twitter / mastodon / linkedin / bluesky also supported
---

Your bio in Markdown goes here.
```

One thoughtful detail worth calling out: you can set an avatar **without**
putting your email address in a public repo. Store the MD5 hash of your
lowercased email as `gravatar_hash`, and Gravatar serves your picture while your
email stays private:

{{< terminal lang="powershell" >}}
$email = "jane@example.com"
[System.BitConverter]::ToString(
[System.Security.Cryptography.MD5]::Create().ComputeHash(
[System.Text.Encoding]::UTF8.GetBytes($email.Trim().ToLowerInvariant())
)
).Replace("-", "").ToLowerInvariant()
{{< /terminal >}}

And if your name ever changes, `./tools/new-author.ps1 "Old Name" -To "New Name"`
rewrites your byline across every article and adds a redirect so your old
profile URL keeps working. Open a PR with the result.

## Your turn

The whole point of PowerShell.org is that it's built by the people who use
PowerShell, and that includes you. You don't have to be sure it's good enough.
That's literally what the review is for. Pitch the idea, paste the draft, or
send the PR, and you won't be doing it alone. There's a community on the other
side of that submit button that wants to help you get it across the line.

[Start here.](https://github.com/PowerShellOrg/PowerShellOrgWebsite/issues/new?template=guest-blog-post.yml)
56 changes: 56 additions & 0 deletions docs/adr/0004-github-alert-admonitions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Admonitions are GitHub-style alerts, capped at GitHub's five canonical types

Callouts on the site are authored with GitHub's alert syntax — `> [!NOTE]`,
`> [!TIP]`, `> [!IMPORTANT]`, `> [!WARNING]`, `> [!CAUTION]` — and rendered by a
Goldmark blockquote render hook (`themes/powershell-community/layouts/_markup/render-blockquote.html`)
into styled callouts, with the look defined in
`themes/powershell-community/static/css/alerts.css`.

The load-bearing constraint is the **contribution model**: an [[Author]] submits
an article either as plain Markdown pasted into a GitHub issue (the no-Git path)
or as a Markdown file in a pull request. In both, the Markdown is read on GitHub
*before* it is ever read on the site. The admonition syntax therefore has to
render identically in both places. GitHub natively renders exactly five alert
types and nothing else; any sixth type degrades on GitHub to a plain blockquote
with a literal `[!SUCCESS]` first line. So the syntax choice and the type set are
the same decision: pick the form GitHub already understands, and support only the
types GitHub renders.

## Considered options

- **Hugo shortcodes** (`{{< note >}}…{{< /note >}}`, matching the existing
`terminal` shortcode) — rejected. It is the established convention here, but a
shortcode renders as literal `{{< note >}}` text in a GitHub issue or PR diff,
which is exactly where submissions are first read. It also asks plain-Markdown
authors to learn a Hugo-specific construct.
- **GitHub-style alerts, full Blowfish vocabulary** (~14 types + aliases like
`tldr`, `hint`, `done`) — rejected. The richer palette renders on the site but
silently breaks the GitHub round-trip for every type beyond the canonical five.
An author who learns `[!SUCCESS]` from our docs and sees a broken blockquote in
their own issue is a worse outcome than not having `success` at all.
- **Front Matter CMS snippets** — not an alternative to the above so much as a
layer on top; a snippet still has to emit *some* markup. Deferred. If added
later it should emit the `> [!NOTE]` syntax, not a shortcode.
- **GitHub-style alerts, canonical five only** — chosen. The same Markdown reads
correctly in the issue form, the PR diff, and the published page.

## Consequences

- **The vocabulary is capped at five** (`note`, `tip`, `important`, `warning`,
`caution`). This is a deliberate ceiling, not an oversight — a future
maintainer who wants `[!SUCCESS]` or `[!QUOTE]` is reintroducing the GitHub
degradation this record exists to prevent. The render hook keeps a type→icon
map so adding a type is mechanically a one-line change; the reason *not* to is
recorded here.
- **The `terminal` shortcode convention was deliberately not extended.** Callouts
and code-terminals now use two different mechanisms (render hook vs. shortcode).
That inconsistency is intentional: `terminal` is site-only chrome with no GitHub
round-trip to honor, whereas callouts must survive it.
- **The render hook owns every blockquote on the site.** It must branch on
`.Type == "alert"` and fall through to a plain `<blockquote>` for ordinary
quotes, or all non-alert blockquotes break. Plain blockquotes-as-containers
(e.g. the category list in the contributor guide) stay plain by design.
- Styling lives in its own theme-owned stylesheet (`static/css/alerts.css`,
linked from `baseof.html`) rather than the inline `<style>` block, but is still
shipped inside the theme so the feature — hook plus styles — stays
self-contained.
57 changes: 50 additions & 7 deletions frontmatter.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,29 +260,72 @@
{
"title": "Articles",
"path": "[[workspace]]/content/articles",
"contentTypes": ["article"],
"contentTypes": [
"article"
],
"filePrefix": "{{year}}-{{month}}-{{day}}"
},
{
"title": "Podcast",
"path": "[[workspace]]/content/podcast",
"contentTypes": ["podcast"],
"contentTypes": [
"podcast"
],
"filePrefix": "{{year}}-{{month}}-{{day}}"
},
{
"title": "Events",
"path": "[[workspace]]/content/calendar",
"contentTypes": ["event"]
"contentTypes": [
"event"
]
},
{
"title": "Authors",
"path": "[[workspace]]/content/authors",
"contentTypes": ["author"]
"contentTypes": [
"author"
]
},
{
"title": "Pages",
"path": "[[workspace]]/content",
"contentTypes": ["page"]
"contentTypes": [
"page"
]
}
],
"frontMatter.content.snippets": {
"Terminal": {
"description": "Show your terminal commands in a nice terminal window.",
"body": [
"{{< terminal title=\"[[filename]]\" lang=\"[[language]]\" >}}",
"[[selection]]",
"{{< /terminal >}}"
],
"fields": [
{
"name": "filename",
"title": "filename",
"type": "string",
"single": true,
"default": ""
},
{
"name": "language",
"title": "language",
"type": "string",
"single": true,
"default": ""
},
{
"name": "selection",
"title": "selection",
"type": "string",
"single": true,
"default": ""
}
]
}
]
}
}
}
7 changes: 7 additions & 0 deletions themes/powershell-community/layouts/_default/baseof.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism-tomorrow.min.css" rel="stylesheet">
<link href="/css/alerts.css" rel="stylesheet">
{{ with .Site.Params.algolia }}<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@algolia/algoliasearch-netlify-frontend@1/dist/algoliasearchNetlify.css" />{{ end }}

<!-- Custom CSS -->
Expand Down Expand Up @@ -87,6 +88,12 @@
terminal chrome renders flush; Prism handles token colors. */
.ps-terminal pre { background: transparent; color: #ccc; margin: 0; border-radius: 0; padding: 1.25rem; overflow-x: auto; }

/* Hugo/Chroma highlighted blocks (.highlight) carry their own light
background; trim the heavier .prose pre padding/margins so they don't
read as a wall of whitespace. */
.prose .highlight { margin: 1.25rem 0; border-radius: 0.5rem; overflow: hidden; }
.prose .highlight pre { margin: 0; padding: 0.85rem 1rem; border-radius: 0; line-height: 1.5; }

.hero-pattern {
background: linear-gradient(135deg, #0078D4 0%, #00BCF2 100%);
position: relative;
Expand Down
Loading
Loading