--- version: alpha name: wayfarer-design description: "A warm off-white canvas (#f7f4ef) with charcoal-brown ink (#26221c), a terracotta (#b34e26) for pins and primary action, and a deep green (#33513f) for closed/sold confirmation. Instrument Serif carries every heading at a large, quiet size; Public Sans carries the surrounding prose. The system reads as an actual neighborhood agent's site, specific and local, never a national listings-aggregator portal." --- ## Brand Positioning Wayfarer Realty is for buyers who want to know the actual block, not scroll a national aggregator's infinite grid of thumbnails. It exists because most real-estate sites borrow portal chrome (a giant search-everything hero, "similar homes" carousels, a mortgage-calculator upsell) that has nothing to do with the specific four listings a small local agent is actually showing this week. Wayfarer's bet: the one piece of drama on the page should be pins landing on a real map, one at a time, each becoming a specific card with a specific price, not a filtered list of thousands. Every visual decision below serves that one bet. If a component doesn't help someone understand a specific listing or reach the agent, or doesn't come from a real listings interaction (listing table, status, inquiry form), it doesn't belong in this system. colors: accent: "#b34e26" accent-alt: "#33513f" ink: "#26221c" ink-muted: "#6b6255" canvas: "#f7f4ef" surface: "#fffdf9" surface-raised: "#ece5d8" border: "#ddd3bf" focus-ring: "#b34e26" ## Logo / Wordmark System The mark is a rounded-square card containing a single map pin, literally a 1:1 miniature of the pins that drop onto the homepage's neighborhood map. ```svg ``` - **Lockup**: mark, then a `0.5rem` gap, then the wordmark "Wayfarer" in Instrument Serif, mark always left of the wordmark, never stacked or reversed. - **Minimum size**: mark not smaller than `16px` square, below that the pin's teardrop shape and center hole merge. Wordmark not smaller than `1rem`, Instrument Serif's fine strokes get illegible below that. - **Clearspace**: empty margin equal to the mark's own height (22px at default size) on all four sides of the lockup. - **Don't**: recolor the mark for a dark background, it only ever renders on `--panel`/`--bg`. Don't use a house or a generic real-estate icon, the pin is the brand's whole visual logic, specific location over generic property. Don't animate the mark itself, the drop motion belongs to the homepage's map, not the nav. Don't rotate or stretch it. ## Color System ### Roles | Role | Token | Value | Used for | |---|---|---|---| | Canvas | `--bg` | `#f7f4ef` | Page background, map streets | | Surface | `--panel` | `#fffdf9` | Panels, nav, map blocks, listing cards, table header | | Surface raised | `--surface-raised` | `#ece5d8` | Map background fill, row hover, input focus bg | | Border | `--border` | `#ddd3bf` | Panel edges, dividers, map block outlines, table rules | | Ink | `--ink` | `#26221c` | Primary text | | Ink muted | `--dim` | `#6b6255` | Secondary text, helper text, pending status | | Pin / primary | `--accent` | `#b34e26` | Map pins, primary button fill, new-listing status, offer-expired copy | | Sold / confirmed | `--accent-alt` | `#33513f` | Sold-status text, sold badge, focus color on confirmation states | There is deliberately no separate "urgent" red beyond the terracotta already used for pins. An expired offer window is real information, not an alarm, it's routed to the same terracotta the pins use, just in sentence form, never a flashing banner. ### Contrast (computed from the hex values above via WCAG relative luminance) | Pairing | Ratio | Passes | |---|---|---| | `--ink` text on `--bg` | ~14.4:1 | AAA | | `--dim` text on `--bg` | ~5.5:1 | AA (body text) | | `--accent` text on `--bg` (new status, offer-expired copy) | ~4.7:1 | AA (body text) | | `--accent-alt` text on `--bg` (sold status, badge) | ~8.0:1 | AAA | | Light label on `--accent` fill (primary button) | ~4.7:1 | AA | | `--ink` on `--panel` | ~15.6:1 | AAA | | `--ink` on `--surface-raised` | ~12.6:1 | AAA | The rule this table exists to enforce: `--accent` is tuned to just clear AA at body-text size on `--bg`, any darker and the terracotta pins would stop reading as warm; a button or badge filled with `--accent` always labels itself in a warm off-white (`#fdf8f2`), never `--ink`, dark-on-terracotta undershoots AA at this saturation. typography: display: fontFamily: Instrument Serif fontWeight: 400 body: fontFamily: Public Sans fontWeight: 400 ## Type Scale | Token | Size | Weight | Family | Line-height | Letter-spacing | Used for | |---|---|---|---|---|---|---| | `--text-display` | `clamp(2.75rem, 7vw, 5rem)` | 400 | Instrument Serif | 1.05 | 0 | Hero H1 only | | `--text-h2` | `2rem` (homepage) / `1.75rem` (second page) | 400 | Instrument Serif | 1.15 | 0 | Section headings | | `--text-price` | `1.1rem` | 400 | Instrument Serif | 1.1 | 0 | The price line on a map listing card, the one other place the serif appears | | `--text-body` | `1rem` | 400 | Public Sans | 1.6 | 0 | Paragraph copy | | `--text-small` | `0.85rem` | 400 | Public Sans | 1.5 | 0.01em | Footer, nav utility links, helper text, table cells, listing-card meta | Rule for Instrument Serif vs. Public Sans: the serif, always regular weight, never bold, is reserved for the hero headline, section headings, and a listing's price, the moments meant to feel quietly confident rather than shouted. Everything else, nav, buttons, form labels, table cells, address and bed-count text, is Public Sans, chosen for its civic, address-plate legibility at small sizes. ## Spacing Scale Base unit `8px`. Tokens in use: `--space-2: 0.5rem`, `--space-3: 1rem`, `--space-4: 1.5rem`, `--space-6: 2rem`, `--space-8: 3rem`, `--space-12: 4rem`. ## Shape One radius scale: `--radius: 10px` for panels, map blocks, listing cards, inputs, and table containers. `--radius-pill: 999px` for buttons and the sold badge. The pin's teardrop shape is the one intentional non-rectangular exception. ## Iconography No icon library. The only non-text glyph beyond the pin illustration is one Unicode glyph, `✓` (sold/confirmed), set at the same weight as the text beside it. Listing status is communicated with a plain-language phrase ("New", "Pending", "Sold, Jul 2026") paired with color, never a status icon. If a future page needs more iconography, the fallback is Phosphor (line style, `1.5` stroke), that decision just hasn't been needed yet. ## Motion Principles Scroll drops four pins onto the homepage's neighborhood map, one at a time with a short stagger, each pin falling from above and settling with a small bounce (`bounce.out`), a listing card fading in beside it a beat after it lands. The whole sequence plays once per view (`toggleActions: play none none reverse`), reversible by scrolling back up. This is the only scripted animation on either page, everything else (buttons, table rows, form inputs) uses only `:hover`/`:active`/`:focus-visible` CSS transitions. The whole system honors `prefers-reduced-motion` by showing every pin and listing card fully in place immediately, no drop, no bounce. ### Never-list (written before the impeccable-pass rebuild) 1. No centered hero sitting a full section above the map, the map and its dropping pins share the first viewport with the headline. 2. No two-button CTA row floating free of the map, the primary action sits beside the intro copy that introduces it. 3. No fixed-pixel pin/card positions that only line up at one container width, they're percentage-based so the map scales correctly at any width, a real bug this pass fixed (pins used to render off-screen on narrow viewports). ## Voice & Tone direct, neighborly, no superlatives, concrete details only. In practice: - **Button label**: "Talk to an agent", not "Find Your Dream Home Today!" - **Error message**: "Offer expired. The window on 412 Elm St closed at 5pm.", not "Oh no, you missed it!" - **Empty state**: "No listings match that search right now.", not "No results found, try again?" - **Success status**: "27 Elm St, closed Jul 2026", not "SOLD! 🏡 Another happy homeowner!" ## Accessibility Baseline - Focus ring: `2px solid var(--accent)`, `2px` offset, applied via `:focus-visible` on every interactive element (links, buttons, inputs, the sort select), never removed without a visible replacement. - Reduced motion: the pin-drop and listing-card fade both check `prefers-reduced-motion: reduce` and render every pin and card fully in place with no animation when it's set. - Minimum tap target: `44px` in the smallest dimension for every button and form control on touch viewports. - Color is never the only signal: listing status pairs color with a specific phrase and, for sold listings, a date, the offer-expired state pairs terracotta with a full sentence naming the address, not a bare icon. ## Components ### Navigation Single-line bar, `64px` tall, `--panel` background with a `--border` bottom hairline, sticky to the top. Left: a small "back to gallery" utility link. Center-left: the mark + wordmark lockup. Right: a Listings link plus one primary button. Collapses to just the lockup and primary button under `480px`, no hamburger, there are only two destinations. ### Buttons Three variants, one radius (`--radius-pill`), one primary accent: - **Primary**: `--accent` fill, light label, used once per view for the single real action ("Talk to an agent"). - **Secondary**: transparent fill, `1px solid --border`, `--ink` label. "See similar listings" and similar secondary actions, form submits that aren't the primary conversion. - **Ghost**: text-only, `--dim` label, `--ink` on hover. Tertiary actions like "see what's on the block." All variants define `:hover` (brightness/border shift), `:active` (`transform: scale(0.98)`), `:disabled` (`opacity: .4`, `pointer-events: none`), and `:focus-visible` (accent outline). ### Neighborhood map A flat SVG of stylized city blocks (`--panel` fill, `--border` outline) over a `--surface-raised` ground with `--bg`-colored streets, overlaid with absolutely-positioned pin markers and listing cards. Purely illustrative at this scale, not a real interactive map, exists to make "know the block" literal. ### Listing cards (map) A small `.panel`-style card with a serif price line and a `--dim` meta line (bed count, cross streets). Appears only after its pin lands, never floating independent of a pin. ### Table Header row in `--panel`/`--dim` labels, body rows in `--bg`, one `--border` rule between rows, row hover to `--surface-raised`. Status cells are bold text colored by phase (`--accent` for new, `--dim` for pending, `--accent-alt` for sold), never a bare dot. ### Forms Label above input, `--text-small`/`--dim`. Input: `--panel` background, `1px solid --border`, `--radius`, `--ink` text, `--dim` placeholder. On `:focus`, border becomes `--accent` and background lifts to `--surface-raised`. On error, border stays `--accent` (the system's only accent) and the helper text explains what to fix in plain language. ### Status / feedback states - **Loading**: a skeleton shaped like the real content (bars matching text line-heights), not a spinner. - **Empty**: a plain sentence in `--dim`, no illustration. - **Error**: an inline `--accent` message naming the specific address, with a concrete next step, never a silent failure. - **Success / status badge**: `--radius-pill`, `1px solid --accent-alt`, `--accent-alt` text on transparent fill, not filled, filled would compete with the primary button's exclusive use of a solid `--accent` fill. ### Footer One row: agency name/tagline on the left, real page links on the right (Listings, back to gallery). No invented legal or brokerage-license text. ## Do's and Don'ts ### Do - Let the pin-drop sequence be the only scroll-triggered element on the page, everything else reacts only to direct interaction. - Reserve `--accent` for pins/primary-action meaning and `--accent-alt` for sold/confirmed meaning, keep that split consistent everywhere. - Keep listing copy to real, specific facts (address, price, bed count, cross streets), never vague marketing adjectives like "charming" or "cozy." - Use a light label on any `--accent`-filled surface. - Show a listing card only attached to a landed pin, never floating free of a specific location. ### Don't - Don't add a "search everything nationwide" mega-hero or a mortgage-calculator upsell, this is a small local agent's site, not a portal. - Don't introduce a separate red for urgency, route offer-expired and similar states to `--accent` with specific copy instead. - Don't replay the pin-drop sequence on every scroll pass, reveal once per view, reversible only by scrolling back up. - Don't stack more than one primary button in the same view. - Don't fill a status badge solid, badges stay outlined so the primary button remains the only solid-`--accent`-fill element on screen. ## Known Constraints - Two routes now exist (home, listings), still no real interactive map or saved-search account, no responsive breakpoint table beyond a single-column mobile collapse that hides the map listing cards, no authenticated buyer login (the "Talk to an agent" flow and inquiry form are not wired to anything real, this is a concept demo).