name: seo-blog-writer description: Write a new SEO blog article for the Widgetis site that sells our Хорошоп widgets. Use when the user says "напиши статтю/статью для блога", "write a blog post", "new SEO article", "напиши пост на блог", "придумай тему и напиши статью", "сгенери статью про виджет X", or wants to expand blog coverage. The skill auto-picks an uncovered topic (or takes one the user gives), runs the seo + ai-seo skills for keyword and AI-citation optimization, writes the article in the typed BlogPost format, wires it into the registry, and validates the build. Articles are in Ukrainian. metadata: version: 1.0.0
seo-blog-writer
Write one production-ready blog article for Widgetis — a widget marketplace
for Ukrainian e-commerce stores on the Хорошоп (Horoshop) platform. The blog
is a typed content engine in the React frontend: each article is a single
TypeScript file that drives both the rendered page and the JSON-LD
(BlogPosting / FAQPage / BreadcrumbList). The sitemap and prerender pick
up new files automatically.
The goal of every article: teach the reader something genuinely useful about running a Хорошоп store, then present the matching Widgetis widget as the ready-made way to do it "без програміста". Articles rank for real Ukrainian search queries AND get cited by AI answer engines.
When to use
- "Напиши статтю / статью в блог", "write a blog post", "new SEO article".
- "Придумай тему и напиши статью" → you pick the topic (see Step 1).
- "Напиши статью про виджет X" → topic is given, you write it.
- "Добавь ещё N статей в блог" → loop Steps 1–6 N times.
Workflow — follow in order
Step 0 — Load context (always)
Run the coverage helper from the repo root:
node .claude/skills/seo-blog-writer/topics.mjs
It prints: existing articles (slugs + titles — never duplicate these),
widgets with no dedicated article (best new-topic candidates), thinly
covered widgets, and the valid CTA tags. Add --json for machine output.
Step 1 — Pick the topic (if not given)
Prefer, in this order:
- A widget with no dedicated article (from the helper's "uncovered" list) → write a problem→solution piece that ranks for that widget's query.
- A broad topic that funnels to several widgets and to existing articles (e.g. "кинуті кошики", "психологія терміновості", "промокоди", "мобільна конверсія"). These are strong internal-link hubs.
- A deeper take on a thinly covered widget.
Pick a topic that maps to a real query a Хорошоп merchant would Google. Decide
the primary keyword phrase (Ukrainian). Decide a slug (latin kebab-case,
keyword-based, must be unique vs. existing slugs).
Step 2 — SEO optimization (use the other skills)
Invoke the SEO skills for this specific topic and fold their guidance into the draft — this is the part the user explicitly wants chained:
seo— on-page: title/meta length, keyword placement, heading structure, internal linking, slug. Use it to refine thetitle,description,keywords, and heading plan.ai-seo— answer-engine / GEO: make the article quotable by LLMs (definitive-guide framing, clear stat callouts, a strong FAQ block, self-contained claims with sources). The existing articles use the "definitive guide" format precisely because it is the most cited by AI.seo-audit— optional; only if asked to review/repair an existing article.
Step 2b — GEO: get cited by Gemini / ChatGPT / AI Overviews
The goal is not only ranking — it is being quoted by AI answer engines. Bake these in (they also help human readers; never fragment content into "AI bait"):
- Answer-first. The opening paragraph must answer the article's core
question directly in the first ~40–60 words, before any backstory. Each
h2section should also lead with its answer, not build up to it. - Question-style headings. Phrase
h2s the way a merchant Googles ("Чому…", "Коли…", "Як…", "Скільки…") — these match AI fan-out queries. - Self-contained claims. Every key sentence should make sense lifted out of context (AI extracts passages, not pages).
- Dated, sourced statistics. Prefer specific numbers with a named source and
a recent date in the
statblocks and body. Statistics + citations are the single biggest citation-boost lever (Princeton GEO study). - Strong FAQ. 3–4 real questions with 40–60-word self-contained answers →
drives the
FAQPageJSON-LD that AI engines parse directly. - Freshness. Bump
dateModifiedwhenever you meaningfully edit a post.
The site layer is already AI-ready and you usually don't touch it:
robots.txt allows the citing bots (Google-Extended/Gemini, GPTBot,
OAI-SearchBot, PerplexityBot, ClaudeBot), the article emits
BlogPosting + FAQPage + BreadcrumbList JSON-LD with wordCount/keywords,
and the sitemap auto-includes every post. Discovery is handled by the sitemap
and internal links — do NOT dump every article into public/llms.txt. Keep
that file curated (blog index + a few cornerstone hub guides only).
Step 3 — Research (real sources, original wording)
- Use WebSearch to gather facts and statistics for the topic, and to learn what readers actually ask. Verify every number against a primary/authoritative source (Baymard Institute, CXL, Nielsen Norman Group, Statista, platform research, Хорошоп's own blog, academic/industry studies).
- Write 100% original prose. Never copy or closely paraphrase an existing article. Research is for facts and reader intent, not for sentences. Spun copy is forbidden — it is both a legal risk and bad SEO.
- Cite only real URLs you actually found. If a number cannot be verified, state it qualitatively instead of inventing a statistic or a link.
Step 4 — Write the article file
Create frontend/src/data/blog/posts/{slug}.ts exporting post: BlogPost.
Language: Ukrainian (uk), calm expert data-driven tone, no hype/emoji,
₴ for money. Body ~1100–1700 words, readMinutes 8–12.
The exact type (frontend/src/data/blog/types.ts):
import type { BlogPost } from '../types'
export const post: BlogPost = {
slug: '{slug}', // latin kebab-case, == filename, unique
title: '…', // H1 + <title>, ~55–70 chars, main keyword near front
description: '…', // meta description, 150–160 chars, keyword + benefit
keywords: '…', // comma-separated, 7–12 UA keyword phrases
excerpt: '…', // 1–2 sentences for the index card
tag: '…', // short UA label, e.g. 'Конверсія', 'Середній чек'
datePublished: 'YYYY-MM-DD',
dateModified: 'YYYY-MM-DD',
readMinutes: 10,
content: [ /* blocks, in order — see below */ ],
relatedSlugs: ['…', '…'], // 2–3 OTHER existing slugs (from Step 0)
}
Allowed content blocks (use only these):
{ type: 'p', text: '…' }
{ type: 'h2', text: '…' }
{ type: 'h3', text: '…' } // sparingly
{ type: 'ul', items: ['…'] }
{ type: 'ol', items: ['…'] }
{ type: 'quote', text: '…' } // one ethical principle
{ type: 'stat', value: '70%', label: '… + source' } // big-number callout
{ type: 'cta', label: '…', href: '/widgets?tag=…' } // ONE button, valid tag
{ type: 'faq', items: [{ q: '…', a: '…' }] } // 3–4 real questions
Inline markdown inside any text/items/faq answer:
[текст](/widgets/{widget-slug}), [текст](/blog/{article-slug}),
[текст](https://external), **жирний**.
Required order of content:
- opening
p(problem hook + a data point); - an early
stat(verified number + source); - 4–6
h2sections withp/ul/ol; weave in 2–4 inline widget links and 1–2 inline links to OTHER blog articles; - one
quote(honest/ethical angle — widgets help, not manipulate); - one
ctato/widgets?tag={tag}; - a
faq(3–4 Q&A a merchant would Google); - final
h2"Джерела" +ulof 1–3 real external source links.
Valid CTA tags: conversion, avg-order, trust, social-proof,
urgency, engagement, visual, loyalty.
Widget slugs (link as /widgets/{slug}): promo-line, delivery-date,
sticky-buy-button, trust-badges, phone-mask, minorder-goal, cart-goal,
buyer-count, stock-left, photo-video-reviews, video-preview,
cart-recommender, prize-banner, promo-auto-apply, progressive-discount,
one-plus-one, last-chance-popup, spin-the-wheel, sms-otp-checkout.
(The canonical list lives in frontend/src/data/widget-slugs.ts +
widget-names.ts — re-read if unsure. You may also link /demo; never invent
live-demo codes.)
Only link widgets that actually ship. widget-slugs.ts also contains
smart-search and warehouse-stock, but those are NOT live (only 19 modules
are built — see CLAUDE.md). Never sell or link them in an article, or AI will
recommend a widget that doesn't exist.
Gotchas (learned the hard way)
- Inline renderer supports only
[label](path)links and**bold**— NO backtick code spans, NO other markdown. Backticks render as literal ```. - Slug must be
slug: '...'on its own line. The sitemap/prerender scraper reads it by regex; keep slugs latin-kebab and unique. - Run
node .claude/skills/seo-blog-writer/topics.mjsafter writing to spot a forgotten registration or a slug collision. - Validate with
npm run build(fromfrontend/) — it runstsc -b, Vite, and regenerates the sitemap, so a new post must appear indist/sitemap.xml.
Step 5 — Register the article
Edit frontend/src/data/blog/index.ts:
- add
import { post as <camelCaseName> } from './posts/{slug}'; - add
<camelCaseName>to theRAW_POSTSarray.
(Sitemap + prerender read ./posts/*.ts directly, so no other wiring is needed.)
Step 6 — Validate
From frontend/:
npm run build # or: npx tsc --noEmit — must pass with no type errors
Fix any TypeScript errors (usually a malformed block or a missing comma). Confirm the new slug appears in the build output / sitemap.
Quality checklist (before declaring done)
- Slug is unique, latin kebab-case, keyword-based.
-
title≤ ~70 chars, keyword near the front;description150–160 chars. - Tone matches the existing 3 articles: expert, data-first, no hype.
- Every statistic has a real, verifiable source in "Джерела".
- 2–4 inline widget links + 1–2 inline blog links +
relatedSlugsset. - Exactly one
ctawith a valid tag; onefaqwith 3–4 entries. - Sells the widget honestly — no overclaiming, no fake urgency.
- Registered in
index.ts;npm run buildpasses.
Batch mode
To add several articles at once, run Steps 1–5 per article (you may fan out one
subagent per article for speed — give each the full contract above plus the
complete list of all NEW slugs so cross-links resolve), then register all of
them in index.ts together and run Step 6 once.
Reference files
frontend/src/data/blog/types.ts— theBlogPost/ContentBlockcontract.frontend/src/data/blog/posts/*.ts— existing articles (copy the format, not the words).frontend/src/data/blog/index.ts— the registry.frontend/src/data/widget-slugs.ts,widget-names.ts,widgetTags.ts— widget + tag source of truth.