# Recipe Rule Guideline

## Purpose
Recipes are the reusable-look layer of Akan UI: Tailwind-variant factories (`recipe(tv({...}))`) that sit between semantic
tokens (values) and components (behavior). This rule governs how agents **consume** and **author** recipes so a look is not
hallucinated, re-derived inline, or duplicated across an app.

## Ownership
- A recipe owns a **look** (a composition of semantic-token utility classes), never behavior. Behavior lives in components.
- **One recipe per file.** App/lib recipes live in `apps/<app>/ui/Recipe/<name>.ts` and `libs/<lib>/ui/Recipe/<name>.ts`,
  re-exported from that folder's hand-written `index.ts`; framework recipes (`buttonRecipe`, `badgeRecipe`,
  `inputRecipe`) mirror the same shape in `pkgs/akanjs/ui/recipe/<name>Recipe.ts`. Apps inherit the framework recipes by
  re-exporting them from their own `Recipe/index.ts`, so a consumer imports every recipe from one barrel.
- The folder must be `Recipe` (PascalCase) in an app/lib `ui/`: the generated `ui/index.ts` exports PascalCase names
  only, so a flat `appCard.recipe.ts` is silently skipped and becomes unimportable. Inside the folder any camelCase
  filename is fine — the barrel only scans one level.
- Recipes use **semantic tokens only** in their `base`/`variants` (never raw palette or hex); they inherit the CSS token rules.

## Consuming Recipes
- Import by **exact name** from the owning barrel: `import { <name> } from "@apps/<app>/ui"` (or `akanjs/ui` for framework),
  then call `<name>(variants?, className?)` — the second arg is merged automatically; no extra `cn()` wrapper is needed.
- The second arg is a `ClassNameValue`, so it takes **an array (and falsy entries), not just one string**:
  `<name>({}, ["h-full", isWide && "w-full", className])`. Reaching for `cn()` to combine several classes is the
  usual way the no-`cn` contract leaks — there is nothing `cn()` adds here that the recipe does not already do.
- **Do not guess** recipe names or import paths. The authoritative index is split by ownership: the root `AGENTS.md`
  `## Recipes` (always loaded) lists **framework** recipes, and each app/lib's own `AGENTS.md` (`## Recipes In Scope`)
  lists what that scope may additionally import — its own recipes plus its dependency libs'. Both carry every recipe's
  variant surface and default, so consuming one never requires opening its file. The scoped index is regenerated by
  `akan sync <name>` and kept honest by `akan lint <name>`, which fails when it is stale.
- Variant options are typed (`Parameters<typeof <name>>[0]`), so a wrong variant is a compile error — let tsc validate
  options rather than memorizing strings.

## Recipe vs Component vs Constant — The Gate
The form of a shared look is decided by **counting**, not judgment (`akan lint` enforces this as `recipeGate`):

1. **Does the caller choose an option?** (at least one variant axis with 2+ values, or a boolean flag)
   - **No** → it is **never a recipe** — a fixed look wrapped in a function is dead indirection.
     - Reused with its own markup → a small **component** (`<Divider/>`, `<DocsList/>`, `<Screen/>`).
     - Injected into another component's `className` prop → a shared **class constant** (`appNavClass`).
     - Used once → plain **inline** classes.
   - **Yes** → it is a recipe. Then:
2. **Does it need markup or behavior** (own tag, structure, state, a11y)?
   - No → pages call the recipe directly on their own element (`panelRecipe({ padding: "row" })`).
   - Yes → a component consumes the recipe internally (`<Button variant>` → `buttonRecipe`).

## Authoring Recipes
- A **reusable or repeated surface** (card, box, tile, chat bubble, hero, …) belongs in a recipe, **not** inline. If the same
  token-class stack appears in more than one place, extract it into a recipe.
- **Before authoring, check the existing recipe indexes** (root `AGENTS.md` `## Recipes` for framework recipes, the
  scope's own `AGENTS.md` `## Recipes In Scope` for app/lib recipes) and **reuse** a matching recipe instead of
  creating a near-duplicate — registry sprawl comes from re-inventing looks that already exist.
- Add a new recipe as its own file, `ui/Recipe/<name>.ts` holding a single
  `export const <name>Recipe = recipe(tv({ base, variants }))` plus a one-line JSDoc describing the surface, then
  re-export it from `ui/Recipe/index.ts`. Name the export `<name>Recipe`; the filename drops the suffix.
- Keep declaration and css in that one file. Splitting a recipe into a hand-written variant type plus a separate style
  file inverts the dependency: the variant type is currently *inferred* from the `tv` config
  (`Parameters<typeof <name>Recipe>[0]`), which makes type/css drift structurally impossible, and the AGENTS listing
  already exposes the surface without reading the file. There is nothing left for the split to buy.
- App recipes **extend** (surfaces the framework lacks); they never re-define a framework component's look in parallel.

## Codegen Rules
- Do not re-derive inline a look that an existing recipe already provides.
- Do not author a near-duplicate of an existing recipe; reuse or extend it.
- Do not guess recipe names or import paths; use the authoritative list.
- Do not put raw-palette / hex / inline color in a recipe; semantic tokens only.

## Review Checklist
- Repeated/variant surfaces are recipes, not inline class stacks duplicated across files.
- No two recipes describe the same look; consumers import by exact name from the correct barrel.
- New recipes live one-per-file in the owning `ui/Recipe/`, exported as `<name>Recipe`, token-only, with a one-line doc,
  and re-exported from that folder's `index.ts`.
- The output contract tells the model which file paths to return.
