Brand StandardsConcept 1 · Draft for review

Chapters

Build Standards

The rules the website must be built to. Internal: visitors never see this chapter’s ideas, only their results.

9.1

Accessibility: WCAG 2.2 AA

A launch requirement, tested on the search and listing flows first because they are the hardest.

  • KeyboardEvery control reachable and operable in a logical order; visible 2 px focus everywhere (2.4.7, 2.4.11). Skip link on every page.
  • Dialogs and sheetsNative <dialog> where possible; focus moves in, is contained, Escape closes, focus returns to the trigger.
  • SearchCombobox pattern for location suggestions; result-count changes announced with a polite live region; the list is the accessible equivalent of the map.
  • FormsVisible labels, autocomplete tokens, errors linked with aria-describedby and summarised at the top on submit (3.3.1–3.3.3). No CAPTCHA puzzles (3.3.8).
  • TargetsAt least 44 × 44 px; never below the 24 × 24 px minimum (2.5.8). Dragging on the map always has a non-drag alternative (2.5.7).
  • ContrastPairs from 3.6 only. Text on photographs only over a solid field.
  • MediaListing photos get alt text from room and feature data (“Kitchen with gas range and window over the sink”), never “image 1.” Virtual tours offer a text summary.
  • MotionReduced-motion respected (8.5); nothing flashes; no auto-advancing content.
9.2

Performance budget

Measured at the 75th percentile on a mid-range phone over 4G.

MetricBudgetHow
Largest Contentful Paint≤ 2.0 sHero image preloaded with fetchpriority="high", AVIF/WebP sources, server-rendered listing data
Interaction to Next Paint≤ 150 msNo framework on content pages; search JS split and deferred
Cumulative Layout Shift≤ 0.05Fixed aspect ratios on every image and map; fonts with metric-matched fallbacks
Fonts≤ 130 KBThree WOFF2 files, Latin subset, self-hosted, font-display: swap
JavaScript, content pages≤ 30 KBProgressive enhancement; every page usable without it
JavaScript, search≤ 180 KBMap library loaded on interaction or when the map view is chosen
9.3

Platform layers

Built for REWAP, separated as if it were the first of many brokerages. Each layer can change without touching the others; none of this appears in the interface.

LayerOwnsNever knows about
BrandTokens, fonts, logo set, theme.json stylesAny brokerage’s data
ContentPages, guides, community pages, market updates (block patterns)Which IDX vendor is active
BrokerageLegal name, license, offices, disclosures, principal brokerPresentation
PeopleBrokers and agents: license, role, contact, team membershipListing feed internals
TeamsTeam name, own mark, members, routing preferencesBrand tokens (teams keep theirs)
PropertyNormalised listing records (9.4), media, open housesVendor field names
IDX adapterFetching, mapping and caching one vendor’s feed into the Property schemaTemplates
Lead routingRules: listing agent → team → brokerage fallback; round robin; office hoursCRM specifics
CRM adapterPushing leads and events to the chosen CRMPage design
AnalyticsConsent-aware events: search, save, contact, callPersonal data beyond consent
ComplianceDisclosures, Fair Housing, MLS attribution and display rules, privacy, cookie consentNothing: every template must include it
9.4

IDX adapter contract

Templates read only this shape. A new vendor means one new adapter class, not new templates. Field names follow the RESO Data Dictionary where one exists.

interface ListingProvider {
  search(query: ListingQuery): Promise<Page<Listing>>;
  get(listingKey: string): Promise<Listing | null>;
  openHouses(range: DateRange, area?: AreaRef): Promise<OpenHouse[]>;
  areas(prefix: string): Promise<AreaRef[]>;       // towns, neighborhoods, ZIPs
  attribution(listing: Listing): Attribution;     // MLS + listing office text, logo rules
  freshness(): Promise<{ updatedAt: string }>;    // shown on every results view
}

type Listing = {
  listingKey: string; mlsNumber: string;
  status: 'Active' | 'ActiveUnderContract' | 'Pending' | 'Closed' | 'ComingSoon';
  listPrice: number; closePrice?: number;
  address: { street: string; unit?: string; city: string; state: 'MA'; postalCode: string };
  geo?: { lat: number; lng: number };
  propertyType: 'Residential' | 'MultiFamily' | 'Condo' | 'Land' | 'Commercial';
  bedrooms?: number; bathroomsTotal?: number; livingArea?: number; lotSizeAcres?: number;
  yearBuilt?: number; units?: number; taxAnnual?: { amount: number; year: number };
  media: { url: string; width: number; height: number; caption?: string }[];
  remarks?: string;
  listOffice: { name: string; id: string }; listAgent?: { name: string; id: string };
  isOwnListing: boolean;   // true when listOffice is REWAP or an affiliated team
  updatedAt: string;
};
9.5

