πŸ“‹β€‚Rules for AI Agents

What do I paste into my repo so an agent stops writing decks that build wrong?

This page is a set of blocks meant to be pasted into your own project’s AGENTS.md, CLAUDE.md, or .cursor/rules β€” not a description of how Narro works. A project scaffolded with npm create narro@latest already has all of this in its AGENTS.md; this page exists for the case where you added Narro to a repository that already existed, so nothing wrote that file for you.

Paste the two contracts below and the verification loop. Each one prevents a specific failure that a model, left to its own defaults, produces reliably.

Choose the entry point before you scaffold

The first decision, and the one most often made wrong β€” a deck ends up inside a Vite application it never needed, and every later question is about the project rather than the deck.

The deck isUseYou get
Prose, lists, code, tables β€” one filenpx @getnarro/cli new deck.mddeck.md, nothing else
The same, but version-pinned in a reponpm create narro@latest x -- --template markdown-minimaldeck.md + package.json
Needs its own dependencies or React layoutsnpm create narro@latest x -- --template markdown-appthe deck inside a Vite project
Live components, data, interactionnpm create narro@latest xa React deck

A single markdown file in an empty directory is a complete deck: the CLI brings its own runtime, so npx @getnarro/cli dev deck.md needs no package.json, no node_modules, and no config. Custom layouts (layouts/<name>.tsx) and custom CSS (deck.css) both work beside a bare deck.md β€” reaching for a project is about dependencies, not capability.

A model that has not read Narro’s documentation falls back on the presentation tooling it has seen most: Marp, reveal.js, Slidev. Those have a different markdown dialect and a different component model, and the resulting deck builds and renders wrong rather than failing β€” directives appear as literal text on the slide, a heading is imported from the wrong package, a layout name that never existed silently falls back to the default. Nothing throws, so nothing tells the model to try again.

The contracts below are written to be retrieved by the next agent that is about to make one of those mistakes.

Contract 1 β€” the markdown dialect

## Narro markdown rules

Narro is not Marp, reveal.js, or Slidev. Their syntax does not work here and
fails silently β€” a directive Narro does not recognise renders as literal text
on the slide.

1. Slides are separated by a line of exactly `---`. A `---` inside a fenced
   code block does not split a slide.
2. The file opens with YAML deck frontmatter; each slide may carry its own
   frontmatter block immediately after its separator.
3. Attach styling with `{.class}` after an element β€” `# Revenue {.text-7xl}`.
   These are Tailwind classes. There is no `<!-- .element: -->` and no `<style>`
   block.
4. `{#id}` sets an element id; `{key=value}` sets a prop.
5. `::name::` on its own line opens a named layout slot. Every built-in layout
   takes `::header::` and `::footer::`; the column layouts take one slot per
   column β€”
   <!-- generated:layout-slots-inline -->`default` takes `header`, `footer`; `cover` takes `header`, `footer`; `section` takes `header`, `footer`; `quote` takes `header`, `footer`; `end` takes `header`, `footer`; `two-column` takes `right`, `header`, `footer`; `three-column` takes `two`, `three`, `header`, `footer`; `image-right` takes `header`, `footer`; `image-full` takes `header`, `footer`<!-- /generated:layout-slots-inline -->.
   Content after a slot the layout does not read renders nowhere, silently.
6. Speaker notes are `<!-- notes: … -->`. Not `???`, not `Note:`.
7. `{.step}` on a list item reveals it on the next keypress β€” one press per
   marker, in source order, then the slide advances. There is no
   `<!-- .slide: data-fragment -->`.
8. Any frontmatter key beginning `x-` is yours: nothing reads it, and
   `check --strict` stays quiet. Use it to say what the file is.
9. Deck frontmatter keys are exactly:
   <!-- generated:deck-frontmatter -->`title`, `author`, `date`, `theme`, `template`, `aspectRatio`, `transition`, `class`, `keyboard`, `mouse`, `touch`, `routing`, `favicon`, `maxDuration`<!-- /generated:deck-frontmatter -->.
   Slide frontmatter keys are exactly:
   <!-- generated:slide-frontmatter -->`layout`, `class`, `id`, `transition`, `background`, `notes`<!-- /generated:slide-frontmatter -->.
   An unrecognised key is ignored, not reported.
10. Set `id:` on any slide you may edit again β€” it keeps diffs stable and makes
    the slide linkable.
