A specimen is a component boxed in a documentation column. A stage is the same component with a real page around it, in its own document. Some components can only be judged — or even rendered — the second way.
Because some components leave the flow, and a div cannot contain that.
A fixed navbar pins itself to the viewport: dropped
among the specimens it would float over the documentation you are
reading. An overlay navbar is transparent and positioned
over what follows it — inline, it has nothing to sit on and looks broken
rather than intentional.
In an iframe the viewport is the stage, so both behave exactly as they will on a real page. No containment tricks, and no CSS crossing in either direction.
Every example already has one. The Full page ↗ link beside a specimen's name opens that example as a document at:
/__stage/<layer>/<partial-id>/<example-index>
Nothing is declared to get it and no file is written. The URL is derived
from the partial's own path and the example's position, so adding an
example adds its stage. The __ prefix follows
/__preview and /__reload: a real URL that is
not a page.
Generated stages put the component on a plain page. When the backdrop is the demonstration — an overlay bar needs a hero to float on — write the page and point the example at it:
# partials/chrome/navbar.ejs
examples:
- name: Overlay — transparent, over a hero
stage: /__stage/navbar-overlay # a page under pages/__stage/
height: 460 # the frame; the docs cannot measure inside it
props: { overlay: true } # shown as demo data, not rendered inline
An example with stage: is embedded as a lazy iframe instead
of rendered among the specimens, and links out to the page itself and to
the preview harness. Hand-written stages live at
pages/__stage/*.ejs — two path segments, so they never
collide with the three-segment generated ones.
Both kinds render through templates/preview-stage.ejs: a
full document with the head, the component, and optional filler.
<%- require('/templates/preview-stage.ejs')({
meta_title: 'Navbar — overlay | stage',
filler: 3, // paragraphs of page underneath; 0 for none
content: navbar({ overlay: true, /* … */ }) + hero({ /* … */ }),
above: '', // markup before the subject (content above a footer)
below: '', // markup after it
}) %> Filler is deliberately plain — the component is the subject, and anything characterful there competes with it. Generated stages pick an amount by layer: chrome gets more, because a bar needs something to pass over.
noindex. They are fixtures, not
content, and should never rank against a real page.
/__preview?path= for viewport and palette switching.
dev/ needs a restart. The watcher
covers partials/, templates/,
pages/ and assets/ — a new route in
dev/server.mjs will 404 until the server is bounced.