Agent skill

Blog Post

by nteract in nteract/semiotic

Author a new entry for the Semiotic blog. An agent skill from nteract/semiotic.

Apache-2.0Auto-check passedWriting & Content

Install Blog Post

skills CLI
$ npx skills add nteract/semiotic --skill blog-post -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install nteract/semiotic blog-post --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/nteract/semiotic.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/blog-post .claude/skills/blog-post && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
blog-post
GitHub stars
2.7k
Token cost
~3.4k tokens
SKILL.md length
1,628 words
Files
4
Skills in repo
4
Repo updated
First seen
Licence
Apache-2.0

At a glance

Author a new entry for the Semiotic blog. An agent skill from nteract/semiotic.

  • Works in 3 steps: Chart explainer — single chart, "what /… → Release summary — what's new in a… → Narrative / case study — comparative…
  • The user asks for a blog post
  • SKILL.md covers Before writing anything, File structure, Required fields and The skeleton — applies to all…, plus 5 more sections
  • Runs JavaScript scripts from its folder; calls npm

What it does

Blog Post is an agent skill from nteract/semiotic. Author a new entry for the Semiotic blog. Use this skill whenever the user asks for a blog post, release summary, chart explainer, or case study to publish at /blog/SLUG.

Its SKILL.md is about 3.4k tokens, which your agent loads only when the skill is triggered. The skill folder holds 4 other files (for example `templates/chart-explainer.js`, `templates/narrative.js` and `templates/release-summary.js`).

It sits in Writing & Content, covering Blog and article writing and Changelog and release notes. It works with React. The repository describes itself as: React data visualization library for streaming, networks, and AI-assisted development. The licence is Apache-2.0.

When your agent uses it

  • The user asks for a blog post
  • Release summary
  • Chart explainer
  • Case study to publish at /blog/SLUG

Example prompts

  • “/blog-post”

Requirements

  • Node.js

Workflow steps

3 steps, taken from the first numbered list in SKILL.md.

  1. Chart explainer — single chart, "what / why / when / wiring".
  2. Release summary — what's new in a version, ordered by impact.
  3. Narrative / case study — comparative posts ("X vs Y"),

What it can do on your machine

Read from SKILL.md and the folder at commit 7ddcd88. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Ships script files (JavaScript), which the agent can run.

    Shell commands in SKILL.md call:

    • npm

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use npm, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Blog Post loads about 3.4k tokens when it runs. Until then it costs about 45 tokens; SKILL.md has 1,628 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~45
When it runs · the whole SKILL.md, loaded when a task matches
~3.4k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from nteract/semiotic at commit 7ddcd88, republished under its Apache-2.0 licence (© nteract). 1,628 words, ~3,379 tokens.

