template-kit/image-bare-needs-reason
The template-kit/image-bare-needs-reason authoring rule — what it enforces, why, and how to fix it.
<Image bare>needs a// bare: <reason>comment above it. A reason cannot be spent twice — one comment documents exactly one element.
Every <Image bare …> must have a qualifying reason comment. A comment qualifies when
all three hold:
- It contains
bare:— e.g.// bare: the logo is chrome; the parent already sizes it. - It sits strictly above the element, within 5 lines. Above, not beside and not below.
- It has not already been spent on an earlier
<Image bare>.
Both comment forms count — a // line comment and a {/* … */} JSX comment. The rule
reads the raw comment stream rather than hopping up the AST, so proximity survives
arbitrary wrapping: a container <div>, a .map() callback, a parenthesized return, an
assignment to a const. If it looks directly above the element, it counts.
The surprising part: a reason cannot be spent twice. One comment above a row of sibling bare logos documents only the first of them; the second still reports. Each bare image is its own decision, so each gets its own sentence. (A comment written between two bare images is fine for the second — it starts after the first element ends.)
Reason
Section titled “Reason”Framed is <Image>’s default, and the frame is load-bearing: a wrapper div that holds
the aspect-ratio box, carries the slot marker, and renders a neutral fallback rect when
src is empty.
bare drops all of it — one <img>, no wrapper. So a bare image with no source is
nothing at all: the layout collapses, and whatever sat below it jumps up the page. A
missing image is a supported state on this platform (a thin listing simply has no photo),
so “the source is always there” is an assumption, not a fact.
bare is still the right call sometimes — a logo whose parent already establishes the
box, a slide inside its own aspect-ratio parent. The rule does not ban it; it makes you
say why the parent guarantees the box, so the next author (or the next agent) can
tell whether the frame is safe to restore.
Put a // bare: <reason> comment immediately above the element. A reason is legitimate in
either of two ways: it can name what guarantees the layout box without <Image>’s
frame (a parent that already establishes the size, a slide inside its own aspect-ratio
container) — or it can name something about the image’s own content that makes the
frame the wrong choice, such as a transparent PNG that framing would letterbox. If
neither holds, drop bare and let <Image> frame it.
Before
Section titled “Before”import { Image, Section } from "@homepages/template-kit";
import type { Props } from "./schema.js";
export function Renderer({ slots }: Props) { return ( <Section> <div className="h-12 w-32"> <Image bare src={slots.brand_logo.url} alt={slots.brand_logo.alt} className="object-contain" /> </div> </Section> );}import { Image, Section } from "@homepages/template-kit";
import type { Props } from "./schema.js";
export function Renderer({ slots }: Props) { return ( <Section> <div className="h-12 w-32"> {/* bare: the logo is chrome; the parent already sizes it. */} <Image bare src={slots.brand_logo.url} alt={slots.brand_logo.alt} className="object-contain" /> </div> </Section> );}Two bare images need two comments — the first one’s reason is spent:
<div className="flex gap-4"> {/* bare: the primary logo is chrome; the row sizes it. */} <Image bare src={slots.logo_a.url} alt={slots.logo_a.alt} className="h-8 object-contain" /> {/* bare: the partner logo is chrome; the row sizes it. */} <Image bare src={slots.logo_b.url} alt={slots.logo_b.alt} className="h-8 object-contain" /></div>A content-based reason is written the same way — only the sentence changes, because it’s justifying a different thing (the image, not the layout around it):
{/* bare: a transparent PNG badge; Image's frame would letterbox its transparent padding against the section background. */}<Image bare src={slots.certification_badge.url} alt={slots.certification_badge.alt} className="h-10" />See also
Section titled “See also”- Contract primitives:
Image— framed vs bare, and what the frame does for you. no-raw-element— why<img>goes through<Image>at all.