Recipes in Depth
Define component variants that compile through the same atomizer as utilities and fx objects.
Recipes are Facet's component-variant authoring surface. They are defined as typed fx objects,
then compiled through the same FIR and atomizer as utility strings and direct fx() calls.
The source example defines a button recipe:
// Component recipes (the 3rd authoring surface). Variants are typed fx objects that
// lower through the SAME atomizer, so recipe atoms dedup with utilities and fx().
import { defineRecipe } from "../src/fir.mjs";
export default {
button: defineRecipe({
base: { _focusVisible: { ring: true } },
variants: {
tone: {
primary: { bg: "action", fg: "on-action", _hover: { bg: "action-hover" } },
neutral: { bg: "surface", fg: "ink", _hover: { bg: "elevated" } },
subtle: { bg: "bg", fg: "ink-muted" },
},
size: {
sm: { px: "inset.md", py: "inset.sm", radius: "control" },
md: { px: "inset.lg", py: "inset.md", radius: "control" },
},
},
defaultVariants: { tone: "primary", size: "md" },
}),
};
Parts of a recipe
base: styles that apply to every variant.variants: enumerated axes. Each axis contains named values and each value is anfxobject.defaultVariants: the values used when a caller does not provide a prop.
The guide also shows compoundVariants support:
import { defineRecipe, registerRecipe } from "facet";
const button = registerRecipe(az, defineRecipe({
base: { radius: "control", _focusVisible: { ring: true } },
variants: { tone: { primary: { bg: "action", fg: "on-action" }, subtle: { bg: "bg", fg: "ink-muted" } },
size: { sm: { px: "inset.md", py: "inset.sm" }, lg: { px: "inset.lg", py: "inset.md" } } },
compoundVariants: [ { when: { tone: "primary", size: "lg" }, style: { shadow: "card" } } ],
defaultVariants: { tone: "primary", size: "lg" },
}));
button.classesFor({ tone: "primary", size: "lg" }); // -> "fc-... fc-... ..."
Dedup behavior
Recipes do not create a separate component CSS layer. Each declaration normalizes to the same
canonical atom tuple used by utilities and fx():
{ prop: "<css-property>", value: "var(--token)", state: null, at: null }
That means tone.primary.bg = "action" can share an atom with:
<button class="bg-action fg-on-action radius-control px-inset-lg py-inset-md hover:bg-action-hover focus-visible:ring">
Action
</button>
and with a direct fx() object:
{ bg: "action", fg: "on-action", radius: "control", px: "inset.lg", py: "inset.md",
_hover: { bg: "action-hover" }, _focusVisible: { ring: true }, "@md": { px: "inset.lg" } }
Practical guidance
Use recipes where the variant list is finite and component-owned. Use utilities or fx() directly
for one-off layout and local composition.
Because Facet is pre-release, treat the recipe API as reference implementation surface. The source spec confirms the concept and shape, while broader production parser coverage remains planned work.