Brokerage data model

Affiliation is a relationship, not a hierarchy baked into URLs, so a team can move or a second brokerage can join without rewriting pages.

Entity relationships A brokerage has many offices, brokers and teams. Brokers and agents are people with roles. A team belongs to one brokerage and has many members. Listings reference a listing person and optionally a team, and sit in communities. Brokerage Team Person Listing Community Lead has teamsemploysmembers, rolelisted bylisted bylocated inroutes to
Entity relationshipsPerson roles: principal broker, broker, agent, staff. Brokerage → The Oberdorfer Group → Brandon Oberdorfer, Broker.
9.6

URLs and search visibility

Pages exist to answer real questions people ask about Massachusetts real estate, for search engines and answer engines alike. Value before keywords.

PathPageAnswers
/buy/Buying with REWAPHow buying works in Massachusetts, step by step, with the legal milestones
/sell/Selling with REWAPPricing, preparation, the P&S, closing
/invest/Investment adviceMulti-family, rent rolls, financing structures
/search/Property searchMap and list; filters reflected in the query string
/homes/{town}-ma/{address-slug}-{mls}/Listing detailStable even if status changes; sold listings follow MLS display rules
/communities/{region}/{town}/Community guideHousing stock, transit, schools data with sources, market snapshot
/market/{yyyy}/{mm}/Market updateMonthly figures with method and source
/guides/{slug}/GuidesBuying a home in Worcester, selling in Massachusetts, contingencies explained
/teams/{slug}/ · /people/{slug}/Teams and peopleWho they are, under which brokerage, their listings
  • Answer-readyEach guide opens with a two-sentence answer, then the detail. Questions as headings where natural; FAQ blocks only where questions are real.
  • SourcedEvery market figure carries its source and date in the caption. Generative engines cite pages that cite.
  • Equal regionsCommunity coverage is planned by region, so no part of the state is thin.
9.7

Structured data

Schema.org JSON-LD generated from the data layers, never hand-written per page.

PageTypes
Site-wideRealEstateAgent (the brokerage) with PostalAddress, areaServed = Massachusetts; WebSite with SearchAction
Person, teamPerson with worksFor; team as Organization with parentOrganization
ListingSingleFamilyResidence / Apartment / House with Offer (price, availability), Place, ImageObject
CommunityPlace with containedInPlace; BreadcrumbList
Guide, market updateArticle with author, dates, citation; FAQPage only for real Q&A
{
  "@context": "https://schema.org",
  "@type": "RealEstateAgent",
  "name": "REWAP Brokerage LLC",
  "url": "https://rewapbrokerage.com/",
  "telephone": "+1-508-509-7759",
  "address": { "@type": "PostalAddress", "streetAddress": "652 Park Ave",
               "addressLocality": "Worcester", "addressRegion": "MA", "postalCode": "01603", "addressCountry": "US" },
  "areaServed": { "@type": "State", "name": "Massachusetts" },
  "founder": { "@type": "Person", "name": "Hong Tran", "jobTitle": "Founder, Real Estate Lawyer" },
  "subOrganization": { "@type": "Organization", "name": "The Oberdorfer Group" }
}
9.8

WordPress translation

The production site is a block theme. This book maps onto it directly, with native features first and the fewest plugins possible.

  • theme.jsonPalette, type scale, spacing and layout widths from tokens/theme.json (draft). Editors pick only from the palette.
  • PatternsEach composition in 6.4 and component in chapter 7 becomes a locked block pattern: Index, Plate, Statement, Catalogue, Ledger, CTA.
  • Content typesCustom post types for Person, Team, Community and Market Update; taxonomies for Region and Town. Listings stay in the IDX layer, cached, not as posts.
  • IDXThe vendor plugin supplies data only; its widgets are not used. A small REWAP plugin implements the adapter (9.4) and renders with the theme’s blocks.
  • HostingCloudways, with page caching for content and short-lived object caching for listing queries.
9.9

Compliance surfaces

Designed in from the start, styled as content (7.10), and reviewed by Hong Tran before launch. This list is a checklist, not legal advice.

  • Agency disclosureThe Massachusetts Mandatory Licensee-Consumer Relationship Disclosure, linked from every contact point.
  • Fair HousingEqual Housing Opportunity statement and logo in the footer; language rules in 1.9 and 5.1.
  • MLS display rulesListing office attribution, data-currency notice and disclaimer on every listing view, per the MLS’s IDX rules.
  • Brokerage identificationLegal name and office address on every page.
  • PrivacyPrivacy policy, consent before non-essential analytics, clear opt-in for listing alerts and texts.
  • Accessibility statementPublished with a contact route for problems.