The CMS we kept not wanting
Every marketing site we build hits the same fork in the road. The site itself is easy: a frontend rendering engine (Next.js, Astro, SolidStart, Vue, take your pick), content in Markdown files, deployed to Cloudflare Pages, all in one Git repository. Markdown is the natural fit. Early-stage startups are run by developers, and developers write the blog themselves - they’d rather commit a Markdown file than log into WordPress.
Then someone asks the question that ruins it: “How do non-developers edit this?”
The usual answer is a CMS - Contentful, Sanity, Storyblok, a headless something. Now you have two systems instead of one. Content lives in a database we don’t control, the site pulls it at build time through an API, and every content model change is a migration in a web UI plus a schema change in the repo. The content team gets an editor, and we get a second source of truth to keep in sync, a webhook to trigger rebuilds, and an API key to rotate. For a site that is mostly a dozen Markdown files, that is a lot of moving parts to avoid teaching someone how to edit Markdown.
We tried the other direction too: just give the content team write access to the repo and let them edit Markdown in the GitHub web editor. That works until the first time someone pastes a smart quote into frontmatter, breaks the YAML, and the build fails silently on a Friday. Markdown is not hard, but “edit this file, respect the exact frontmatter schema, don’t touch the components, open a branch, wait for the preview” is a real workflow with real footguns, and it is not a content manager’s job to learn it.
Claude Tag let us delete both options.
What Claude Tag actually is
Claude Tag puts Claude in a Slack channel as another team member. You tag
@Claude with a request, it works the task in stages with whatever tools it has
been given, and reports back in the thread. It is in beta for Claude Enterprise
and Team customers, runs on Opus 4.8, and replaces the older Claude in Slack
app.
Two properties matter for using it as a CMS:
- It is multiplayer. Within a channel there is one Claude that everyone talks to. The whole content team sees the request, the work, and the result in the same thread. No per-seat editor logins, no “who changed this?”
- It connects to your codebase. Administrators grant it access to specific tools and data sources - including a GitHub repository. That is the whole trick. Give it the repo, and the Slack channel becomes the front-end to your content.
So we made a channel. We called it #website.
The setup
The site is a standard Zentered build:
- Astro with
output: 'static', fully pre-rendered - SolidJS islands for the few interactive components
- Markdown/MDX content in
src/content/, validated by a Zod schema - Cloudflare Pages with the GitHub integration: every push builds,
maingoes to production, every branch and pull request gets its own preview URL
There is no CMS in that list, and that is the point. Content is
src/content/posts/*.md and a few JSON files in src/data/ for page copy. The
schema in src/content.config.ts is the content model. Git history is the audit
log. Cloudflare preview builds are the staging environment.
Claude Tag gets access to exactly one thing: the GitHub repository. Nothing touches production directly. Everything it does becomes a pull request.
The workflow
A content manager types into Slack:
@Claudeadd a blog post announcing our new observability service. Same tone as the last two posts, tag itannouncement, and use the diagram I dropped in#designyesterday as the cover image.
Claude works the thread. It reads the existing posts to match the frontmatter
schema and the writing style, creates src/content/posts/observability.md on a
new branch, wires up the cover image, and opens a pull request. Because
Cloudflare Pages builds every branch, within a minute or two the thread has a
preview URL that renders the actual post on the actual site.
The content manager clicks the preview, reads it, and either asks for a change
in the same thread (“shorten the intro, the headline should be sentence case”)
or approves and merges the pull request on GitHub. Merging to main deploys to
production. That is the entire loop:
- Ask in
#website - Claude opens a PR
- Cloudflare builds a preview
- Reviewer approves and merges on GitHub
- Cloudflare deploys to production
Nobody logged into a CMS. Nobody ran a build. Nobody edited YAML by hand. And the reviewer approving the change is looking at the rendered page and the exact diff at the same time - which is a better review surface than any CMS “preview” we have shipped, because it is not a preview of the content, it is the content.
Keeping Claude on the rails
The obvious risk: you have handed a language model write access to your website’s repository and pointed non-developers at it. What stops “update the pricing page” from turning into “refactor the pricing component, introduce a new dependency, and change the framework because it felt like a good idea”?
The answer is the same thing that governs any Claude session in a repo: the
CLAUDE.md file at the root. It is the system prompt, checked into the repo,
and Claude Tag reads it before it touches anything. We treat it as the
content-editor contract. The core of it is a three-tier classification of every
file in the project - what is safe to edit, what needs confirmation, and what a
non-developer must never touch:
## Content editor guardrails
### Safe - edit freely
Pure content. Editing text, swapping images, adding entries is always fine.
| What | Where |
| ------------ | --------------------------- |
| Blog posts | `src/content/posts/*.md` |
| Case studies | `src/content/projects/*.md` |
| Page copy | `src/data/*.json` |
| Images | `src/assets/images/` |
### Caution - confirm before editing
These affect how the site looks and behaves. Explain the change first.
| What | Where | Risk |
| -------------- | --------------------------- | -------------------- |
| Page templates | `src/pages/**/*.astro` | Can break rendering |
| Components | `src/components/**/*.astro` | Can break every page |
| Styles | `<style>` blocks | Can break the design |
### Do not touch - requires a developer
| What | Where |
| -------------- | --------------------------- |
| Astro config | `astro.config.mjs` |
| Content schema | `src/content.config.ts` |
| Dependencies | `package.json` |
| Deploy & CI | `wrangler.toml`, `.github/` |
Then a short list of absolute rules that close the footguns we actually worried about:
### General rules for all edits
- **Re-use existing components** - before creating anything new, check
`src/components/` for something that already does the job. A duplicate
component is worse than slightly imperfect reuse.
- **Never change the framework** - this is an Astro site. Don't introduce React,
Vue, or Svelte. Don't suggest migrating to Next.js.
- **Never install packages** - no new dependencies.
- **Never restructure files or folders** - don't move, rename, or reorganize.
- **When editing JSON, preserve the key structure** - change values, not keys.
The page template may not render a field you invent.
- **If something seems broken**, describe the problem instead of fixing it.
Recommend involving a developer.
- **One change at a time** - make a single focused edit, confirm it looks right,
then move on.
This is the same guardrail file we run on the Nevados marketing site, almost
verbatim. It is not clever prompt engineering - it is a written policy that
happens to be readable by both a new content hire and the model. The component
re-use rule is the one that earns its keep the most: without it, “add a
testimonial section” produces a brand-new component with slightly different
spacing every time. With it, Claude finds the existing <Testimonial>, matches
the existing pattern, and the design stays consistent.
The component library is the design system
That last point deserves its own heading. It is what makes this safe for a marketing site, not just convenient.
The site ships a small library of components - <Hero>, <CTA>,
<FeatureGrid>, <Testimonial>, an <Image> wrapper that enforces dimensions.
The CLAUDE.md rule “re-use existing components, don’t invent new visual
patterns” turns that library into a boundary Claude works inside. A content
manager can ask for a new landing section and get one assembled from the
approved building blocks, styled with the existing tokens, without a designer in
the loop and without visual drift.
You constrain the model with the same thing that constrains a junior developer: a component library and a written instruction to use it. Not guardrails bolted on after the fact - the design system, doing the job it was already there to do.
What we gave up, honestly
This is not free, and it is not right for every site.
- It needs Claude Enterprise or Team. Claude Tag is in beta and gated to those plans. A three-person startup on individual Pro accounts can’t do this yet.
- There is a cost meter running. Administrators set token spend limits per channel, and you should. “Rewrite the entire blog in a warmer tone” is one Slack message and a lot of tokens. The spend controls and activity logging are there for a reason - use them.
- Review discipline is non-negotiable. The whole model rests on a human approving the pull request. If someone enables auto-merge to move faster, you have handed an LLM unreviewed write access to production. The preview build and the required review are the safety mechanism. Don’t remove them.
- It is Slack-only. If your content team doesn’t live in Slack, this workflow isn’t for them.
For a content-heavy Astro site with a team already in Slack and on a Team or Enterprise plan, though, it removed an entire category of software from the stack. No CMS, no headless API, no editor seats, no rebuild webhook. The content model is a Zod schema, the editor is a Slack channel, staging is a Cloudflare preview URL, and the publish button is a GitHub merge. Every piece was already in the repo. Claude Tag just made them reachable from a sentence.
If you are running a static site on Astro or SolidJS and have been putting off the CMS decision, this is worth a look before you add one. The best CMS might be the repository you already have, plus a channel to talk to it.
If you have any questions or comments, please reach out on Bluesky or start a discussion on GitHub.
Further reading
- Introducing Claude Tag - the announcement, with setup and availability details
- Astro content collections - the Zod-validated content model that acts as the schema
- Cloudflare Pages Git integration - per-branch preview builds and production deploys on merge
