Designer Handbook
Everything you need to design on Tortus — the workflow, token reference, compliance rules, and how to hand off your work.
If you've built custom sites before (Figma → hand-coded front-end, or a headless CMS like Sanity), you already know how to work on Tortus. You hand-write each section in HTML and CSS; we host it and render the regulated real-estate modules (MLS search, live listings, valuations, lead capture) inside your design as audited, compliant blocks. We also run the content behind them: blogs, market reports, and the team roster.
The core rule
You own the chrome. We own the listing modules.
- Yours: every layout, type choice, color, image, animation, section, page. 100% bespoke. Hand-write the HTML and CSS; we render it faithfully.
- Ours: the actual search results, listing cards, valuation tool, and lead forms. These are governed by the MLS / real-estate boards whose data they show and must render from our components. You don't rebuild them. You drop a token where one goes, and we render the compliant module inside your design.
How it works
Your site is hosted on Tortus, so hosting, SEO, native MLS search, and lead management are built in. You build each section as a Custom Code block in the editor and mix it with our prebuilt blocks.
Quickstart
- In the editor, click Add Block, open the Layout category, and choose Custom Code.
- Paste your section's HTML in one field, its CSS in the other. Your CSS
is automatically scoped to that block, so
.card {}won't leak anywhere else. Write normal selectors; we namespace them for you. - Wherever a live real-estate module belongs, drop a
{{tortus:…}}token. - Repeat per section. Mix Custom Code blocks with our prebuilt ones freely.
- Publish. Your design is live, with native search, lead management, and SEO underneath.
<!-- HTML field -->
<section class="hero">
<h1>Park Slope, reimagined.</h1>
<p>Boutique representation for Brooklyn's brownstone belt.</p>
</section>
<!-- the live MLS search bar renders right here (it posts to the native /search page) -->
{{tortus:search}}
<section class="featured">
<h2>Current exclusives</h2>
{{tortus:listings maxListings=6}}
</section>/* CSS field — auto-scoped to this section */
.hero { padding: 120px 24px; text-align: center; }
.hero h1 { font-family: "Canela", serif; font-size: 64px; letter-spacing: -0.02em; }
.featured { padding: 96px 24px; }That's a fully custom hero, a live MLS search bar, and a live listings grid: your markup, our data, zero compliance risk.
Placing a module: Custom Code vs. Convert to custom
A Tortus module (listings, team, testimonials, …) can go on a page three ways. They are not interchangeable — this trips people up, so read it once:
| How | Custom HTML/CSS? | Config pane — "Pick Manually", filters, sort | Best for |
|---|---|---|---|
| The block — Add Block → its category | No (our layout, your theme) | ✅ Full picker + filters | standard sections; curated / hand-picked content |
Token in a Custom Code block — {{tortus:listings}} | ✅ Your markup wraps it | ❌ None — configure by typing params (columns=3); manual needs listingIds="…" | bespoke chrome around auto / filtered content |
| Convert to custom — on a placed block | ✅ Full HTML and CSS — edit the block's own markup | ✅ Keeps the block's full pane, incl. "Pick Manually" | curated / hand-picked content with bespoke markup + styling |
The "Pick Manually" picker lives on the block, not on a Custom Code token. A
Custom Code block has no config pane, so a token is configured only by the params
you type — great for auto/filtered content, but to hand-pick listings or agents
you'd be typing IDs by hand. To pick from a visual list, use the block's picker
and, if you also want bespoke markup, Convert to custom — which keeps that
picker and opens the block's own markup as an editable HTML box plus a
scoped CSS box (same {{ field }} / {{tortus:…}} / {{#if}} / {{#each}}
grammar as Custom Code; the live data + compliance stay intact via the sealed
tokens). (Convert to custom is neither a token nor a Custom Code block; it's the
same module, unlocked for full HTML and CSS.)
Token reference
A token is {{tortus:NAME}}, optionally with params:
{{tortus:listings columns=4 layout=carousel maxListings=6}}. Params are
key=value, space-separated; quote multi-word values (heading="Our Team").
Booleans are true/false, numbers are bare (columns=4). Every field in the
tables below is a valid inline param. (These configure a token in a Custom Code
block; for the "Pick Manually" picker, see Placing a module above.)
Universal params — work on almost every module
Before the per-token tables, these apply across the board:
| Param | What it does | Notes |
|---|---|---|
showHeader | show/hide the module's built-in header (eyebrow + heading + subheading) | In Custom Code the built-in header is OFF by default — a bare {{tortus:team}} renders just the body. It turns on automatically if you set heading/subheading/eyebrow, or force it with showHeader=true. |
heading | the module's built-in <h2> | omit it and write your own <h2> in your markup instead |
subheading | the line under the heading | empty by default |
eyebrow | small label above the heading | supported on team, blog, testimonials |
backgroundColor | section background role | transparent · surface (your Background) · surface-alt (muted) · primary · secondary · accent · text. Re-skins with the brand. |
backgroundColorCustom | exact hex background | overrides the role, e.g. backgroundColorCustom="#F7F5F1" |
Modules with a call-to-action button (valuation, contact, property,
search, blog, live_market) also accept the button-style params:
buttonStyle (solid/outline/pill/text), buttonRadius (0–40),
buttonColor + buttonColorCustom, buttonTextColor + buttonTextColorCustom,
showButton (true/false).
The "you own the chrome" default. Because Custom Code is fully bespoke, a module token drops in headerless — just the cards / form / calculator, no "Meet Our Team" band on top — so your own section header is the only one on the page. Want ours instead? Pass a
heading(andsubheading), orshowHeader=true.
Listings & lead modules — the regulated ones
Board-reviewed listing/lead markup. Drop the token; don't hand-author it.
{{tortus:listings}} — curated or auto-pulled listing cards.
| Param | Values | Default |
|---|---|---|
layout | grid · carousel | grid |
columns | 2 · 3 · 4 (grid only) | 3 |
maxListings | number | 12 |
selectionMode | auto · manual | auto |
saleType | sale · rental (multi) | sale |
listingStatus | active · pending · closed (multi) | active |
propertyTypes | CONDO · COOP · CONDOP · HOUSE_TOWNHOME · MULTIFAMILY_RENTAL (multi) | all |
neighborhoods · boroughs · cities · addresses | facet filters (multi) | none |
agentId | limit to one agent's listings | none |
sortOrder | default · price_desc · price_asc · beds_desc · relevance | default |
fallbackMode | recent_sold · none (when no matches) | recent_sold |
listingIds | pick exact listings (with selectionMode=manual) | none |
showPrice · showDetails | true/false | true |
| status chip colors | chipColorInContract · chipColorSold · chipColorForRent · chipColorRented (+ …Custom hex) | brand roles |
{{tortus:valuation}} — "what's my home worth" estimate + lead capture.
⏳ Coming soon — not live yet; the token and options may still change, so we'll
document them when it ships. Coming soon →
{{tortus:contact}} — a lead-capture form → the broker's lead inbox.
| Param | Values | Default |
|---|---|---|
formType | contact · buyer · seller · newsletter | contact |
layout | centered · split (form + image) | centered |
imageUrl | image for split layout | none |
showNameField · showEmailField · showPhoneField · showMessageField | true/false | true |
showBudgetField · showTimelineField | true/false | false |
…FieldRequired (name/email/phone/message/budget/timeline) | true/false | name+email true |
submitButtonText | text | "Send Message" |
successMessage | text | "Thank you. We will be in touch shortly." |
formType reshapes the form: buyer adds budget + timeline and requires phone;
seller adds a required "Property Address" field; newsletter collapses to
email-only. (Custom fields are added in the editor's form settings.)
{{tortus:property}} — a single listing's detail view. Params: listingId,
source (deal/manual/mls), showPrice · showDetails · showDescription
(true), backgroundColor, button-style. Its header is the listing's own
address (data-driven), so there's no heading to set.
Where's the search page? Full MLS search is a native page at
/search, generated automatically for every site, SSR'd and indexable. It is not added to your nav automatically — link to/searchfrom your nav, a button, or a hero CTA wherever you want an entry point. Its look is configured in the editor's search settings (result-card style, brand, filters) rather than hand-authored in markup, so the results stay compliant and consistent. To drop a search bar inside a custom section, use the{{tortus:search}}token (below).
Backend-fed display modules — the broker's own content, live
These pull the broker's own live data and render it in your section. Never paste stale content.
{{tortus:team}} — the agent / team roster.
| Param | Values | Default |
|---|---|---|
columns | 2 · 3 · 4 | 3 |
selectionMode | auto (whole roster) · manual | auto |
teamMemberIds | pick agents (manual mode) | none |
maxMembers | number (auto mode) | 12 |
showTitle · showBio · showContact · showSocial | true/false | true |
showLicense | true/false | false |
{{tortus:blog}} — latest posts.
| Param | Values | Default |
|---|---|---|
layout | grid · featured-grid · editorial · list | grid |
columns | 2 · 3 · 4 (grid layouts) | 3 |
cardAspect | wide · video · photo · square · tall | wide |
maxPosts | number (0 = full index) | 6 |
tag | filter to one tag | none |
showTagFilter | true/false | false |
showExcerpt · showAuthor · showCategory · showReadTime · showDate | true/false | true |
ctaText · ctaLink | "view all" button | "View All Posts" → /blog |
{{tortus:testimonials}} — client testimonials & synced reviews.
| Param | Values | Default |
|---|---|---|
layout | carousel · grid | carousel |
carouselPerView | multiple · one | multiple |
cardStyle | card · plain · quote | card |
selectionMode | auto · manual | auto |
source | all · manual · google_reviews · zillow | all |
sortOrder | display_order · newest · rating | display_order |
nameFormat | full · firstLast | full |
showRatings · showImages | true/false | true |
maxTestimonials | number | 10 |
Reviews = testimonials.
{{tortus:reviews}}and{{tortus:testimonials}}read the same data (synced Google/Zillow reviews + your own testimonials all live together).{{tortus:reviews}}is just an alternate skin that adds source badges (Google/Zillow) and an outbound "View" link; it takeslayout(grid/carousel),sourceFilter(all/google_reviews/zillow/manual),showSourceBadges,showRatings,showImages,maxReviews. Prefer{{tortus:testimonials}}unless you specifically want the platform badges.
{{tortus:market}} — your published market-report editions as an insights feed.
| Param | Values | Default |
|---|---|---|
layout | grid · list | grid |
maxItems | number of editions to show | 12 |
reportAccess | both (read on site + gated PDF) · read (on-site only) · pdf (gated download only) | both |
showFeatured | true/false — featured editions as hero cards | true |
showMetrics | true/false — key market metrics on each card | true |
heading · subheading | section copy | "Market Insights" |
orgSlug | pin a specific brokerage's reports (auto-filled from yours) | your org |
{{tortus:market}}vs. the Market system page. This token is an embeddable insights feed you drop into a section you're designing. The full standalone index is the Market system page at/neighborhoods— use that when you want the whole neighborhood-report surface.
{{tortus:instagram}} — the broker's Instagram feed.
| Param | Values | Default |
|---|---|---|
columns | 3 · 4 · 6 | 3 |
limit | number of posts | 12 |
accountId | pick one connected account | org default |
showCaption · showStats · showFollowButton | true/false | true |
Interactive / data-fed tools
The components where the behavior is ours.
{{tortus:search}} — the MLS search bar (posts to the native /search page).
Params: heading ("Find your next home"), placeholder, buttonLabel
("Search"), backgroundColor, button-style.
{{tortus:showings}} — bookable (showing-enabled) listing cards.
⏳ Coming soon — visitor booking isn't live yet; the token and options may still
change, so we'll document them when it ships. Coming soon →
{{tortus:live_market}} — a live per-neighborhood pricing spotlight with a
trend chart. ⏳ Coming soon — not live yet; the token and options may still
change, so we'll document them when it ships. Coming soon →
{{tortus:mortgage}} — interactive mortgage calculator. ⏳ Coming soon —
calculators aren't live yet; the token and options may still change, so we'll
document them when it ships. Coming soon →
(Map, gallery, and FAQ are editor blocks, not tokens — they're props-only, so you configure them in the editor rather than dropping a bare token.)
Display styles at a glance
Which tokens can change their layout, and how:
| Token | Layout options |
|---|---|
listings | grid (2–4 col) · carousel |
blog | grid · featured-grid · editorial · list (+ 5 card aspect ratios) |
testimonials | carousel (one / multiple per view) · grid; card styles card/plain/quote |
reviews | grid · carousel |
market | grid · list |
team · showings | grid (fixed columns) |
instagram | scroll carousel (3/4/6 per row) |
Note on the display modules: they render our card layout for each row, dropped into your section — you fully control the section around them (header, spacing, background, surrounding art). The card markup is now editable too: Convert to custom on the block opens its card/section template as HTML, where
{{tortus:photo}}-style tokens keep the live photo, price, and compliance intact.
If you mistype a token, it stays visible on the page (e.g. {{tortus:tem}}) so
you catch it immediately; it never silently disappears.
Custom navigation header
Building a bespoke nav bar? Convert the header to Custom — the same lossless, reversible path as any other block — and hand‑write its HTML/CSS. (The nav‑style switcher's built‑in starting points are Standard, Centered, and Bold; a fully custom bar comes from Convert to Custom + a chat‑driven rewrite, not a separate "mode.") Inside the converted header a small nav‑specific token set is available (these work only there, and take no params):
| Token | Renders |
|---|---|
{{tortus:logo}} | the brand logo (org branding, or a per-page override) |
{{tortus:nav_links}} | the configured nav items as <a> links |
{{tortus:cta}} | the header CTA button (ctaText / ctaLink, default "Contact" → /contact) |
{{tortus:brand}} | the brand name as text |
{{tortus:menu_toggle}} | the mobile hamburger button (mobile header only — see below) |
The hover sub-menu on the nav reads the --nav-dropdown-* tokens (see the
Design tokens reference), so you can restyle the dropdown surface
without touching the page background.
Mobile header — fully custom, too
By default your custom header is desktop only: below lg it collapses to the
platform's logo + hamburger + drawer. Turn on Custom mobile header in the
header settings to hand-write the mobile (<lg) bar and drawer with your own HTML
- CSS — the same nav tokens work, plus
{{tortus:menu_toggle}}for the hamburger.
The interactive part is CSS-only. When the menu is open, the header carries
data-tortus-nav-open="true" — style your drawer against it, no JavaScript:
<nav class="mn-bar">
<a href="/">{{tortus:logo}}</a>
{{tortus:menu_toggle}}
</nav>
<div class="mn-scrim" data-tortus-nav-scrim></div>
<aside class="mn-drawer">
{{tortus:nav_links}}
{{tortus:cta}}
</aside>.mn-drawer { position:fixed; inset:0 0 0 auto; width:80%; max-width:340px;
background:var(--color-background); transform:translateX(100%);
transition:transform .3s ease; }
[data-tortus-nav-open="true"] .mn-drawer { transform:translateX(0); } /* slide in */
.mn-scrim { position:fixed; inset:0; background:rgba(0,0,0,.4); opacity:0;
pointer-events:none; transition:opacity .3s; }
[data-tortus-nav-open="true"] .mn-scrim { opacity:1; pointer-events:auto; }{{tortus:menu_toggle}}opens/closes the menu; tapping a nav link, the CTA, or any element markeddata-tortus-nav-scrimcloses it (Escape does too).- Style the hamburger via
[data-tortus-nav-toggle]and its[data-tortus-nav-toggle-bar]spans.
System pages
Beyond the pages you build, every site has a set of system pages — routed templates Tortus generates and keeps fed with live data. They appear in the builder's left Pages panel under System Pages, labelled exactly as below. Open one to restyle it: most are block-editable (rearrange, restyle, and Convert to custom like any page), and each per-record detail template nests beneath its index page.
| Page (left-nav) | Route | Detail template | What it is |
|---|---|---|---|
| Search | /search | — | MLS search results. Compliance-locked — style the chrome around it, not the regulated results. |
| Building | /buildings/:slug | — | Per-building page. Compliance-locked. |
| Listings | /properties/:slug | — | Per-listing detail. Compliance-locked. |
| Team | /team | /team/:slug — Team profile | Agent roster + per-agent profile. |
| Blog | /blog | /blog/:slug — Post layout | Your journal index + post layout. |
| Market | /neighborhoods | /neighborhoods/:slug — Report layout | Neighborhood market-report index + full report. |
| Developments | /projects | /projects/:slug — Project layout | Your published new-development projects + per-project detail. |
The IDX trio — Search, Building, Listings — is compliance-locked: you style the section around them, never the regulated card/detail markup itself. Everything else (Team, Blog, Market, Developments) is fully block-editable and convert-to-custom.
Heads-up on Market. The left-nav button reads Market, but its public URL and visitor-facing copy are Neighborhoods (
/neighborhoods) — same page. Don't confuse it with the embeddable{{tortus:market}}insights feed, which is a token you drop into your own section.
Recipes — starting points
The pattern in all of these: your section header + a headerless module token.
You write and style the <h2>; the token drops in just the body. Paste, then
make it yours.
Featured listings under your own header
<section class="feature">
<p class="feature__eyebrow">Now representing</p>
<h2>Current exclusives</h2>
{{tortus:listings maxListings=6 backgroundColor="surface-alt"}}
</section>.feature { text-align: center; padding: 96px 24px 24px; }
.feature__eyebrow { text-transform: uppercase; letter-spacing: 0.18em;
font-size: 13px; color: #8a8a8a; margin: 0 0 12px; }
.feature h2 { font-family: "Canela", serif; font-size: 44px;
letter-spacing: -0.02em; margin: 0; }Meet the team, your layout
<section class="team">
<div class="team__intro">
<h2>The people behind every deal</h2>
<p>Boutique by design — you work with a principal, not a call center.</p>
</div>
{{tortus:team columns=4 showBio=false}}
</section>.team { padding: 112px 24px; }
.team__intro { max-width: 40rem; margin: 0 auto 56px; text-align: center; }
.team__intro h2 { font-family: "Canela", serif; font-size: 40px; margin: 0 0 12px; }
.team__intro p { color: #666; font-size: 18px; margin: 0; }Editorial testimonials band
<section class="voices">
<h2>In their words</h2>
{{tortus:testimonials layout="carousel" carouselPerView="one" cardStyle="quote" backgroundColor="primary"}}
</section>Use our header when you want it
Prefer our built-in, theme-styled header for a module? Just give it copy — the header turns itself back on:
{{tortus:blog heading="From the journal" subheading="Notes on the market" layout="editorial"}}The compliance banner (when session recording is on)
If a broker enables Session Recording (a premium feature that replays how
visitors move through the site), every published page shows a cookie-consent
banner. You don't add it or rebuild it. It appears automatically, and its
chrome is governed by a single Tortus design token, complianceBanner:
- Default look: charcoal ink, a thin mint signal rail along the top, a charcoal "Accept all" button. Clean and trustworthy.
- It adopts the broker's brand accent. The accent color tints the top rail and the primary button, so the banner reads as part of their site, not a generic Tortus chrome.
- The wording is fixed. The privacy + session-replay disclosure is compliance-reviewed. You can't edit the copy, and you don't need to.
- It's consent-gated. Nothing records until a visitor accepts analytics, and password / payment fields are never captured.
It's a small fixed banner pinned bottom-center. You control everything around
it; you don't restyle the banner itself. The token keeps it consistent and
compliant across every broker site. (Token: complianceBanner /
complianceBannerFromBrand() in @tortus/design-tokens.)
Art direction & motion level
Every Tortus site is built against one art direction — a complete aesthetic system (type, layout language, color usage, space, signature moves) the build commits to, so the result is distinctive and cohesive instead of a safe generic default. You pick it when you build (or leave it on the default, Editorial Luxury):
| Direction | Feel |
|---|---|
| Editorial Luxury (default) | Magazine‑grade. Oversized serif display, generous white space, hairline rules, asymmetric grids. |
| Quiet Minimalist | Swiss restraint. Near‑monochrome, precise grid, tight type, almost no ornament. |
| Bold & Confident | Big and unafraid. Huge type, full‑bleed brand‑color blocks, dramatic scale and contrast. |
| Warm Boutique | Inviting and tactile. Softer palette, rounded corners, warm serif/sans mix, gentle shadows. |
| Modern & Sleek | Crisp and tech‑forward. Precise sans, dark moments, subtle gradients and glass, sharp detail. |
Orthogonally, a motion level dials how much the site moves — independent of the look, because parallax taste is personal:
| Motion level | What it does |
|---|---|
| Cinematic (default) | Full parallax and depth — images drift as you scroll. |
| Subtle | A gentle sense of motion, dialed back. |
| Still | No parallax. Calm, flat, and quiet. |
The pick is deterministic: the direction sets the parallax intensity and the
reveal timing, and the motion level scales the parallax (Still → none). Both
land as the --parallax-speed and --tr-dur CSS vars, so any motion you
hand‑write in a Custom Code block automatically matches — see the
design‑token reference. Scroll‑reveals
always play at a tasteful baseline, and prefers-reduced-motion is always
honored.
Motion & loader tokens
Want a preloader, scroll-reveal, parallax, or hover-lift? You don't have to
hand-pick durations and easing curves. Every published page (and the editor
canvas) carries a --motion-* and --loader-* CSS-var namespace you can
animate against from any Custom Code block or converted section. Use them and
your motion matches the platform's feel and stays consistent if a broker
re-brands. (These follow the brand: loader colors reference the brand color
vars, so the spinner is on-brand automatically.)
Motion — durations & easings
| Variable | Default | Use it for |
|---|---|---|
--motion-duration-instant | 0ms | snap states |
--motion-duration-fast | 150ms | hovers, small toggles |
--motion-duration-normal | 300ms | most transitions |
--motion-duration-slow | 500ms | section reveals |
--motion-duration-slower | 800ms | hero / preloader |
--motion-easing-standard | cubic-bezier(0.4, 0, 0.2, 1) | general UI |
--motion-easing-decelerate | cubic-bezier(0, 0, 0.2, 1) | enter / reveal-in |
--motion-easing-accelerate | cubic-bezier(0.4, 0, 1, 1) | exit / fade-out |
--motion-easing-emphasized | cubic-bezier(0.2, 0, 0, 1) | expressive moves |
--motion-easing-spring | cubic-bezier(0.34, 1.56, 0.64, 1) | playful overshoot |
--motion-parallax-depth | 0.15 | parallax translate multiplier |
--motion-scroll-reveal-distance | 24px | how far reveal-in elements rise |
--motion-hover-lift | -4px | translateY on hover |
/* reveal-on-scroll + on-brand hover lift, from a Custom Code block */
.reveal { opacity: 0; transform: translateY(var(--motion-scroll-reveal-distance));
transition: opacity var(--motion-duration-slow) var(--motion-easing-decelerate),
transform var(--motion-duration-slow) var(--motion-easing-decelerate); }
.reveal.in-view { opacity: 1; transform: none; }
.card:hover { transform: translateY(var(--motion-hover-lift));
transition: transform var(--motion-duration-fast) var(--motion-easing-standard); }Loader — the preloader / route spinner
| Variable | Default | Notes |
|---|---|---|
--loader-spinner-color | var(--color-accent) | follows brand accent |
--loader-spinner-track-color | var(--color-border) | faint ring track |
--loader-spinner-size | 40px | |
--loader-spinner-thickness | 3px | ring stroke |
--loader-spinner-duration | 0.8s | one rotation |
--loader-spinner-easing | linear | |
--loader-overlay-color | var(--color-background) | full-screen backdrop |
--loader-overlay-blur | 8px | backdrop blur |
/* a brand-aware page preloader */
.preloader { position: fixed; inset: 0; display: grid; place-items: center;
background: var(--loader-overlay-color); backdrop-filter: blur(var(--loader-overlay-blur)); }
.preloader__spinner { width: var(--loader-spinner-size); height: var(--loader-spinner-size);
border: var(--loader-spinner-thickness) solid var(--loader-spinner-track-color);
border-top-color: var(--loader-spinner-color); border-radius: 50%;
animation: spin var(--loader-spinner-duration) var(--loader-spinner-easing) infinite; }
@keyframes spin { to { transform: rotate(360deg); } }Widget skins
Backend‑fed widgets already follow your brand (backgrounds, type, text read the
--color-* / --font-* tokens). A few small accents are their own tokens so you
can recolor them:
| Variable | Default | Drives |
|---|---|---|
--rating-star-filled | #FBBF24 | filled review stars |
--rating-star-empty | #D1D5DB | empty review stars |
--instagram-overlay | rgba(0, 0, 0, 0.6) | hover scrim over Instagram tiles |
/* brand‑colored review stars */
:root { --rating-star-filled: var(--color-accent); }(The Google / Zillow source badges stay source‑authentic by default so they read as trustworthy; ask us if you want them recolored.)
Want to override any default for a specific site (e.g. a slower, more cinematic
preloader, or brand‑colored stars)? Tell us the values and we set them on the
brand's design_tokens, and every page picks them up. (Source: motion /
loader / widgets groups in @tortus/design-tokens.)
What you can't change (and why)
A short, honest list. None of it limits your design. It's about staying within the rules the MLS boards set for displaying their data. To access an MLS's feed, its listings and the surrounding disclosures have to render a specific way, and a few of these items carry their own compliance requirements on top.
- The listing/search markup itself — render it via the tokens above; don't hand-author listing cards or search results. (MLS IDX/VOW rules.)
- The cookie-consent banner (when session recording is on) — it appears
automatically, its copy is compliance-fixed, and it auto-tints to your brand
accent via the
complianceBannertoken. You don't rebuild or remove it. - The footer's legal strip — the "Powered by Tortus" credit, the brokerage's legal identity + license, the Fair-Housing / SOP / privacy links. Some of this is statutorily required on every public NY broker site (NY RPL §442-h for the SOP link). You can style and arrange the footer, but a small always-present legal line can't be deleted. In the editor, clicking it shows a quiet "managed by Tortus" note instead of selecting it. That's expected.
- Per-listing disclaimers + source attribution — rendered automatically on listings; you don't add or remove them.
Everything else (nav, heroes, galleries, about, team, blog, contact, custom sections, motion, type, color) is yours.
What to send us to get started
So we can stand up your design fast:
- Your Figma (or live URL / inspiration) for the pages in scope.
- Per page, a note on where the real-estate modules go: which page is the search/conversion page, where featured listings sit, where the valuation CTA lives.
- Your fonts (files or Google Fonts names) and brand colors.
We'll get the first page into the editor and share a live preview link you can review and iterate against.
Need something the handbook doesn't cover? Tell us — a lot of what's here started as a designer's request.

