[object Object]

← 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

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 →