Download SKILL.mdSave it as .claude/skills/blog-post/SKILL.md (or your agent's skills folder). This skill also uses 3 other files; get the full folder from GitHub.
name
blog-post
description
Author a new entry for the Semiotic blog. Use this skill whenever the user asks for a blog post, release summary, chart explainer, or case study to publish at /blog/SLUG.

Writing a Semiotic blog post

This skill writes one entry for the Semiotic blog. The blog lives at /blog/; entries live at /blog/<slug>/. Three story shapes are supported — pick the one that matches what's being written:

  1. Chart explainer — single chart, "what / why / when / wiring".
  2. Release summary — what's new in a version, ordered by impact.
  3. Narrative / case study — comparative posts ("X vs Y"), walkthroughs, recreations of historical visualizations.

Every entry follows the same structure regardless of shape. The why-care section is non-negotiable: the post has to give the reader a reason to read it that lands even if they're not currently using Semiotic. The blog isn't reference docs; it's a publication that happens to be hosted on the docs site.

Before writing anything

Ask the user for the author byline unless they've already given one. Don't assume "Elijah Meeks" by default — many entries will be co-authored or attributed to "Semiotic Team" for releases. One short question, then proceed.

File structure

For a new entry with slug <slug>:

  1. Body component — docs/src/blog/entries/<slug>.jsx. Default export is { slug, title, subtitle, author, date, tags, excerpt, component, ogChart? }. The component is a React function returning JSX (the entry's body — no header, no chrome, the BlogEntryView wraps it).
  2. Register in registry — docs/src/blog/entries.js imports the new file and adds it to blogEntries.
  3. Register in metadata mirror — docs/src/blog/entries-meta.js gets the same metadata object (without component, without the React imports). This mirror is read by the OG-card generator and the prerender script, both of which run under plain Node and can't load JSX.

Both registry files must stay in sync. The OG-card generator and prerender script read entries-meta.js; the React app reads entries.js.

Required fields

js
{
  slug: "kebab-case-route",
  title: "Title-Case Headline",
  subtitle: "One or two sentences orienting the reader.",
  author: "Author Name",                  // ASK THE USER if unspecified
  date: "YYYY-MM-DD",                     // ISO; controls sort order
  tags: ["release"] | ["chart-explainer", "xy"] | ...,
  excerpt: "2–3 sentence preview shown on the index card.",
  component: Body,                        // function returning JSX
  ogChart: { component: "DifferenceChart" }, // optional, see OG step
}
Tags vocabulary

Pick freely from:

  • Shape: release, chart-explainer, case-study, tutorial
  • Family: xy, network, geo, ordinal, realtime, hierarchy

Multi-tag is fine and encouraged. Don't invent new top-level tags without checking the existing taxonomy in entries.js.

The skeleton — applies to all shapes

Every entry MUST have these sections (with the names below as h2 headings, except the intro):

  1. Opening paragraph (no heading) — one paragraph that orients the reader. State the chart / topic in concrete terms. Don't start with "In this post we will…". Start with the thing.
  2. Why this exists / why care — answer "why should I care about this if I'm not currently using Semiotic?". Even chart-explainer posts need this: tell the data-viz audience what makes the chart-type interesting, then connect it to Semiotic's implementation.
  3. The thing itself — live demo, or release-note bullets, or the comparative pair. This is the meat. Show, don't tell.
  4. How to read / how it works — once the reader has seen the thing, walk them through how to read the visual encoding (for chart posts) or where to look for the API change (for releases).
  5. When to reach for it / when not — guidance. Pair every "use it for X" with "don't use it for Y, use Z instead". This is the section that earns the reader's trust.
  6. Wiring it up — minimal code snippet showing the prop shape. For releases, link to the changelog and migration notes.
  7. Related — link to neighbor charts, related features, and the full reference page.

The Why and the When-to-reach sections are what distinguish a Semiotic blog post from the reference docs at /charts/<name>. The reference doc tells you what's there; the blog post tells you when you'd care.

Story-shape specifics

Chart explainer
  • Title format: <ChartName>, explained.
  • Opening: one-sentence elevator pitch. ("DifferenceChart is the chart you reach for when the story is the gap between two series, not either series on its own.")
  • Why-care section: cover the general data-viz problem the chart solves, NOT the Semiotic-specific API. The same audience that reads HN data-viz threads should find this useful. Then add a paragraph relating it to Semiotic's implementation (e.g. "in Semiotic this is wired through…").
  • Live demo: one self-contained chart with inline synthetic data. Keep the data small enough that the reader can imagine the underlying rows (5–15 rows is the sweet spot).
  • When-to-reach section: list 3–5 cases for it, then 3 cases against it pointing to the right alternative chart.
  • Wiring section: ≤15 lines of code. Just the minimum props.
  • Streaming / push mode section — REQUIRED for every chart explainer. Three pieces:
    1. A live push demo using BlogPushDemo from docs/src/blog/components/BlogPushDemo.jsx. Hand it a chartRef, the frames array (one entry per step), a pushAt(ref, row, i) callback that calls the chart's push method, and a resetAt(ref) callback that calls clear(). The demo gives the reader Play / Step / Reset controls and a step counter for free.
    2. A push-mode wiring snippet — ≤15 lines — showing the ref, the push() / update() calls relevant to the chart, and any required *IdAccessor (XY charts want pointIdAccessor; ordinal charts want dataIdAccessor; network HOCs use nodeIDAccessor / edgeIdAccessor).
    3. A "why push helps here" paragraph specific to this chart's nature. Generic boilerplate is worthless; the story has to land on a property the reader can map back to their own code. Examples from the seeded entries:
      • DifferenceChart: segment recomputation is cheap and in-buffer; setting data on every tick triggers React reconciliation that push skips.
      • QuadrantChart: update(id, fn) mutates one point without re-keying the rest; preserves hover and in-flight tooltips.
      • FunnelChart: bar-and-trapezoid size deltas are animated; data resets lose the animation.

    Charts that explicitly DO NOT support push (hierarchy HOCs: OrbitDiagram, TreeDiagram, Treemap, CirclePack) get a different streaming section that explains WHY push doesn't apply (the layout reads the full tree, not incremental appends) and the pattern that does work (set the data prop to a new tree; the chart's transitions still ease cleanly between trees).

  • Tags: ["chart-explainer", "<family>"].
Push-mode demo skeleton

Inside the entry file, alongside the static Body function, declare a PushDemo function that wires BlogPushDemo:

jsx
function PushDemo() {
  const chartRef = useRef(null)
  return (
    <div style={chartFrame}>
      <ThemeProvider theme="carbon-dark">
        <BlogPushDemo
          chartRef={chartRef}
          frames={DEMO_DATA}                 // array, one item per step
          pushAt={(ref, row) => ref?.push?.(row)}
          resetAt={(ref) => ref?.clear?.()}
        >
          <YourChart
            ref={chartRef}
            // ...the same props as the static demo, MINUS `data`
            pointIdAccessor="id"             // or dataIdAccessor etc.
          />
        </BlogPushDemo>
      </ThemeProvider>
    </div>
  )
}

Then reference <PushDemo /> from inside <Body>'s streaming section. Keep the same chart frame styling as the static demo so the visual continuity between the two reads as "same chart, two flavors."

Show full SKILL.md (646 more words)Show less
Release summary
  • Title format: Semiotic <X.Y.Z> (no "released today" or other date-stamped language; the entry's own date carries that).
  • Opening: one sentence summarizing the release's theme ("3.5.2 is mostly a factor-and-extend release."). Link to the full CHANGELOG entry on GitHub.
  • Why-care section: optional for releases, but if there's a big-picture story (new hook family, new chart, architecture shift) tell it here.
  • Sections: one h2 per major feature group, ordered by impact. Use the actual feature names so the reader can grep CHANGELOG.
  • Upgrade notes h2: any breakages or behavior changes, even small ones. Be explicit about what to do if affected.
  • No live demos required; link to the docs pages for new features.
  • Tags: ["release"].
Narrative / case study
  • Title format: pick a memorable one. X vs Y works; so does <famous-thing>, rebuilt in Semiotic.
  • Opening: state the comparison or the recreation in the first paragraph. Include the punchline. Don't bury it.
  • Why-care section: the reader is here because the topic is interesting independently of Semiotic. Lean into that. If you're rebuilding Minard's map, say what makes Minard's map the canonical example of data-viz composition. If you're comparing two chart types, say what makes the question "which one?" hard.
  • Multiple demos throughout. Comparative posts ideally show the two charts side-by-side or stacked.
  • Add a final h2 that lists 3–5 OTHER domains where the same story plays out. ("This pattern also shows up in pull-request lifecycle, supply-chain logistics, financial settlement, manufacturing rework.") The blog audience often isn't in the example domain; the cross-references are what make the post useful.
  • Tags: ["case-study", ...]. Add a family tag if the post is centered on one chart family.

OG card

Each entry produces a 1200×630 PNG at docs/public/blog/og/<slug>.png for social previews. Layout:

  • Left 2/3 — designed text: "Semiotic · BLOG" brand row, large title, subtitle, byline + date, tags row.
  • Right 1/3 — chart panel. When ogChart is set in metadata, the generator renders that chart via semiotic/server's renderChart and embeds the SVG. When omitted (release-summary posts, narrative posts without a single canonical chart), the panel renders a brand placeholder.

To add a chart preview:

js
ogChart: {
  component: "DifferenceChart",      // chart name as known to renderChart
  props: { /* optional overrides */ }
}

Supported chart components live in scripts/generate-blog-og-cards.mjs's OG_CHART_PRESETS. Add a new preset there if the chart you want isn't listed — preset fields are chartType + defaults (props object). The renderChart function only knows the chart families it has config for; check src/components/server/serverChartConfigs.ts to see what's supported. Charts not in renderChart (e.g. QuadrantChart, OrbitDiagram, AnscombesSankey, MinardsMarch) fall through to the brand-only card.

Run npm run generate:blog-og-cards after registering a new entry to refresh docs/public/blog/og/<slug>.png. The website build pipeline runs this automatically (it sits between generate:demo-gifs and parcel build in website:build).

SEO / pre-rendering

The blog inherits the docs' static-prerender path (scripts/prerender.mjs). For each blog entry, the script:

  • Reads metadata from docs/src/blog/entries-meta.js.
  • Writes docs/build/blog/<slug>/index.html with the title set to <entry-title> — Semiotic Blog.
  • Injects per-entry <meta name="description">, og:type=article, og:title, og:description, og:image (the rendered card PNG), article:published_time, article:author, per-tag article:tag, the full twitter:summary_large_image block, and a BlogPosting JSON-LD payload.

No additional wiring required — registering the entry in entries-meta.js is what the prerender script reads. Crawlers get a fully-resolved meta block; humans get the same SPA-loaded React experience.

Verification

Before declaring an entry done, run:

bash
# Typecheck
npm run typescript

# OG card generation
npm run generate:blog-og-cards

# Open in dev server after building the library
npm run website:start   # → http://localhost:3000/blog/<slug>/

Check that:

  • The entry appears in /blog/ (most recent in full, or in the preview list below).
  • The entry renders at /blog/<slug>/ with title, subtitle, byline, tags, and body content.
  • The OG card PNG was written and has the entry's title, subtitle, byline, and (if ogChart set) a rendered chart on the right.
  • The site builds: npm run website:build succeeds and docs/build/blog/<slug>/index.html has the entry-specific meta tags injected into <head>.

Template starter

A starter template is in templates/. Copy the shape that matches what you're writing:

  • templates/chart-explainer.js
  • templates/release-summary.js
  • templates/narrative.js

Each template has placeholder sections at the right heading levels and TODO comments at each spot the author needs to fill in. Use the templates as a checklist — every TODO needs an answer before publishing.

© nteract, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 3 other files in .agents/skills/blog-post of nteract/semiotic.

  • SKILL.md
  • templates/chart-explainer.js
  • templates/narrative.js
  • templates/release-summary.js

Open the folder on GitHubat commit 7ddcd88

Compare with similar skills

Blog Post next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Blog Post compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Blog Post this skillnteract/semiotic2.7k—~3.4kAutomated safety check: PassApache-2.0
No Em Dashespetera2c/simple-table229—~195Automated safety check: PassMIT
SepiaNanako0129/sepia3.1k—~3.6kAutomated safety check: PassMIT
Blogtheopenco/llmgateway1.7k—~2.2kAutomated safety check: PassCustom licence
Release Postquarto-dev/quarto-r1601 repos~2.5kAutomated safety check: PassMIT
Penguin Harness DevPrism-Shadow/penguin-harness2.5k—~3.4kAutomated safety check: PassApache-2.0

Similar skills

  • No Em Dashes

    petera2c/simple-table

    Avoid em dashes in marketing copy, hero text, changelogs, blog posts, docs, UI strings, and chat.

    229 GitHub stars~195 tokensUpdated 5 days ago
    Writing & ContentAuto-check passed
  • Sepia

    Nanako0129/sepia

    Make AI-generated writing read as human-written, in fiction and in professional prose.

    3.1k GitHub stars~3.6k tokensUpdated today
    Writing & ContentAuto-check passed
  • Blog

    theopenco/llmgateway

    Write and validate an LLM Gateway marketing blog post in the repository's current house style, including structured frontmatter and a gpt-image-2 OpenGraph image.

    1.7k GitHub stars~2.2k tokensUpdated today
    Writing & ContentAuto-check passed
  • Release Post

    quarto-dev/quarto-r

    Create professional package release blog posts following Tidyverse or Shiny blog conventions.

    160 GitHub starsUsed in 1 repo~2.5k tokens
    Writing & ContentAuto-check passed
  • Penguin Harness Dev

    Prism-Shadow/penguin-harness

    A skill your agent uses when developing PenguinHarness itself — changing packages/{core,server,web,cli,desktop,landing,docs,skills}, the built-in model catalog, the installers or the release…

    2.5k GitHub stars~3.4k tokensUpdated today
    Writing & ContentAuto-check passed
  • Release Blog Drafter

    obot-platform/obot

    Drafts a release announcement blog post for an obot release as a Markdown file, with an optional WordPress draft through MCP and no live publishing without confirmation.

    1.1k GitHub stars~4.6k tokensUpdated today
    Writing & ContentAuto-check passed

More from nteract/semiotic

  • Chatgpt App Submission

    nteract/semiotic

    Inspect a ChatGPT Apps MCP server codebase and generate chatgpt-app-submission.json with app info suggestions, tool hint justifications, test cases, and negative test cases, then report review-check…

    2.7k GitHub stars~2.8k tokensUpdated today
    Auto-check passed
  • Code Review

    nteract/semiotic

    Review Semiotic pull requests for behavioral bugs, regressions, contract drift, and missing evidence.

    2.7k GitHub stars~1.5k tokensUpdated today
    Auto-check passed
  • Semiotic Charts

    nteract/semiotic

    Build, repair, and verify charts in an existing Semiotic project, when Semiotic is explicitly requested, or when evaluating its documented capabilities against a visualization task.

    2.7k GitHub stars~2.1k tokensUpdated today
    Auto-check passed

Works with

Questions about Blog Post

What does Blog Post do?

Author a new entry for the Semiotic blog. An agent skill from nteract/semiotic. Blog Post is an agent skill from nteract/semiotic. Author a new entry for the Semiotic blog.

When should I use Blog Post?

Blog Post fits situations like: the user asks for a blog post; release summary; chart explainer; case study to publish at /blog/SLUG.

How do I install Blog Post in Claude Code?

Run `npx skills add nteract/semiotic --skill blog-post -a claude-code`. Or copy the skill folder (.agents/skills/blog-post in nteract/semiotic) into .claude/skills/blog-post in your project. Claude Code loads it when a task matches its description.

How do I install Blog Post in Codex?

Run `npx skills add nteract/semiotic --skill blog-post -a codex`. Or copy the skill folder (.agents/skills/blog-post in nteract/semiotic) into .agents/skills/blog-post in your project. Codex loads it when a task matches its description.

Can I use Blog Post in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add nteract/semiotic --skill blog-post -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/blog-post, .gemini/skills/blog-post, .github/skills/blog-post and .opencode/skills/blog-post in your project.

What does Blog Post need to run?

Going by SKILL.md and its folder, Blog Post needs JavaScript for the scripts in its folder and the command-line tools its instructions call (npm). Our summary lists: Node.js.

Does Blog Post access the network?

SKILL.md contains no URLs. Its commands use npm, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Blog Post safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Blog Post use?

Blog Post is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Blog Post use?

About 3.4k tokens (SKILL.md is roughly 14k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Blog Post?

Skills that share tags, products or a category with Blog Post: No Em Dashes (petera2c/simple-table, 229 stars), Sepia (Nanako0129/sepia, 3.1k stars), Blog (theopenco/llmgateway, 1.7k stars) and Release Post (quarto-dev/quarto-r, 160 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Blog Post?

nteract (a GitHub organization) maintains it in nteract/semiotic, which has 2,714 GitHub stars. The repository holds 4 skills in this directory. The repository was last updated on October 10, 2026.

Source: nteract/semiotic on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.