12. Definitions
When the same shape comes back again and again, such as a card for each plot, you can write it once as a component of your own and use it like a built-in one. This chapter writes two: a card for a plot, used as a leaf, and a gardener's name, used inside a sentence. They are written in CompMD itself, so they need no code and work wherever the document is shown.
The newsletter, with components of its own#
---
md:
version: 1
title: Elm Street Garden
issue: November 2026
plots:
- {number: 1, gardener: Maya Okafor, crop: garlic}
- {number: 2, gardener: Tomás Reyes, crop: kale}
- {number: 3, gardener: "", crop: ""}
- {number: 4, gardener: Priya Nair, crop: leeks}
---
# {{md.doc.title}}
News from the plots, {{md.doc.issue}}.
```compmd component=plot-card {padding=s border=subtle border-radius=m background=(gardener == "" ? "accent" : "none")}
params:
number: number
gardener: string
crop: string
---
# Plot {{number}}
:value[Maya Okafor, growing garlic.]{of=(gardener == "" ? "Free for next year." : gardener + ", growing " + crop + ".")}
```
```compmd component=gardener
params:
name: string
plot: number
---
**{{name}}** (plot {{plot}})
```
## The plots
::: grid {min=12rem gap=m}
::: each {in=(md.doc.plots) as=p}
:: plot-card[Plot {{p.number}}] {number=(p.number) gardener=(p.gardener) crop=(p.crop)}
::: /each
::: /grid
## Thanks
To :gardener[Maya Okafor (plot 1)]{name="Maya Okafor" plot=1} for turning the compost, and to :gardener[Priya Nair (plot 4)]{name="Priya Nair" plot=4} for mending the gate.
Try it in the playground.
The page shows four cards, one per plot, each with its number as a heading and a line about it. Plot 3 has no gardener, so its card has the accent background and says Free for next year. The thanks name Maya and Priya in bold, with their plots.
A definition#
A component is defined in a fenced code block, the kind you'd write for code, whose first line says it is a definition:
```compmd component=plot-card {padding=s border=subtle}
compmdis the language the component is written in: CompMD itself. Components written in code use other languages, and are the Developer's Guide's.component=plot-cardnames it. The name is how you write it in the document::: plot-card[…] {…}.- The params in braces, which may be left out, style the component's box (below).
Inside the fence come three parts:
- The signature: what the component takes. Here
paramslists three params and the type of each value. Chapter 15 has every type;string(text) andnumberare the ones you'll use most. - A line of three dashes,
---. - The body: CompMD, as you'd write it anywhere in a document. It is what each use of the component shows.
The fence itself doesn't show on the page. A plain Markdown viewer, such as GitHub's, shows it as a code block, so a reader there can see how the component is made.
Params in the body#
In the body, a param is a name of its own: {{number}} inserts the plot's number, as {{md.doc.title}} inserts the frontmatter's title. In an expression, write it without braces: gardener == "" ? "accent" : "none".
A param whose name has a -, such as last-year, is written with _ in the body: {{last_year}}, because expressions can't use - in names.
The body also sees the frontmatter of the document the definition is in, as md.doc, but not the reader's inputs (chapter 7): md.input in a body is a diagnostic (F014). A component gets an input's value as a param instead (chapter 15).
Using a component#
You write your component as any component: as a leaf, or inline, or, once it has slots (chapter 13), as a container. Its params go in braces, with the values its signature says:
:: plot-card[Plot 1] {number=1 gardener="Maya Okafor" crop=garlic}
A bare value is read by its type: number=1 is the number 1, because the signature says number. A value can be an expression too, as in the each above: number=(p.number).
The brackets hold the fallback, as for any leaf: the text a plain viewer shows, and the page shows if the component can't be drawn (chapter 15). Write what the card says: Plot 3, or Plot 1: Maya Okafor, garlic.
An inline use, :gardener[Maya Okafor (plot 1)]{…}, puts the body's text in the sentence. That works when the body is one paragraph, as gardener's is; a body of several blocks can't go in a sentence, and an inline use of it shows its fallback.
The processor checks every use against the signature, and says what's wrong:
| Written | Diagnostic |
|---|---|
:: plot-crad[…] |
F002: no such component, did you mean plot-card? |
colour=red |
F008: plot-card has no param colour |
number=one |
F009: number takes a number |
no number |
F010: plot-card needs number, and shows its fallback |
::: plot-card … ::: /plot-card |
F018: plot-card declares no slots, so it takes no body |
The component's box#
Each use of a component as a block is one box, as each ::: box is, and the body goes in it. The params after the name on the fence style that box: they take the common params of chapter 3, and box's own, such as display and gap (Part III). They can be expressions of the component's params: background=(gardener == "" ? "accent" : "none") makes a free plot's card stand out.
The params of a use style the same box, and win over the fence's. :: plot-card[…] {… padding=m} gives that card more room, and keeps the fence's border and background.
A component that should add no box of its own, such as a name inside a list of cards, has display=contents on its fence, and nothing else. Its blocks then sit directly in whatever holds it. Such a component has no box to style, so a box param on its use is ignored, with a warning (W120).
Headings in a body#
The card's title is written # Plot {{number}}, yet on the page it is a third-level heading. Headings in a body are relative to where the component is used: # is one level below the heading above the use, ## two below, and so on. The cards are under The plots, a second-level heading, so their titles are third-level. Used under the document's title, they would be second-level. So the page's outline stays right wherever a component goes. Chapter 15 shows how a component keeps its headings as written.
Where definitions go#
A definition goes anywhere at the top level of the document, not inside a container, and its name is known to the whole document, above and below it. These are near the top, so a reader of the text sees them before their uses; at the end, they would keep the newsletter's text together. Inside a container, such as a box, it isn't a definition, and the component's uses are F002.
When several documents use the same components, keep the definitions in a file of their own and import them: chapter 15 shows how.
Components that use components#
A body can use any component the document can name: built-in ones, as plot-card uses value, ones from packs, and your own. So a gardener-list could use gardener for each name. What a component can't do is use itself, directly or through another component: that would never end, and is F019.
Names#
A component's name is made of letters, digits, - and _, and starts with a letter. Like the built-in ones, names are usually lower case words joined by -: plot-card, work-day.
- Two definitions with one name are
F013, and the first one is used. if,each,valueandboxare CompMD's own, and can't be defined (F013).- A definition named like another built-in component, such as
roworgrid, replaces it in this document, with a warning (W112), so check that's what you mean.
Next: slots, for components with a body you write, in parts.