← back to Homesonspec
Design system iter-2: reskin /map explorer panel + /search cards & controls from tokens (brand/accent, Fraunces prices, card-interactive)
832a5c0b2eca46816a676dfb4aa2547584c69789 · 2026-07-22 17:31:12 -0700 · Steve
Files touched
M apps/web/src/app/map/MapExplorer.tsxM apps/web/src/app/search/SearchClient.tsx
Diff
commit 832a5c0b2eca46816a676dfb4aa2547584c69789
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jul 22 17:31:12 2026 -0700
Design system iter-2: reskin /map explorer panel + /search cards & controls from tokens (brand/accent, Fraunces prices, card-interactive)
---
apps/web/src/app/map/MapExplorer.tsx | 21 ++++++++++++---------
apps/web/src/app/search/SearchClient.tsx | 32 ++++++++++++++++----------------
2 files changed, 28 insertions(+), 25 deletions(-)
diff --git a/apps/web/src/app/map/MapExplorer.tsx b/apps/web/src/app/map/MapExplorer.tsx
index b7d77cc..dd71aae 100644
--- a/apps/web/src/app/map/MapExplorer.tsx
+++ b/apps/web/src/app/map/MapExplorer.tsx
@@ -204,8 +204,11 @@ export default function MapExplorer() {
{/* Toggle panel */}
<aside className="w-full shrink-0 overflow-y-auto border-b border-neutral-200 bg-white p-4 text-sm lg:w-80 lg:border-b-0 lg:border-r">
<div className="flex items-baseline justify-between">
- <h1 className="text-lg font-bold">Map explorer</h1>
- <span className="text-xs text-neutral-500" data-testid="visible-count">
+ <h1 className="font-display text-lg font-semibold text-brand-900">Map explorer</h1>
+ <span
+ className="rounded-full bg-brand-50 px-2 py-0.5 text-xs font-medium text-brand-700"
+ data-testid="visible-count"
+ >
{loading ? "loading…" : `${visible.length.toLocaleString()} of ${(feed?.total ?? 0).toLocaleString()}`}
</span>
</div>
@@ -213,11 +216,11 @@ export default function MapExplorer() {
{/* Color-by */}
<div className="mt-4">
- <label className="text-xs font-semibold uppercase tracking-wide text-neutral-500">Color markers by</label>
+ <label className="eyebrow">Color markers by</label>
<select
value={colorBy}
onChange={(e) => setColorBy(e.target.value)}
- className="mt-1 w-full rounded border border-neutral-300 px-2 py-1"
+ className="mt-1.5 w-full rounded-xl border-0 bg-white px-3 py-2 text-sm shadow-sm ring-1 ring-inset ring-neutral-200 focus:ring-2 focus:ring-brand-400"
data-testid="color-by"
>
{COLOR_BY.map((c) => (
@@ -271,16 +274,16 @@ function ToggleGroup({
return (
<div className="mt-4 border-t border-neutral-100 pt-3" data-testid={testid}>
<div className="flex items-center justify-between">
- <h3 className="text-xs font-semibold uppercase tracking-wide text-neutral-500">{title}</h3>
- <button className="text-xs text-teal-700 hover:underline" onClick={() => onAll(!allOn)}>
+ <h3 className="eyebrow">{title}</h3>
+ <button className="text-xs font-semibold text-brand-700 hover:text-brand-800" onClick={() => onAll(!allOn)}>
{allOn ? "clear" : "all"}
</button>
</div>
- <div className="mt-1.5 space-y-1">
+ <div className="mt-1.5 space-y-0.5">
{items.map((it) => (
- <label key={it.v} className="flex cursor-pointer items-center justify-between gap-2 rounded px-1 py-0.5 hover:bg-neutral-50">
+ <label key={it.v} className="flex cursor-pointer items-center justify-between gap-2 rounded-lg px-1.5 py-1 hover:bg-brand-50">
<span className="flex items-center gap-2">
- <input type="checkbox" checked={on.has(it.v)} onChange={() => onToggle(it.v)} />
+ <input type="checkbox" className="h-4 w-4 rounded accent-brand-700" checked={on.has(it.v)} onChange={() => onToggle(it.v)} />
{it.label}
</span>
<span className="text-xs text-neutral-400">{(counts[it.v] ?? 0).toLocaleString()}</span>
diff --git a/apps/web/src/app/search/SearchClient.tsx b/apps/web/src/app/search/SearchClient.tsx
index 3486f96..20bb3c4 100644
--- a/apps/web/src/app/search/SearchClient.tsx
+++ b/apps/web/src/app/search/SearchClient.tsx
@@ -168,10 +168,10 @@ export default function SearchClient({ initialQuery }: { initialQuery: string })
value={q}
onChange={(e) => setQ(e.target.value)}
placeholder="City, ZIP, or community"
- className="w-full max-w-md rounded-lg border border-neutral-300 px-4 py-2"
+ className="field max-w-md flex-1 py-2"
data-testid="search-input"
/>
- <button type="submit" className="rounded-lg bg-teal-700 px-5 py-2 font-medium text-white hover:bg-teal-800">
+ <button type="submit" className="btn-primary">
Search
</button>
</form>
@@ -180,7 +180,7 @@ export default function SearchClient({ initialQuery }: { initialQuery: string })
{/* Filters */}
<aside className="space-y-5 text-sm" data-testid="filters">
<div>
- <h3 className="font-semibold">Price</h3>
+ <h3 className="font-semibold text-brand-900">Price</h3>
<div className="mt-2 flex gap-2">
<input type="number" placeholder="Min" className="w-full rounded border border-neutral-300 px-2 py-1"
onChange={(e) => setSingle("priceMin", e.target.value)} data-testid="price-min" />
@@ -189,19 +189,19 @@ export default function SearchClient({ initialQuery }: { initialQuery: string })
</div>
</div>
<div>
- <h3 className="font-semibold">Beds (min)</h3>
+ <h3 className="font-semibold text-brand-900">Beds (min)</h3>
<div className="mt-2 flex gap-1">
{[1, 2, 3, 4, 5].map((n) => (
<button key={n} type="button" data-testid={`beds-${n}`}
onClick={() => setSingle("bedsMin", filters.bedsMin === String(n) ? "" : String(n))}
- className={`rounded border px-2.5 py-1 ${filters.bedsMin === String(n) ? "border-teal-600 bg-teal-50 text-teal-800" : "border-neutral-300"}`}>
+ className={`rounded-lg border px-2.5 py-1 transition ${filters.bedsMin === String(n) ? "border-brand-600 bg-brand-50 font-semibold text-brand-800" : "border-neutral-300 hover:border-brand-300"}`}>
{n}+
</button>
))}
</div>
</div>
<div>
- <h3 className="font-semibold">Construction status</h3>
+ <h3 className="font-semibold text-brand-900">Construction status</h3>
{facets?.constructionStatus.map((facet) => (
<label key={facet.value} className="mt-1 flex items-center gap-2">
<input type="checkbox"
@@ -213,7 +213,7 @@ export default function SearchClient({ initialQuery }: { initialQuery: string })
))}
</div>
<div>
- <h3 className="font-semibold">Builder</h3>
+ <h3 className="font-semibold text-brand-900">Builder</h3>
{facets?.builder.map((facet) => (
<label key={facet.value} className="mt-1 flex items-center gap-2">
<input type="checkbox"
@@ -225,7 +225,7 @@ export default function SearchClient({ initialQuery }: { initialQuery: string })
))}
</div>
<div>
- <h3 className="font-semibold">More</h3>
+ <h3 className="font-semibold text-brand-900">More</h3>
<label className="mt-1 flex items-center gap-2">
<input type="checkbox" checked={filters.incentives === "1"}
onChange={() => setSingle("incentives", filters.incentives === "1" ? "" : "1")} />
@@ -259,7 +259,7 @@ export default function SearchClient({ initialQuery }: { initialQuery: string })
<label className="flex items-center gap-2 text-sm">
Sort
<select value={sort} onChange={(e) => setSortPersist(e.target.value)}
- className="rounded border border-neutral-300 px-2 py-1" data-testid="sort-select">
+ className="rounded-lg border-0 px-2 py-1 shadow-sm ring-1 ring-inset ring-neutral-200 focus:ring-2 focus:ring-brand-400" data-testid="sort-select">
{SORTS.map((s) => (
<option key={s.value} value={s.value}>{s.label}</option>
))}
@@ -267,7 +267,7 @@ export default function SearchClient({ initialQuery }: { initialQuery: string })
</label>
<label className="flex items-center gap-2 text-sm">
Density
- <input type="range" min={2} max={4} step={1} value={cols}
+ <input type="range" min={2} max={4} step={1} value={cols} className="accent-brand-700"
onChange={(e) => setColsPersist(Number(e.target.value))} data-testid="density-slider" />
</label>
</div>
@@ -277,9 +277,9 @@ export default function SearchClient({ initialQuery }: { initialQuery: string })
<div className="grid gap-4" style={{ gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` }} data-testid="results-grid">
{(data?.homes ?? []).map((home) => (
<Link key={home.id} href={`/homes/${home.id}`}
- className="rounded-xl border border-neutral-200 p-4 shadow-sm transition hover:border-teal-400 hover:shadow-md"
+ className="card-interactive p-4"
data-testid="home-card">
- <div className="text-lg font-semibold">{fmtPrice(home.price)}</div>
+ <div className="font-display text-xl font-semibold text-brand-900">{fmtPrice(home.price)}</div>
<div className="mt-0.5 truncate text-sm text-neutral-700">
{home.street ?? "Address on request"} · {home.city}, {home.state}
</div>
@@ -302,7 +302,7 @@ export default function SearchClient({ initialQuery }: { initialQuery: string })
</p>
)}
</div>
- <div className="h-[540px] overflow-hidden rounded-xl border border-neutral-200" data-testid="map">
+ <div className="h-[540px] overflow-hidden rounded-2xl border border-neutral-200 shadow-[var(--shadow-card)]" data-testid="map">
<MapView markers={markers} onMoveEnd={onMapMove} />
</div>
</div>
@@ -310,10 +310,10 @@ export default function SearchClient({ initialQuery }: { initialQuery: string })
{totalPages > 1 && (
<div className="mt-6 flex items-center justify-center gap-3 text-sm">
<button disabled={page <= 1} onClick={() => setPage((p) => p - 1)}
- className="rounded border border-neutral-300 px-3 py-1 disabled:opacity-40">Prev</button>
- <span>Page {page} of {totalPages}</span>
+ className="rounded-full border border-neutral-300 px-4 py-1.5 hover:border-brand-300 disabled:opacity-40">Prev</button>
+ <span className="text-neutral-600">Page {page} of {totalPages}</span>
<button disabled={page >= totalPages} onClick={() => setPage((p) => p + 1)}
- className="rounded border border-neutral-300 px-3 py-1 disabled:opacity-40">Next</button>
+ className="rounded-full border border-neutral-300 px-4 py-1.5 hover:border-brand-300 disabled:opacity-40">Next</button>
</div>
)}
</div>
← 8b8126d Design system iter-1: Claude Design bundle (tokens + buttons
·
back to Homesonspec
·
Stage-3 verification engine: freshness decay + relabel, sour 3cb6635 →