feat(search): align the map view with the mockup and open it on desktop
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m14s
PR Checks / Backend Smoke (pull_request) Successful in 10s
PR Checks / Build Backend (no push) (pull_request) Successful in 19s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m20s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 11s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 26s
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m14s
PR Checks / Backend Smoke (pull_request) Successful in 10s
PR Checks / Build Backend (no push) (pull_request) Successful in 19s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m20s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 11s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 26s
The map view now follows option B of the results-controls mockups: - The list sits in a pane on the left, under the result count and sort, and the map fills the rest of the screen below the pinned toolbar. The toolbar's height is measured, so the split ends 1rem above the bottom of the screen however the controls wrap. - Pins are brand-teal dots and the selected one is coral. The search location is an ink dot, the search radius a dashed circle with its distance, and the view fits that circle. Tiles are muted and the zoom sits under the fullscreen button. - A school picked on the map or in the list opens a card on the map (View, + Compare, following the basket), and its list card is ringed and scrolled into view. Phones keep the bottom sheet. - The list cards show the full name, Ofsted and school type, the headline figure and pupils. The map cards now follow the list rows: no England benchmark for special schools, PRUs and AP, and no placeholder all-zero RWM (Greenmead showed "0% RWM -62 pts vs national"). That rule moves to a shared listRwmValue helper. A postcode search opens on the map for desktop browsers, chosen on the server from the user agent so the list never paints first. The client falls back to the list below 1024px, and follows the default through client-side navigation from the hero until the reader picks a view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0e177ca2ec
commit
4e4b30e812
14 files changed
+1118
-440
No files matched your search
@@ -572,133 +572,174 @@
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Map View Layout */
|
||||
/* ── Map view ──────────────────────────────────────────────────────────────
|
||||
The list on the left, the map filling the rest, in one card that runs from
|
||||
under the pinned toolbar to the bottom of the screen. --toolbar-h is the
|
||||
toolbar's measured height, written by HomeView. */
|
||||
/* Where the split starts before any scrolling: header, the page's top
|
||||
padding (globals.css .main), the toolbar and its margin. */
|
||||
.homeView {
|
||||
--map-top: calc(64px + 1.25rem + var(--toolbar-h, 76px) + 1.25rem);
|
||||
--map-bottom: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.homeView {
|
||||
--map-top: calc(64px + 1rem + var(--toolbar-h, 76px) + 1.25rem);
|
||||
}
|
||||
}
|
||||
|
||||
.mapViewContainer {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 340px;
|
||||
gap: 1rem;
|
||||
height: calc(100dvh - 280px);
|
||||
min-height: 520px;
|
||||
max-height: 800px;
|
||||
grid-template-columns: minmax(340px, 420px) minmax(0, 1fr);
|
||||
height: calc(100dvh - var(--map-top) - var(--map-bottom));
|
||||
min-height: 480px;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mapListPane {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
background: var(--bg-primary);
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.mapListPane .resultsHeader {
|
||||
gap: 0.5rem;
|
||||
padding: 0.875rem 1rem 0.75rem;
|
||||
}
|
||||
|
||||
.mapListPane .resultsHeader h2 {
|
||||
font-size: var(--step-1);
|
||||
}
|
||||
|
||||
.mapContainer {
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.compactList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
gap: 0.625rem;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
padding-right: 0.375rem;
|
||||
padding: 0.125rem 1rem 1rem;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.compactList::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.compactList::-webkit-scrollbar-track {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.compactList::-webkit-scrollbar-thumb {
|
||||
background: var(--border);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.compactList::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Compact School Item */
|
||||
/* Compact School Item: the list pane's card, and the phone's bottom sheet. */
|
||||
.compactItem {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.625rem 0.75rem;
|
||||
flex-direction: column;
|
||||
gap: 0.4375rem;
|
||||
padding: 0.75rem 0.875rem;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: border-color var(--transition), box-shadow var(--transition);
|
||||
}
|
||||
|
||||
.compactItem:hover {
|
||||
border-color: var(--brand);
|
||||
box-shadow: 0 2px 6px rgba(var(--shadow-rgb), 0.05);
|
||||
border-color: var(--border-strong);
|
||||
}
|
||||
|
||||
.compactItemContent {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
.compactItemSelected,
|
||||
.compactItemSelected:hover {
|
||||
border-color: var(--brand);
|
||||
box-shadow: 0 0 0 2px rgba(var(--brand-rgb), 0.28);
|
||||
}
|
||||
|
||||
.compactItemHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
margin-bottom: 0.125rem;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 0.625rem;
|
||||
}
|
||||
|
||||
.compactItemName {
|
||||
font-weight: 600;
|
||||
font-size: 0.8125rem;
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.compactItemName:hover {
|
||||
color: var(--brand-strong);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.distanceBadge {
|
||||
flex-shrink: 0;
|
||||
padding: 0.0625rem 0.375rem;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
padding: 0.125rem 0.375rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
background: var(--brand);
|
||||
color: var(--brand-on);
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.compactItemMeta {
|
||||
.compactItemTags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.compactBadge,
|
||||
.compactTag {
|
||||
padding: 0.125rem 0.4375rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.compactTag {
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 0.25rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.compactItemMeta span:not(:last-child)::after {
|
||||
content: '·';
|
||||
margin-left: 0.375rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.ofsted1,
|
||||
.ofsted2 { background: var(--status-above-bg); color: var(--status-above); }
|
||||
.ofsted3 { background: var(--status-below-bg); color: var(--status-below); }
|
||||
.ofsted4 { background: var(--status-below); color: var(--text-inverse); }
|
||||
.ofstedRc { background: var(--phase-secondary-text); color: var(--text-inverse); }
|
||||
.ofstedInspected { background: var(--phase-primary-bg); color: var(--phase-primary-text); }
|
||||
.ofstedPending { background: var(--border); color: var(--text-muted); }
|
||||
|
||||
.compactItemStats {
|
||||
.compactItemFooter {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
font-size: 0.6875rem;
|
||||
}
|
||||
|
||||
.compactStat {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.compactStat strong {
|
||||
font-size: 0.9375rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.deltaUp { color: var(--status-above); font-weight: 600; }
|
||||
.deltaDown { color: var(--status-below); font-weight: 600; }
|
||||
|
||||
.compactItemActions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
gap: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -795,49 +836,11 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mapViewContainer {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 260px auto;
|
||||
height: auto;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.mapContainer {
|
||||
height: 260px;
|
||||
}
|
||||
|
||||
.compactList {
|
||||
height: auto;
|
||||
max-height: 350px;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.compactItem {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.compactItemActions {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.compactItemActions > * {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.emptyState {
|
||||
padding: 2rem 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Highlighted List Item */
|
||||
.highlightedItem .compactItem {
|
||||
border-color: var(--brand);
|
||||
box-shadow: 0 0 0 1px var(--brand);
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
/* ── Mobile dock ───────────────────────────────────────────────────────────
|
||||
The floating List/Map button (phones only) above the selected pin's card.
|
||||
A column, so the button always clears the card instead of covering it. */
|
||||
@@ -883,10 +886,7 @@
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.bottomSheet .compactItem:hover {
|
||||
box-shadow: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.closeSheetBtn {
|
||||
@@ -917,21 +917,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* When map view on mobile, expand map and hide list */
|
||||
/* Map only: the pane keeps its heading, the cards give way to the pins and
|
||||
the bottom sheet. */
|
||||
.mapViewContainer {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
height: calc(100dvh - 280px);
|
||||
min-height: 400px;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.mapContainer {
|
||||
height: 100%;
|
||||
.mapListPane {
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.mapListPane .sortSelect,
|
||||
.compactList {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mapListPane .resultsHeader {
|
||||
padding: 0.625rem 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Above the tab bar, and riding with it when iOS Chrome's URL bar hides
|
||||
@@ -964,6 +970,16 @@
|
||||
.hasViewFab {
|
||||
padding-bottom: 4.5rem;
|
||||
}
|
||||
|
||||
/* The map runs down to the tab bar; the button floats over the map. */
|
||||
.mapViewResults.hasViewFab {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.homeView {
|
||||
--map-top: calc(56px + var(--toolbar-h, 124px) + 1rem);
|
||||
--map-bottom: calc(56px + env(safe-area-inset-bottom, 0px) + 1rem);
|
||||
}
|
||||
}
|
||||
|
||||
.quickSearches {
|
||||
|
||||
Reference in new issue
Block a user