// Shared HTML builders for the live-preview gallery. Every specimen is built // from REAL framework classes (.sf-*) plus the thin token-driven pv-* skin // (see ./skin.ts). Values that need to vary per specimen use inline // `style="…:var(--sf-*)"` — still 100% token-driven, so configurator overrides // update them live exactly like a class would. Nothing here re-implements a // framework component; it only arranges real ones and labels them. /** Escape text that ends up as HTML content (token/class names, notes). */ export function esc(s: string): string { return String(s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } /** Monospace caption under a specimen (class/token name or scale step). */ export function tag(text: string): string { return `${esc(text)}`; } /** A labelled specimen cell: the rendered thing, then its name underneath. */ export function specimen(label: string, body: string): string { return `
${body}${tag(label)}
`; } /** Like `specimen`, but for primitives whose whole point is filling real * available width (grid column counts, sidebar, switcher, bento) — the * `sf-stack--center` in the base variant shrink-wraps its child to * intrinsic content width (`align-items: center`), which silently * defeats these: a 4-column grid or a sidebar collapse can't demonstrate * anything at ~100px. Only the caption is centered here; the demo itself * stretches to the card's full width, same as it would in real markup. */ export function specimenFull(label: string, body: string): string { return `
${body}${tag(label)}
`; } /** A titled section block (eyebrow + optional note + content). */ export function section(eyebrow: string, content: string, note?: string): string { const noteHtml = note ? `

${esc(note)}

` : ""; return `
${esc(eyebrow)}
${noteHtml} ${content}
`; } /** The page shell every tab body is wrapped in. `head` is the tab title row. */ export function page(title: string, lead: string, ...sections: string[]): string { return `
Framework API

${esc(title)}

${esc(lead)}

${sections.join("\n")}
`; } export function cluster(...items: string[]): string { return `
${items.join("")}
`; } export function stack(gap: "xs" | "s" | "m" | "l", ...items: string[]): string { return `
${items.join("")}
`; } /** A responsive auto-fitting grid of specimen cells. `min` in rem. */ export function grid(min: number, ...items: string[]): string { return `
${items.join("")}
`; } /** A bordered card used as a neutral backdrop for a specimen row. */ export function well(content: string): string { return `
${content}
`; } /** A plain bordered surface backdrop — like `well`, but NOT a `.sf-card`. * Use for rows of `.sf-btn`: the framework's `.sf-card .sf-btn` rule pins a * nested button's label to `--sf-text-s`, which would flatten a per-size * button font ladder; a non-card frame lets each button render its true size. */ export function frame(content: string): string { return `
${content}
`; }