feat(search): pin the results toolbar and float the map button on phones
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m12s
PR Checks / Backend Smoke (pull_request) Successful in 10s
PR Checks / Build Backend (no push) (pull_request) Successful in 33s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m19s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 1m16s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 26s
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m12s
PR Checks / Backend Smoke (pull_request) Successful in 10s
PR Checks / Build Backend (no push) (pull_request) Successful in 33s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m19s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 1m16s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 26s
The List/Map switch was a small grey control beside the results heading, and the filters were plain dropdowns labelled "All Phases" and "Advanced". Both scrolled away with the first result. Search, filters and the List/Map switch now share one card pinned under the header. Distance, phase and school type are pill controls in the row; "Advanced" becomes "More filters" and counts only what it hides. The switch is filled brand teal and says which view is on (aria-pressed). On phones the search folds to a one-line summary once made, the filter pills scroll sideways, and a floating Map/List button sits above the tab bar in place of the toolbar switch. The selected pin's card now stacks under that button instead of covering the tab bar. Switching view from far down the list scrolls back to the top of the results, and each switch is tracked as results_view_changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b34feb8e98
commit
983a581555
8 files changed
+716
-214
No files matched your search
@@ -484,42 +484,84 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* View Toggle */
|
||||
.viewToggle {
|
||||
/* ── Results toolbar ───────────────────────────────────────────────────────
|
||||
Search, filters and the List/Map switch in one card, pinned under the
|
||||
header. It sits above Leaflet's controls (z-index 1000), which scroll up
|
||||
beneath it, and below the comparison toast and the map's own fullscreen
|
||||
overlay. */
|
||||
.resultsToolbar {
|
||||
position: sticky;
|
||||
top: calc(64px + 0.5rem);
|
||||
z-index: 1001;
|
||||
display: flex;
|
||||
gap: 0.2rem;
|
||||
background: var(--bg-secondary);
|
||||
padding: 0.2rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.viewToggleBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0.375rem 0.625rem;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.viewToggleBtn:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.viewToggleBtn.active {
|
||||
align-items: flex-start;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.25rem;
|
||||
padding: 0.875rem 1rem;
|
||||
background: var(--bg-card);
|
||||
color: var(--brand-strong);
|
||||
box-shadow: 0 2px 4px rgba(var(--shadow-rgb), 0.08);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.viewToggleBtn svg {
|
||||
.resultsToolbar > :first-child {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* The one filled control in the toolbar: which view you are in is the first
|
||||
thing a results page should answer. */
|
||||
.viewSwitch {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
padding: 1px;
|
||||
background: var(--bg-card);
|
||||
border: 1.5px solid var(--brand);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.viewSwitchBtn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
min-height: 2.5rem;
|
||||
padding: 0 1.125rem;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: calc(var(--radius-md) - 3px);
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--step--1);
|
||||
font-weight: 700;
|
||||
color: var(--brand-strong);
|
||||
cursor: pointer;
|
||||
transition: background-color var(--transition), color var(--transition);
|
||||
}
|
||||
|
||||
.viewSwitchBtn:hover {
|
||||
background: var(--brand-bg);
|
||||
}
|
||||
|
||||
.viewSwitchBtn[aria-pressed='true'] {
|
||||
background: var(--brand);
|
||||
color: var(--brand-on);
|
||||
}
|
||||
|
||||
/* Phones: a full-bleed band flush under the header, and the switch gives way
|
||||
to the floating button in .mobileDock. */
|
||||
@media (max-width: 640px) {
|
||||
.resultsToolbar {
|
||||
top: 56px;
|
||||
margin: -1rem -1rem 1rem;
|
||||
padding: 0.75rem 1rem;
|
||||
border-width: 0 0 1px;
|
||||
border-radius: 0;
|
||||
box-shadow: 0 2px 8px rgba(var(--shadow-rgb), 0.06);
|
||||
}
|
||||
|
||||
.viewSwitch {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.results {
|
||||
@@ -744,11 +786,6 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.viewToggle {
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* The sort <select> sizes to its widest option ("Highest Reading, Writing
|
||||
& Maths %"), which overflows a phone viewport — beside the view toggle it
|
||||
ran off the right edge. Let it flex into the remaining space and shrink;
|
||||
@@ -801,24 +838,38 @@
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
/* Mobile Bottom Sheet */
|
||||
.bottomSheetWrapper {
|
||||
/* ── 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. */
|
||||
.mobileDock {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.viewFab {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.bottomSheetWrapper {
|
||||
display: block;
|
||||
.mobileDock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
z-index: 1002;
|
||||
padding: 1rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mobileDock > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.bottomSheet {
|
||||
align-self: stretch;
|
||||
position: relative;
|
||||
background: var(--bg-card);
|
||||
border-radius: 12px;
|
||||
@@ -883,6 +934,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Above the tab bar, and riding with it when iOS Chrome's URL bar hides
|
||||
(MOBILE.md, check 4). */
|
||||
@media (max-width: 640px) {
|
||||
.mobileDock {
|
||||
bottom: calc(56px + env(safe-area-inset-bottom, 0px));
|
||||
padding: 0.75rem;
|
||||
transform: translate3d(0, var(--mobile-bar-offset, 0px), 0);
|
||||
}
|
||||
|
||||
.viewFab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
min-height: 3rem;
|
||||
padding: 0 1.375rem;
|
||||
background: var(--text-primary);
|
||||
color: var(--bg-primary);
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
box-shadow: 0 8px 20px rgba(var(--shadow-rgb), 0.32);
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--step--1);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Room under the last card so the floating button never sits on it. */
|
||||
.hasViewFab {
|
||||
padding-bottom: 4.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.quickSearches {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in new issue
Block a user