Specimens
The five tile kinds — imageCrop, palette, type, motion, layoutMock — each with a live rendered example.
A specimen is what an option looks like. It is the rendered content of a tile. There are exactly five kinds, and a Specimen is a discriminated union on kind, so the type system knows which fields each one needs.
Every example on this page is a real <Loupe /> rendering the specimen kind it documents. Lock a tile to see it flow into the composed preview.
imageCrop
A real <img> of a declared asset, framed to a normalized crop rectangle. This is the specimen for photographic and moodboard art direction — choosing which part of a board is the hero.
{
kind: "imageCrop",
asset: "board", // a key in config.assets
crop: { x: 0, y: 0, w: 0.34, h: 1 }, // normalized 0..1 of the intrinsic image
alt: "The left ridge of the valley", // required — every crop is labelled
}The asset must be declared in config.assets with its intrinsic pixel dimensions, because the crop engine needs them to fill the tile at any aspect ratio without letterboxing. The four crop numbers are fractions of that intrinsic image, top-left origin.
The crop math is exact, not a CSS background-position approximation. It earns its own page: the crop model.
palette
A set of colors, rendered as swatches. Use it to decide a color system — which palette carries the brand, how restrained the accent is.
{
kind: "palette",
colors: ["#0c1a1c", "#11272b", "#8499ab", "#2fd4c4"], // 1..8 colors
over: "primary", // optional: a backdrop asset the swatches sit over
}With no over asset, the swatches render as a clean full-tile grid. With one, they sit as a row over a crop of that asset.
type
A real type specimen — an actual font family at an actual weight, rendering a sample string. Use it to decide a headline voice or a system's type personality.
{
kind: "type",
family: "Bricolage Grotesque, system-ui, sans-serif", // any CSS font-family
weight: 600, // optional, 100..900
sample: "Lucid futures, built.",
kicker: "Display / Sans", // optional small label above the sample
}The family is rendered literally, so load the font in your app (or the artifact) for an exact specimen. The type group also supplies the composed preview's headline by default.
motion
A motion feel, chosen from a fixed, safe preset enum — breathe, pan, or field. The presets are deliberately a closed set: an agent-authored config can pick a feel but cannot inject arbitrary CSS or scripts.
{
kind: "motion",
preset: "breathe", // "breathe" | "pan" | "field"
asset: "board", // optional image the motion plays over
crop: { x: 0, y: 0, w: 1, h: 1 },
asset2: "board2", // optional second image (used by "pan")
crop2: { x: 0.1, y: 0, w: 0.9, h: 1 },
}Asset-free, the presets render as motion-framed placeholders — the feel, not a photo. All presets honor prefers-reduced-motion.
layoutMock
A clean wireframe diagram of a layout rhythm, chosen from a plan enum — portrait, threshold, sparse, chapters, belowfold, stack. Use it to decide structure before any imagery exists: how the first screen opens, how a run is drawn, how dense a surface is.
{
kind: "layoutMock",
plan: "portrait", // one of the six plans
asset: "board", // optional: fill a region of the mock with a real crop
crop: { x: 0, y: 0, w: 0.31, h: 0.66 },
asset2: "board2", // optional second region (threshold / stack)
crop2: { x: 0.25, y: 0.78, w: 0.38, h: 0.22 },
}With no asset, the mock is a pure wireframe — perfect for locking layout rhythm with zero commitment to art. With assets, regions of the mock fill with real crops.
At a glance
| Kind | Decides | Needs an asset? | Key fields |
|---|---|---|---|
imageCrop | which part of an image is the hero | yes | asset, crop, alt |
palette | the color system | no (optional over) | colors[] |
type | the type voice | no | family, sample |
motion | the motion feel | no (optional) | preset |
layoutMock | the layout rhythm | no (optional) | plan |
motion presets and layoutMock plans are fixed safe enums; the renderer never evaluates author-supplied CSS or scripts. All author and agent text is HTML-escaped, and image URLs pass a safeUrl allowlist (http(s) and relative only). See loupe-core.