11. `{ … }` takes whatever Tailwind calls a class, including arbitrary values
    (`text-[10px]`), opacity modifiers (`bg-white/5`), fractions, variants
    (`hover:`, `md:`), half-steps (`p-1.5`) and `!` overrides. On a list item it
    styles the `<li>`; on a fence, the `<pre>`.
12. Full dialect: https://getnarro.com/docs/markdown-mode.md

Contract 2 β€” never invent a name

This is the one that matters most, because every failure it prevents is silent. layout: split and theme: corporate both read exactly like names Narro would have. Neither exists.

## Narro closed sets

Every name a Narro deck can use is enumerated. If a name is not on the list, it
does not exist β€” do not reach for a plausible-looking alternative, and do not
assume a name from another presentation tool carries over.

- Built-in layouts (`layout:`):
  <!-- generated:layouts-inline -->`default`, `cover`, `section`, `quote`, `end`, `two-column`, `three-column`, `image-right`, `image-full`<!-- /generated:layouts-inline -->
- Named slots, per layout:
  <!-- generated:layout-slots-inline -->`default` takes `header`, `footer`; `cover` takes `header`, `footer`; `section` takes `header`, `footer`; `quote` takes `header`, `footer`; `end` takes `header`, `footer`; `two-column` takes `right`, `header`, `footer`; `three-column` takes `two`, `three`, `header`, `footer`; `image-right` takes `header`, `footer`; `image-full` takes `header`, `footer`<!-- /generated:layout-slots-inline -->.
  `::right::` on a three-column slide is a slot nothing reads, and the content
  silently disappears. The image layouts take their image from the slide's
  `image:` key (falling back to `background:`), not from a slot.
- Themes (`theme:`):
  <!-- generated:themes-inline -->`default`, `corporate-dark`, `minimalist-light`, `creative-gradient`, `ocean-blue`, `forest-green`, `sunset-warm`, `midnight-purple`, `tech-dark`, `startup-bold`, `academic-classic`<!-- /generated:themes-inline -->
- Colour schemes β€” **palettes, not a frontmatter key**; `narro themes apply <id>
  --color-scheme`, or read from `@getnarro/marketplace`:
  <!-- generated:color-schemes-inline -->`ocean-blue`, `sunset-warm`, `forest-mist`, `royal-purple`, `rose-gold`, `arctic-blue`, `charcoal-slate`, `emerald-night`<!-- /generated:color-schemes-inline -->
- Font presets β€” **not a frontmatter key**; `applyFontPreset(name)` from
  `@getnarro/core`:
  <!-- generated:font-presets-inline -->`system`, `inter`, `roboto`, `openSans`, `lato`, `montserrat`, `poppins`, `raleway`, `sourceSerif`, `playfairDisplay`, `merriweather`, `jetBrainsMono`<!-- /generated:font-presets-inline -->

The complete machine-readable set β€” the above plus every component's props,
every exported string union (transitions, fragment effects, shapes, chart
types), every CLI command β€” is one fetch:

    https://getnarro.com/catalog.json

Offline, the same file is `@getnarro/docs/catalog.json` after
`npm i -D @getnarro/docs`, pinned to the version actually installed.

## Narro imports

Two packages. Reaching into the wrong one is the most common broken deck, and
it fails at build time rather than silently β€” but only after you have written
the whole file.

`@getnarro/core` β€” the presentation runtime plus the motion and canvas
primitives. Exactly these, and nothing else:

<!-- generated:components:core -->`AnimatePresence`, `Canvas`, `CanvasElement`, `CanvasShape`, `ErrorBoundary`, `ErrorOverlay`, `MarqueeRow`, `MasonryBackground`, `Motion`, `MotionContainer`, `MotionList`, `MotionNumber`, `MotionPresence`, `MotionSpotlight`, `MotionStep`, `MotionSteps`, `MotionText`, `MotionTransform`, `NebulaBackground`, `Notes`, `OverviewGrid`, `Presentation`, `PresenterNotes`, `Slide`, `SlideContent`, `TransformSlide`, `TrustedByMarquee`<!-- /generated:components:core -->

`@getnarro/shared-ui` β€” every slide content component: `Heading`, `Text`,
`List`, `Code`, `Image`, and the rest
(<!-- generated:component-count:shared-ui -->53<!-- /generated:component-count:shared-ui --> in total).

