Nana Adjei‑Bonsu Accra Prime Properties

Build Guide — How this site was made

Listing houses as drawings

Accra Prime Properties sells trust, and it does it without a single photograph: four residences issued as architectural plates — measured floor plans, dusk elevations, honest arithmetic. This page explains every technique generously enough that you could rebuild it.

Concept & creative direction

Property sites lead with wide-angle photographs because photographs flatter. The brief inverted that instinct: a top agent whose whole register is trust should present homes the way an architect hands them over — as drawings you can measure. That one decision produced the entire system:

  • Estate-brochure structure. Wide margins, plates numbered I–IV with centred rules, engraved serif capitals (Marcellus — genuinely Trajan-flavoured roman capitals) over Mulish for running text and UI.
  • A four-colour law. Deep navy #132A3E carries authority, warm white #FAF7F1 is the paper, brass #A5854B is rationed to hairlines, seals and prices, and the rose #C97B63 exists only inside the dusk gradient — it never appears as a flat UI colour.
  • Double-ruled frames everywhere. Every card is a border plus an offset outline — the twin gold hairline of a letterpress brochure plate, for one line of CSS.

Toolchain

Designed and engineered by Fable 5 (Anthropic's model) working as designer-engineer, directed by Hannah Kwakye — the art direction, the taste and the final cut are hers. The stack is deliberately plain:

  • Hand-authored static HTML, CSS and vanilla JavaScript. No framework, no build step, zero libraries — IntersectionObserver, pointer events and a little date maths cover everything.
  • Three self-hosted woff2 files (Marcellus, Mulish upright + italic — Mulish is a variable font, so one file carries weights 200–1000), preloaded, font-display: swap. No request ever leaves the origin.
  • Every visual — the skyline, the seal, four floor plans, four elevations, three charts — is inline SVG or pure CSS, so the identity ships inside the documents. First view is roughly 190 KB before compression, fonts included.

Drawing the floor plans

The four sheets (A-101 to A-104) are hand-plotted SVGs, each on a plain rectangular grid so wall runs land on round numbers. The drawing system separates meaning from ink:

  • A <g class="rooms"> layer holds one group per room — an invisible hit-rectangle, the room's label, and its data (data-name, data-area, data-note). This layer is fully interactive.
  • A <g class="walls"> layer above it carries the architecture as four stroke classes and is pointer-events: none, so ink never blocks a tap: .wall (5px structural), .inner (1.8px partitions), .door (brass quarter-circle swing arcs), .win (3.5px brass sills). Outdoor space is a dashed outline.
  • Door swings are the detail that makes plans read as architecture rather than diagram: each is a move-line plus an arc with the radius equal to the leaf width — M370 450h40 M370 450a40 40 0 0 1 40-40.
  • Each sheet ends with a title strip, a north arrow and a true scale bar — drawn, like everything else, from the same brass-and-navy stroke kit.

Room areas are consistent with each listing's quoted interior area, and the sheets carry the caveat every real plan should: drawn to survey dimensions, simplified for clarity.

The plan-room explorer

The signature interaction is deliberately quiet: hover, tap or Tab onto any room and a sticky side panel fills with its name, measured area and a first-person viewing note. The mechanics:

room.setAttribute('aria-label',
  name + ', ' + area + '. Activate for viewing notes.');
room.addEventListener('mouseenter', show);
room.addEventListener('focus', show);       // keyboard = mouse
room.addEventListener('click', toggleActive);
room.addEventListener('keydown', e => {     // Enter/Space, like a button
  if (e.key === 'Enter' || e.key === ' ') toggleActive(e);
});
  • Every room is an SVG <g tabindex="0" role="button">, so the keyboard order walks the plan room-by-room in drawing order, and screen readers hear "Living Room, 47 m²" instead of silence.
  • Clicking pins a room (dashed brass outline) so the note survives the cursor leaving; the info panel is aria-live="polite", announcing each room as it changes.
  • The four sheets sit behind a proper role="tablist" with roving tabindex and arrow-key navigation; each listing plate carries an "Open sheet" button that selects the right tab and scrolls to it.
  • The trick that keeps it robust: the walls layer sits above the rooms in paint order but has pointer-events: none — crisp architectural ink, zero dead zones.

Dusk elevations & the skyline

The romance lives in one reusable gradient — navy falling through violet into rose, the fifteen minutes after an Accra sunset:

--dusk: linear-gradient(180deg,
  #0A1826 0%, #132A3E 34%,
  #3E3A50 58%, #8A5560 76%, #C97B63 100%);

Each elevation card layers a building silhouette (near-black #0A1723 fill) over that sky, then does three things that make it read as an architect's dusk drawing rather than clip-art: lit windows in two brass intensities (occupied vs. ambient), a one-pixel brass ground line, and a real dimension line with a level mark (+9.60) in draughting caps. The hero skyline is the same recipe at city scale — two silhouette depths, Independence Arch and its star drawn as landmarks, a construction crane for honesty, and a starfield built from stacked radial-gradient dots that breathes on an 7s opacity loop (killed under reduced motion).

The affordability sketchpad

Four range inputs feed the standard amortisation formula, and every output is recomputed on input — no submit button, no debounce needed at this arithmetic weight:

const r = rate / 100 / 12,  n = years * 12;
const monthly = principal * r / (1 - Math.pow(1 + r, -n));

Two design decisions matter more than the maths. First, the label is the ethics: the panel is titled a "sketchpad", the copy says "illustrative arithmetic only — not an offer, a quote, or financial advice", and the fine print names what's excluded (fees, insurance, rate changes). Second, the principal-vs-interest split renders as a two-segment proportion bar with a 2px gap and direct percentage labels — at Ghanaian cedi rates the interest share is startling, and showing it plainly is exactly the honesty the brand claims.

Chart craft, and what the validator said

The market-insight charts follow a strict dataviz discipline. The brand's three inks were run through a palette validator before any chart was drawn, and it failed them as a categorical set — brass and rose sit only ΔE 7.9 apart under deuteranopia. The correct response was not "better colors" but a different color job: none of these charts needs categorical color at all.

  • All three are single-series magnitude/trend forms — horizontal bars, one line, a dot plot — so every mark is navy, with brass reserved for hover and the terminal emphasis dot.
  • Bars get flat baselines and 4px-rounded data ends; the line is 2.5px with 8px+ hover targets; category labels are direct, values are direct, the grid is recessive.
  • A shared tooltip follows the cursor via one delegated data-tip handler, and the same figures ship as a real <table> inside a <details> — the accessibility escape hatch and the "show your working" gesture in one.
  • Every chart is captioned illustrative concept data — a fictional agent must not publish fake market research as fact.

The viewing calendar

The booking form is Netlify Forms with a honeypot — but the date field is the brochure moment: a hand-rolled month grid, Monday-first (lead = (firstDay + 6) % 7), navigable three months ahead, with past days and Sundays disabled ("Sundays are for rest"). Each day is a real <button> whose aria-label is the full date; choosing one writes "Saturday 18 July 2026" into a readonly text input, so what the visitor sees is exactly what the form submits. No JS, and the field degrades to a plain typeable input.

Iteration log

Three passes, screenshots read at mobile, tablet and desktop, findings acted on:

Pass 1 — Design critique

  • The hero skyline was colliding with the stats row and scroll cue — buildings growing through "214 keys". Reserved a bottom band for the drawing (padding-bottom: clamp(200px, 36vh, 400px)) so type and city never share pixels.
  • The seal's motto was a single overflowing ring of textPath that crossed the inner circles and started mid-word. Rebuilt as a proper engraver's seal: two centred arcs (name over the top, "EST · MMXIV · ACCRA" under the bottom) with brass diamonds at the compass points.
  • Independence Arch read as a tombstone with a mismatched grey infill. Redrawn as one silhouette with a real fill-rule="evenodd" opening — the dusk sky now shows through the arch — and the black star set on a pedestal above.
  • The price-index chart's "100" label collided with the first data point: a CSS text-anchor: middle rule was silently beating the SVG's text-anchor="end" attribute (CSS outranks presentation attributes). The y-labels got their own end-anchored class.

Pass 2 — Elevation

  • The Meridian tower elevation was a squat block with floating "hedge" smudges. Redrawn as a true fourteen-storey shaft — podium, two neighbour massings, ten floor lines, two-intensity windows, only the crown lit — and the hedge marks were deleted from all four plates.
  • Deepened the plan room: clicking now pins a room with a dashed brass survey outline so notes persist while you compare rooms, and the plates' "Open sheet A-10x" buttons drive the tablist remotely.
  • Draughting-detail sweep: Saltpond Row's lit window no longer overlaps the front-door leaf, the villa's kitchen island moved off its room label, and every elevation kept a dimension line and level mark (+9.60, Floor 14) as the plate's quiet authority.
  • The sketchpad's monthly figure was wrapping "/ month" onto its own line at desktop; recut as a large value with a muted suffix so the headline number stays one unbroken gesture.

Pass 3 — Ship quality

  • All three routes screenshotted at three widths with zero console errors; every anchor, tab (pointer and arrow keys), room (pointer and keyboard), slider, chip, calendar day and the Netlify form's honeypot + ?success panel exercised in a scripted browser run.
  • Caught a specificity bug: Nana's brass signature was silently inheriting body ink because .about-lede p outranked .about-sign. Reduced-motion re-verified: reveals appear in place, the starfield and seal hold still, smooth scroll falls back to instant, and the plan room stays fully usable.
  • Fonts confirmed self-hosted (three woff2, ~77 KB total) with zero external requests anywhere; GH¢ prices proofread against prime-Accra plausibility; every fictional-persona disclosure checked on page, in the footer and in the process study.

Deploy

Static files on Netlify, deployed CI-style from the collection's repository. netlify.toml publishes the site root, sets immutable year-long caching on /assets/*, and adds the standard hardening headers. The booking form is Netlify Forms — plain HTML with a honeypot, no JavaScript required to submit.

This is one of 26 sites in Hannah Kwakye's collection, whose shared thesis is that AI doesn't replace taste — it amplifies the designer who has it. Read the design process, or return to the site.