| name | document-recipe |
| description | Step 5 of the create-recipe chain. Runs a 4-phase documentation flow (pattern extraction → source analysis → draft → conformance check) grounded 100% in existing recipe doc pages and the recipe TypeScript source. Reference pages auto-select based on color pattern — badge.md for Full, card.md for Container, spinner.md for Minimal. Writes the final doc to apps/docs/content/docs/05.components/<category-dir>/<nn>.<name>.md. Standalone-capable — runs against an existing recipe even without prior artifacts. Absorbs the previous generic write-docs skill. |
Document Recipe
Step 5 of the Styleframe recipe creation chain. Produce a documentation page that is indistinguishable in style from the existing recipe docs, grounded 100% in the recipe source code.
Persona
You are a senior technical writer with 15+ years of experience at developer-facing companies (Stripe, Vercel, Supabase). You specialize in producing documentation that developers can scan, understand, and act on in under 60 seconds. You study the conventions of the existing Styleframe docs site and apply them consistently. You read recipe source code to extract accurate variant axes, options, and defaults — and you never invent functionality that isn't present in the code. You flag uncertainty with [VERIFY: ...] rather than filling gaps.
Inputs
.context/recipe-<component-name>/design.md (optional — falls back to reading the recipe TS directly)
.context/recipe-<component-name>/implementation.md (optional — falls back to ls theme/src/recipes/<name>/)
.context/recipe-<component-name>/showcase.md (optional but recommended — provides story IDs for ::story-preview embeds)
Standalone mode: this skill can run without any .context/ artifacts. If invoked alone (/document-recipe), ask the user which recipe to document, then locate the recipe TS under theme/src/recipes/<name>/.
Outputs
.context/recipe-<component-name>/documentation.md — process artifact: reference pages used, remaining [VERIFY] flags, conformance deviations, confidence rating.
apps/docs/content/docs/05.components/<category-dir>/<nn>.<name>.md — the final documentation page. <category-dir> is the recipe's numbered category folder under 05.components/: 02.actions, 03.navigation, 04.feedback, 05.forms, 06.overlays, 07.layout, or 08.ai-chat.
Reference recipe docs (auto-select by color pattern)
- Full color (9 colors — badge, button, callout, chip):
apps/docs/content/docs/05.components/04.feedback/00.badge.md
- Container color (3 colors — card, modal, tooltip):
apps/docs/content/docs/05.components/07.layout/03.card.md
- Minimal color / multi-recipe:
apps/docs/content/docs/05.components/04.feedback/05.spinner.md
Pick the reference that matches the recipe's color pattern. For multi-part recipes also read 03.card.md for its Anatomy section. Read at least 2 reference pages when in doubt.
The 4-phase workflow
Run these phases in order. Output each in its own tagged block inside documentation.md.
Phase 1: Pattern extraction — <patterns>
Read the 2–3 most similar reference pages in full. Extract the conventions:
- Frontmatter:
title + description. No H1 in the body — the title renders from frontmatter.
- Section order: Overview → Why use the X recipe? → Usage (
::steps{level="4"}) → Colors → Variants → Sizes → Anatomy (multi-part only) → Accessibility → Customization → API Reference → Best Practices → FAQ (::accordion).
- Heading style: sentence case, verb-first where it makes sense ("Why use the Badge recipe?", "Overriding defaults").
- Usage block:
::steps{level="4"} with three sub-steps: "Register the recipe" (:::code-tree with component .styleframe.ts + root styleframe.config.ts), "Build the component" (::framework-switcher with #vue, #react, and #other slots), "See it in action" (::story-preview).
- Framework switcher: the "Build the component" step MUST use
::framework-switcher with #vue, #react, and #other slot markers (Vue first), not :::tabs / ::::tabs-item. The switcher renders one synced toggle across the whole docs site, so a reader's framework choice persists between pages — per-page tabs don't. The toggle always offers all three options (React, Vanilla, Vue); the #other slot is the Vanilla view, so provide all three — if a slot is missing, the reader sees a "Not available for X — showing Y" fallback notice instead of code. Some older pages still use :::tabs; treat ::framework-switcher as the standard and do not copy the tabs form. The #vue and #react slots show idiomatic component code; the #other (Vanilla) slot shows the runtime returning a class string applied to plain .ts + .html (see card.md).
- Code-fence languages: use only languages the docs Shiki config loads (
apps/docs/nuxt.config.ts → content.build.markdown.highlight.langs: bash, diff, json, js, ts, html, css, vue, shell, mdc, md, yaml). The React block's language token MUST be ts, NOT tsx (and never jsx) — tsx is not registered and renders unhighlighted. Put the .tsx extension in the filename label only, e.g. ```ts [src/components/<ComponentName>.tsx].
- Story-preview embed:
::story-preview with YAML front body story: theme-recipes-<category>-<component-name>--<story> and optional panel: true or height: NNN. The story ID is Storybook's auto-generated ID from the story's title: "Theme/Recipes/<Category>/<Component>", so it always includes the category segment (actions, navigation, feedback, forms, overlays, layout, ai-chat) — e.g. theme-recipes-feedback-badge--default, theme-recipes-layout-pagehero--all-sizes. PascalCase / multi-word titles collapse per Storybook's rules (PageHero → pagehero, Button Group → button-group). Take exact IDs from showcase.md; never hand-write them without the category segment.
- Colors section: opening sentence, a
::story-preview of the default-color story, a "Color Reference" sub-heading with a table (| Color | Token | Use Case |) and a ::tip admonition at the end.
- Variants section: one sub-heading per variant style with a 1-sentence description and a
::story-preview per variant.
- Sizes section: opening sentence, a
::story-preview of the all-sizes story, and a reference table (| Size | Font Size | Border Radius | — adapt columns to the recipe).
- Anatomy (multi-part only):
| Part | Recipe | Role | table.
- Accessibility: bulleted list, WCAG-flavored, bold lead phrases.
- Customization: "Overriding Defaults" + "Filtering Variants" sub-sections with code blocks.
- API Reference: per-recipe Parameters table (
| Parameter | Type | Description |) + Variants table (| Variant | Options | Default |).
- Best Practices: bullets with bold lead phrases.
- FAQ:
::accordion with 4–6 :::accordion-item{label="..." icon="i-lucide-circle-help"}.
- Voice: second person, imperative, bold lead phrases in bullets.
- Admonitions:
::tip, ::note, ::accordion. No emoji. Use the exact marker syntax the references use (double-colon for block admonitions, triple-colon for nested containers).
- Cross-links:
/docs/api/recipes, /docs/theme/design-tokens/presets, /docs/api/recipes#compound-variants.
Present the extracted conventions as a concise bullet list in <patterns>.
Phase 2: Source analysis — <analysis>
Read the recipe TS under theme/src/recipes/<component-name>/. If implementation.md exists, it lists every file. Otherwise, glob the directory.
Extract:
- Purpose: what the component is and when a developer uses it (infer from the design's component type; the recipe name alone is not enough).
- Public API surface:
- Exported composable(s) and their signatures (e.g.,
use<Name>Recipe(s, options?)).
- For multi-recipe: every sub-recipe (
use<Name>HeaderRecipe, use<Name>BodyRecipe, ...).
- Per recipe:
- Variant axes and their values.
- Default variants.
- Compound variants count (N colors × M variant styles = total).
- Non-semantic colors used (
light, dark, neutral) and their behavior (fixed vs adaptive).
- Setup-callback effects (e.g., registered keyframes).
- Dependencies: which design tokens the recipe references (scan for
@color.*, @font-size.*, @border-radius.*, ...).
- Edge cases: what
filter removes, how defaultVariants adjusts.
Flag anything unclear with [VERIFY: brief description of uncertainty]. Do not infer behavior that isn't in the code. If the design's rationale isn't in the source, cite design.md if available or flag with [VERIFY: design-level claim, source unclear].
Write the analysis in <analysis>.
Phase 3: Draft — <doc>
Determine <nn> for the filename by listing the existing files in the recipe's category folder apps/docs/content/docs/05.components/<category-dir>/ (use Glob or ls) — numbering restarts per category. Pick the next number in sequence. If there are gaps, skip them — do not renumber existing pages.
Use the canonical structure below. Substitute values from Phase 2. Match the conventions from Phase 1 exactly.
CRITICAL: The template below uses spaced-out backticks (` ` `) to avoid breaking this skill's own Markdown. In the real file you write, replace them with actual triple backticks.
---
title: <ComponentName>
description: <One-sentence description covering purpose, colors, visual styles, sizes, and the recipe system.>
---
## Overview
The **<ComponentName>** is <description of what the component is and when it's used>. The `use<ComponentName>Recipe()` composable creates a fully configured [recipe](/docs/api/recipes) with <list the axes> options — plus compound variants that handle the color-variant combinations automatically.
The <ComponentName> recipe integrates directly with the default [design tokens preset](/docs/theme/design-tokens/presets) and generates type-safe utility classes at build time with zero runtime CSS.
## Why use the <ComponentName> recipe?
The <ComponentName> recipe helps you:
- **Ship faster with sensible defaults**: Get <N> colors, <N> visual styles, and <N> sizes out of the box with a single composable call.
- **Maintain consistency**: Compound variants ensure every color-variant combination follows the same design rules.
- **Customize without forking**: Override base styles, default variants, or filter out options you don't need — all through the options API.
- **Stay type-safe**: Full TypeScript support means your editor catches invalid color, variant, or size values at compile time.
- **Integrate with your tokens**: Every value references the design tokens preset, so theme changes propagate automatically.
## Usage
::steps{level="4"}
#### Register the recipe
Add the <ComponentName> recipe to a local Styleframe instance. The global `styleframe.config.ts` provides design tokens and utilities, while the component-level file registers the recipe itself:
:::code-tree{default-value="src/components/<component-name>.styleframe.ts"}
` ` `ts [src/components/<component-name>.styleframe.ts]
import { styleframe } from 'virtual:styleframe';
import { use<ComponentName>Recipe } from '@styleframe/theme';
const s = styleframe();
const <componentName> = use<ComponentName>Recipe(s);
export default s;
` ` `
` ` `ts [styleframe.config.ts]
import { styleframe } from 'styleframe';
import { useDesignTokensPreset, useUtilitiesPreset } from '@styleframe/theme';
const s = styleframe();
useDesignTokensPreset(s);
useUtilitiesPreset(s);
export default s;
` ` `
:::
#### Build the component
Import the `<componentName>` runtime function from the virtual module and pass variant props to compute class names:
::framework-switcher
#vue
` ` `vue [src/components/<ComponentName>.vue]
<script setup lang="ts">
import { <componentName> } from "virtual:styleframe";
const { color = "<default>", variant = "<default>", size = "<default>" } = defineProps<{
color?: /* union */;
variant?: /* union */;
size?: /* union */;
}>();
</script>
<template>
<<element> :class="<componentName>({ color, variant, size })">
<slot />
</<element>>
</template>
` ` `
#react
` ` `ts [src/components/<ComponentName>.tsx]
import { <componentName> } from "virtual:styleframe";
interface <ComponentName>Props {
color?: /* union */;
variant?: /* union */;
size?: /* union */;
children?: React.ReactNode;
}
export function <ComponentName>({
color = "<default>",
variant = "<default>",
size = "<default>",
children,
}: <ComponentName>Props) {
const classes = <componentName>({ color, variant, size });
return <<element> className={classes}>{children}</<element>>;
}
` ` `
#other
The `<componentName>()` runtime returns a class string. Apply it however your framework binds classes:
` ` `ts [src/components/<component-name>.ts]
import { <componentName> } from "virtual:styleframe";
const classes = <componentName>({ color: "<default>", variant: "<default>", size: "<default>" });
// → "<component-name> ..."
` ` `
` ` `html [src/components/<component-name>.html]
<<element> class="<component-name> ...">...</<element>>
` ` `
::
#### See it in action
:::story-preview
---
story: theme-recipes-<category>-<component-name>--default
panel: true
---
:::
::
## Colors
<Description of the color system for this component.>
::story-preview
---
story: theme-recipes-<category>-<component-name>--<default-color>
panel: true
---
::
### Color Reference
::story-preview
---
story: theme-recipes-<category>-<component-name>--all-variants
height: 420
---
::
| Color | Token | Use Case |
|-------|-------|----------|
| `primary` | `@color.primary` | Default actions, links, key information |
| `secondary` | `@color.secondary` | Secondary information, neutral states |
| `success` | `@color.success` | Positive states, confirmations, completions |
| `info` | `@color.info` | Informational messages, tips, highlights |
| `warning` | `@color.warning` | Caution states, pending actions |
| `error` | `@color.error` | Error states, destructive actions, alerts |
| `light` | `@color.white` / `@color.gray-*` | Light surfaces, stays light in dark mode |
| `dark` | `@color.gray-900` | Dark surfaces, stays dark in light mode |
| `neutral` | Adaptive (light <-> dark) | Default color, adapts to the current color scheme |
::tip
**Pro tip:** Use semantic color names that describe purpose, not appearance.
::
## Variants
<Description of variant styles and count.>
### Solid
<Description.>
::story-preview
---
story: theme-recipes-<category>-<component-name>--solid
panel: true
---
::
### Outline
<Description.>
::story-preview
---
story: theme-recipes-<category>-<component-name>--outline
panel: true
---
::
### Soft
<Description.>
::story-preview
---
story: theme-recipes-<category>-<component-name>--soft
panel: true
---
::
### Subtle
<Description.>
::story-preview
---
story: theme-recipes-<category>-<component-name>--subtle
panel: true
---
::
## Sizes
<Description of size system.>
::story-preview
---
story: theme-recipes-<category>-<component-name>--all-sizes
height: 480
---
::
| Size | Font Size | Border Radius |
|------|-----------|---------------|
<!-- Fill based on recipe size values -->
## Anatomy
<!-- Multi-part recipes only. Omit this section for single-recipe components. -->
| Part | Recipe | Role |
|------|--------|------|
| `<componentName>` | `use<ComponentName>Recipe` | Root container |
| `<componentName>Header` | `use<ComponentName>HeaderRecipe` | Header section |
| `<componentName>Body` | `use<ComponentName>BodyRecipe` | Main content |
| `<componentName>Footer` | `use<ComponentName>FooterRecipe` | Footer actions |
## Accessibility
- **<Accessibility guideline 1>**
- **<Accessibility guideline 2>**
## Customization
### Overriding Defaults
` ` `ts [src/components/<component-name>.styleframe.ts]
const <componentName> = use<ComponentName>Recipe(s, {
base: { borderRadius: '@border-radius.full' },
defaultVariants: { color: 'success', variant: 'soft', size: 'md' },
});
` ` `
### Filtering Variants
` ` `ts [src/components/<component-name>.styleframe.ts]
const <componentName> = use<ComponentName>Recipe(s, {
filter: {
color: ['primary', 'error'],
variant: ['solid', 'outline'],
},
});
` ` `
::note
**Good to know:** Filtering also removes compound variants and adjusts default variants that reference filtered-out values, so your recipe stays consistent.
::
## API Reference
### `use<ComponentName>Recipe(s, options?)`
Creates a full <component-name> recipe with all variants and compound variant styling.
**Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `s` | `Styleframe` | The Styleframe instance |
| `options` | `DeepPartial<RecipeConfig>` | Optional overrides for the recipe configuration |
| `options.base` | `VariantDeclarationsBlock` | Custom base styles |
| `options.variants` | `Variants` | Custom variant definitions |
| `options.defaultVariants` | `Record<keyof Variants, string>` | Default variant values |
| `options.compoundVariants` | `CompoundVariant[]` | Custom compound variant definitions |
| `options.filter` | `Record<string, string[]>` | Limit which variant values are generated |
**Variants:**
| Variant | Options | Default |
|---------|---------|---------|
| `color` | <!-- list --> | `<default>` |
| `variant` | <!-- list --> | `<default>` |
| `size` | <!-- list --> | `<default>` |
<!-- For multi-recipe, add one `### use<Name><Part>Recipe(s, options?)` section per sub-recipe. -->
[Learn more about recipes ->](/docs/api/recipes)
## Best Practices
- **Choose colors by meaning, not appearance**: Use `success` for positive states and `error` for errors.
- **Stick to one or two sizes per context**: Mixing too many sizes creates visual noise.
- **Filter what you don't need**: Pass a `filter` option to reduce generated CSS.
- **Override defaults at the recipe level**: Set your most common combination as `defaultVariants`.
<!-- Add component-specific best practices -->
## FAQ
::accordion
:::accordion-item{label="How do compound variants work in this recipe?" icon="i-lucide-circle-help"}
<Answer explaining the compound variant matrix for this specific component.>
[Learn more about compound variants ->](/docs/api/recipes#compound-variants)
:::
:::accordion-item{label="Can I add custom colors?" icon="i-lucide-circle-help"}
<Answer with code example showing how to extend colors.>
:::
:::accordion-item{label="How does filtering affect compound variants?" icon="i-lucide-circle-help"}
When you use the `filter` option, compound variants that reference filtered-out values are automatically removed. Default variants are also adjusted if they reference a removed value.
:::
<!-- Add 2-3 more component-specific FAQ items -->
::
Substitutions:
- Replace every
<ComponentName>, <componentName>, <component-name>, <category>, <element> with real values. <category> is the recipe's Storybook category slug (actions, navigation, feedback, forms, overlays, layout, ai-chat), matching its title: "Theme/Recipes/<Category>/<Component>"; the docs folder is the numbered <category-dir> under 05.components/ (e.g. 04.feedback for feedback).
- Replace every
/* union */ with the actual TypeScript union of recipe values (e.g., "primary" | "secondary" | ...).
- Fill every
<!-- ... --> placeholder with content derived from Phase 2.
- Trim the Color Reference table to the colors the recipe actually supports (drop rows for colors not in
variants.color).
- Trim the Variants section to the variant styles the recipe actually supports.
- Drop the Anatomy section for single-recipe components.
- Every
::story-preview story ID must exist in showcase.md (or be a story the showcase step would emit under the conventions) and must include the category segment (theme-recipes-<category>-<component-name>--<story>). Flag missing IDs with [VERIFY: story ID not found in showcase.md].
Phase 4: Conformance check — <conformance>
Compare the draft against the chosen reference page(s). In <conformance>, list:
- Structural deviations: section order, missing sections, extra sections.
- Formatting inconsistencies: heading style, admonition syntax, code-block language tags, table column count.
- Framework switcher: the "Build the component" step uses
::framework-switcher with #vue, #react, and #other (Vanilla) slots (Vue first), not :::tabs / ::::tabs-item. Flag the tabs form, or a missing #other slot, as a deviation.
- Story ID validity: every
::story-preview story ID must match an entry in showcase.md.
- Remaining
[VERIFY] flags: list them all.
- Confidence rating: High (everything traceable to source + showcase) / Medium (a few
[VERIFY] flags or mild deviations) / Low (significant gaps or invented behavior — must not ship without human review).
Write the final doc file to apps/docs/content/docs/05.components/<category-dir>/<nn>.<name>.md.
documentation.md schema
# Documentation: <Name>
## Reference pages used
- apps/docs/content/docs/05.components/<category-dir>/<reference>.md
- <additional references>
## Final doc path
apps/docs/content/docs/05.components/<category-dir>/<nn>.<name>.md
## Remaining [VERIFY] flags
- <file:line — verification needed>
- ...
## Confidence: <High | Medium | Low>
## Conformance deviations
- <section X: observed behavior vs reference>
- ...
Quality checklist
Before writing the final file:
Constraints
- Source code is the single source of truth. Do not infer, assume, or invent any behavior, parameter, return value, or error not present in the recipe TS or the approved design.
- Reference pages define the form. Do not introduce structural or stylistic patterns not present in the reference pages.
- Flag uncertainty, don't fill gaps. Use
[VERIFY: ...] for anything ambiguous.
- Maintain the same markdown flavor as the reference pages (Nuxt Content —
::block, :::container, ::::nested syntax).
- Do not hallucinate cross-references. Only link to pages/sections that exist in the references or are explicitly referenced in the recipe.
- Remove filler words. No "simply," "just," "obviously," "it should be noted that," "as you can see."
Next step
After documentation.md and the final doc file are written, invoke /verify-recipe.