`import { Heading } from "@getnarro/core"` does not resolve. If a component is
not in the `core` list above, import it from `@getnarro/shared-ui`. Every
component's package and props: https://getnarro.com/components.json, or one at
a time at https://getnarro.com/llms/<Component>.md.

Contract 3 β€” verify, do not trust

Documentation tells a model what is possible. Only a checker tells it whether what it wrote is correct, and a model with a fast checker self-corrects while a model with only prose cannot.

## Narro verification loop

Run after every edit, not once at the end:

    npx @getnarro/cli check deck.md

`check` parses the deck, resolves every layout, theme, slot, and directive
name, reports every class that was written but landed on no element, and exits
non-zero naming what was wrong and which names would have worked. It takes about
a second.

When you want to see what a slide says, without a browser:

    npx @getnarro/cli check deck.md --render

Before you finish:

    npx @getnarro/cli check deck.md --strict   # the CI gate: warnings are errors
    npx @getnarro/cli check deck.md --fit      # does it fit the slide? needs Chromium
    npx @getnarro/cli build deck.md            # markdown decks
    npm run build                              # React projects

A deck that renders is not a deck that is correct. `check` is the only thing
that catches a layout that does not exist, a slot name the layout has no slot
for, a theme that is not installed, or a class that attached to nothing β€” none of
which produce an error at render time. `--fit` is the only thing that catches a
slide whose content is clipped, in either mode: for a React deck, build first and
point it at `dist`.

Do not run Prettier over `deck.md`. A reflow can move an attribute block onto its
own line, which changes what it styles, silently. `narro fmt deck.md` is the safe
formatter.

Predicting a name you have not looked up

Narro’s naming is regular, so a name can usually be derived rather than guessed. State the rule, not just the list β€” a model that knows the rule stops producing coin-flip guesses:

You wantThe name isNot
A frontmatter keycamelCase β€” aspectRatio, colorScheme, fontPresetaspect-ratio, color_scheme
A layout, theme, or colour schemekebab-case β€” two-column, corporate-darktwoColumn, TwoColumn
A layout slotthe position, not the content β€” ::right::, ::left::::sidebar::, ::second::
A componentPascalCase, singular β€” List, Image, SlideLists, SlideItem
A Tailwind class in {…}whatever Tailwind calls ita Narro-specific alias

Two places where the regularity breaks, and both bite:

layout means two different things. In slide frontmatter it is the built-in layout name (two-column). On the React SlideContent component it is a content alignment:

"default" | "centered" | "top" | "bottom".

They are separate closed sets and neither accepts the other’s values.

List variants are CSS marker names, not semantic ones. variant is

"disc" | "decimal" | "check" | "arrow" | "none" β€”

not "bullet" and not "number", which are the two a model reaches for first.

The rest of the AI surface

For an assistant with no shell, the MCP server covers the same ground as tools. For one with a shell, documentation plus check is the better setup β€” see AI integration for the split.

IntroductionWhat is Narro, and should I be writing markdown or React?
InstallationWhat do I install, and what does the project look like afterwards?
First DeckWhat does a working React deck look like, end to end?
Markdown ModeHow do I split a slide, style one word, or reveal a line β€” without leaving the markdown file?
Writing AI PromptsWhat do I tell an AI so the deck it writes actually builds?
Rules for AI AgentsWhat do I paste into my repo so an agent stops writing decks that build wrong?
React APIWhich component or hook do I import, and what does it take?
CLIWhich command do I run, and what are its flags?
Markdown APIHow do I read, edit, or validate a deck file from my own code instead of by hand?
Component ReferenceWhat props does this component take, and which package do I import it from?
AnimationHow do I reveal a list one line at a time, or move between slides with something other than a cut?
NavigationHow does the audience move through the deck, and how do I present it?
Canvas & PositioningHow do I put something at an exact position instead of in the flow?
Images & MediaHow do I use an image as a background, tint it, or embed a video?
ArchitectureWhich package owns what, and why is the seam where it is?
Transform ModeHow do I zoom and pan across one big canvas instead of cutting between slides?
Import & ExportHow do I get this deck out as PPTX, PDF, or one file I can email?
AI IntegrationHow do I wire an AI assistant up to Narro so it can write and build decks?
ThemingHow do I change the colours, fonts, and overall look of a deck?
Deck TemplatesHow do I define one house style with named layouts my slides can reference, like a PowerPoint master?
TroubleshootingSomething is wrong with my deck. What is it, and how do I fix it?
LimitationsWhat can't Narro do, and what do I do instead?