Pick a scenario for a fixture
Source a fixture's slots from real property data, by the fill-by-source rule.
Fill a section’s fixtures.ts from the kit’s golden scenarios instead of inventing
values, so preview stays honest and every fixture is grounded in a real,
self-consistent property.
The delta
Section titled “The delta”Pick a scenario
Section titled “Pick a scenario”Import scenarios from the fixtures entry — never the root barrel; dev-only sample
data must never reach a published renderer bundle’s import graph — and pick the one
whose breadth matches the state you’re building:
import { scenarios } from "@homepages/template-kit/fixtures";
const luxury = scenarios.luxuryMultiUnit; // 12 units, 3 contacts, both logos, video + 3D tourconst sparse = scenarios.sparseSingleFamily; // 1 unit, 1 contact, no logos, no mediaconst rent = scenarios.forRentCondo; // 1 unit, 2 contacts, both logos, video onlybase almost always builds on luxuryMultiUnit — it’s the breadth ceiling, so a
layout that survives it survives everything smaller. Reach for sparseSingleFamily
or forRentCondo for a states entry designed to look
different against a thinner property.
| Scenario | units | listing_intent | property_type | contacts | pois | photos | contact logos | media |
|---|---|---|---|---|---|---|---|---|
luxuryMultiUnit |
12 | for_sale | multi_family | 3 | 24 | 12 | both logos | video + 3D tour |
sparseSingleFamily |
1 | for_sale | single_family | 1 | 4 | 3 (reused from forRentCondo) |
none (null) | none (null) |
forRentCondo |
1 | for_rent | condo_townhome | 2 | 8 | 8 | both logos | video only |
Fill every slot by its fill-spec source
Section titled “Fill every slot by its fill-spec source”Don’t invent a value. Every slot in schema.ts already declares how it’s filled in
production — fixtures.ts mirrors that:
| Slot’s fill-spec source | How the fixture fills it |
|---|---|
a fact (fact.<container>.<field>) |
read it off the scenario — s.facts.address.display, primaryContact(s).fields.phone |
derived() |
a helper — propertyMetrics(s), poisByCategory(s), contactCards(s) |
| ai | a short sample fill grounded in the scenario’s facts — a headline about this property, never lorem ipsum or invented facts |
A fixture that gets this wrong is worse than a missing one: a slot bound
fact.unit.beds whose fixture value doesn’t match s.facts.units[0].beds teaches
nothing about the layout it’s supposed to preview.
The helper cheat-sheet
Section titled “The helper cheat-sheet”import { contactCards, poisByCategory, primaryContact, propertyMetrics, scenarios,} from "@homepages/template-kit/fixtures";
const s = scenarios.luxuryMultiUnit;| Helper | Returns |
|---|---|
primaryContact(s) |
The scenario’s primary contact (ContactFixture, position 0 by construction) — throws if the scenario has none |
contactCards(s) |
Every attached contact, in order (ContactFixture[]) |
poisByCategory(s) |
The scenario’s POIs bucketed by category (Record<PoiFixture["category"], PoiFixture[]>) |
propertyMetrics(s) |
beds/baths/sqft/price off the lowest-position unit, bedsMin/bedsMax/bathsMin/bathsMax/sqftMax/priceStart across all units, and addressLine1/addressLine2 |
propertyMetrics returns kit fact vocabulary — addressLine1/addressLine2, not a
template’s own slot names, so the kit never learns what a section calls its slots. Map
it to your slots at the call site:
const m = propertyMetrics(s);// ...in base.slots:address_street: m.addressLine1,address_locality: m.addressLine2,Fields the helpers don’t cover are readable straight off the scenario: s.photos,
s.pois, s.amenities, s.contacts, s.media, and s.facts (the raw
PropertyFacts). A contact’s org name and logos (logo_light_id/logo_dark_id) live
on ContactFixture["fields"], not on a sibling “brand” object — read them off
primaryContact(s).fields or s.contacts.
Media comes off the scenario as an id
Section titled “Media comes off the scenario as an id”Every media reference in a scenario is an asset id, the same thing a saved page
holds — s.photos[n].id, primaryContact(s).fields.headshot_id / .logo_light_id /
.logo_dark_id, s.media.video_id, and a floor plan ref’s id. Put the id in the
fixture and stop there: the url, the alt fallback, and the responsive ladder are
attached for you when the fixture loads. Never state a url in a fixture.
(s.media.tour_3d_url is the one exception — a 3D tour is a third-party embed with no
asset behind it.)
Add a scenario-driven state
Section titled “Add a scenario-driven state”A states entry designed to look different against a sparser property sources from a
different scenario, not from overridden literals — see
Preview a section’s structural edge cases
for the full pattern.
Commands
Section titled “Commands”None — this is a pure content edit. No CLI command beyond the author loop below.
Verify
Section titled “Verify”Verify with the author loop.
See also
Section titled “See also”- The schema system —
the
fixtures.tscontract, the scenario breadth table, and the fill-by-source rule. - Preview a section’s structural edge cases —
states, including a scenario-swapped state. - The closed vocabulary — the facts a scenario’s
factssupplies.