Concrete examples of what Astroc4 does, and who each one is for.
Developer
A diagram and the prose around it in the same diff
A page explains a request path, and the diagram beside it lives in a design tool that nobody updates when the path changes, so the two disagree and a reviewer cannot see it.
````mdx
---
title: Request path
---
```likec4 title="Request path" height=360
specification { element box }
model {
browser = box 'Browser'
api = box 'API'
store = box 'Postgres'
browser -> api 'GET /orders'
api -> store 'select'
}
views { view index { include * } }
```
````
What you get: The diagram is the page: the fence is compiled and laid out during the build, the layout is passed to the island as serialised JSON, and a change to the model is a line in the pull request a reviewer can read next to the prose.
The fence runs LikeC4.fromSource plus layoutedModel at build time, so nothing is laid out in the browser; a typo fails astro build with the file name and parser message rather than shipping a blank frame, and layouts are memoised by a hash of the snippet so a page re-rendered on every dev edit pays for it once. The same fence in a .md page stays a code block deliberately, because Astro cannot put a component there.
Read the guide →
Developer
Highlighting that follows LikeC4, with no ec.config.mjs
You want ```c4 and ```likec4 fences coloured in your docs, but Shiki has no LikeC4 grammar and the community TextMate file is missing the shapes and keywords upstream added this year.
// astro.config.mjs — there is no ec.config.mjs anywhere in the site
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
import react from '@astrojs/react';
import astroc4 from '@purview-dev/astroc4';
export default defineConfig({
integrations: [
starlight({ plugins: [astroc4({ highlighting: { ref: 'v1.60.0' } })] }),
react(),
],
});
What you get: The plugin derives a TextMate grammar from likec4/likec4's Langium language definition and injects it into Starlight's Expressive Code options from the config:setup hook, so every LikeC4 fence is coloured and an existing ec.config.mjs keeps its own languages untouched.
Only the word lists are generated; the structure (comments, triple-quoted strings, #tag versus #hex, tech:astro icons, -[https]-> connectors) is a curated layer on top. The difference is testable: ElementShape gained agent after v1.59.4, so a build pinned to that tag paints agent as a plain identifier while main paints it as a shape. Expressive Code concatenates shiki.langs, which is why a site with its own grammar ends up with both.
Read the guide →
Architect
One model in the repository, many diagram views in the docs
Several documentation pages describe different slices of the same system, and you want them to read from one model so a change to a dependency cannot leave three diagrams telling three stories.
```mdx
---
title: Deployment
---
import LikeC4View from '@purview-dev/astroc4/components/LikeC4View.astro';
<LikeC4View viewId="production" projectId="platform" height={560} />
```
What you get: The .c4 model lives in src/likec4 and each page selects a view of it; the model is compiled once by LikeC4's Vite plugin, and every page hydrates the same islands, so a view cannot describe a system the model does not contain.
The plugin compiles every likec4.config.json project under the site root into likec4:model, likec4:icons and likec4:projects virtual modules, and the first project found is the default for a view that omits projectId — which is also the failure mode, since a vendored example model can silently become the default. An unresolvable element kind fails the build instead of rendering a partial diagram.
Read the guide →
Team lead
Prove the diagrams actually hydrate before calling the docs built
A static build exits 0 even when every island fails to hydrate, so the pipeline is green and the published page shows empty frames.
bun run build
bun scripts/smoke.ts http://localhost:4321
bun run test:site
What you get: The browser smoke test and the packed-consumer build are the evidence: one drives a real page and checks hydration, icons, highlighting and translations, the other installs the tarball into a throwaway Starlight site so publishing mistakes surface before npm does.
The smoke test asserts at least four distinct colour variables per highlighted block, counts prerendered pack icons and <img> icons, and fails on a file:// URL in an <img src> — the build-machine path that LikeC4's imageAliases bakes into the bundle. test:site catches the cases the playground cannot see: a file missing from files, an exports subpath pointing at an unpublished file, or an undeclared peer dependency.
Read the guide →
Contributor
Regenerate the grammar instead of hand-editing it
Upstream LikeC4 adds a shape or a keyword, and the highlighting has to follow without someone patching a TextMate JSON file and hoping the next release does not overwrite it.
bun run grammar:sync # from main
bun run grammar:sync --ref v1.60.0 # or pin the tag you ship against
bun run grammar:check # fail if the committed snapshot is stale
What you get: The Langium snapshot and the TextMate grammar are generated artefacts: the working tree says so, grammar:check fails when they drift from the pinned ref, and the next upstream addition reaches highlighting without a code change.
src/grammar/likec4.tmLanguage.json is re-derived from the committed Langium text on every run (single-digit milliseconds), and snapshot.test.ts re-derives it again and compares, so a change to the derivation that is not followed by a regeneration fails bun run test. Because the icon packs come from LikeC4's LIB_ICON terminal, a pack upstream adds is highlighted and rendered without an edit here either.
Read the guide →