PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m7s
PR Checks / Backend Smoke (pull_request) Successful in 8s
PR Checks / Build Backend (no push) (pull_request) Successful in 10s
PR Checks / Build Frontend (no push) (pull_request) Successful in 44s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 11s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 49s
The fade between the map band and the school header ramped through rgba(255,255,255,...) and landed on var(--bg-card). In the light theme that is white into white and invisible, as designed. In the dark theme it climbed to 95% WHITE and then met a near-black card, putting a bright band across the full width exactly where the map should dissolve into the title. Fading to the colour the gradient lands on is the whole trick, and it only works if that colour is a token — so --bg-card-rgb now exists in both theme blocks, matching the --hero-ground-rgb precedent. Two more defects in the same file, same cause, found while in there: The controls floating over the map paired a hardcoded white background with color: var(--text-primary), which resolves to #E9EEF0 in dark — near-white text on a near-white button. These deliberately do NOT follow the theme, because the map tiles are light in both, so the ink is now literal too and says why. A themed token is the wrong tool for a surface that never changes. The loading skeleton swept 50% white across var(--bg-secondary), which is a bright flash every 1.4s on a dark page. It now sweeps toward the card colour, a shade lighter than the ground in both themes. The guard is a stylesheet test rather than a render test, because the bug is invisible in the theme it was written for. Verified by reverting each fix in turn: it names .fade and .openHint exactly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015mWQnpye9F299NVRCCSRvj
169 lines
5.2 KiB
CSS
169 lines
5.2 KiB
CSS
/* Preview band that blends into the hero title; expands to fullscreen.
|
|
isolation:isolate contains the map + fade + open-button z-indices (which
|
|
must beat Leaflet's internal panes) so they don't leak out and outrank the
|
|
header's Compare button. */
|
|
.wrapper {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 210px;
|
|
background: var(--bg-secondary);
|
|
isolation: isolate;
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.wrapper {
|
|
height: 150px;
|
|
}
|
|
}
|
|
|
|
/* Preview: move the OSM attribution off the bottom edge, where the school
|
|
title slides up under the fade — top-left is the band's only quiet corner
|
|
(Compare floats top-right). Fullscreen keeps Leaflet's default bottom-right,
|
|
since the zoom control occupies the top-left there. */
|
|
.wrapper:not([data-fullscreen]) :global(.leaflet-bottom.leaflet-right) {
|
|
top: 0;
|
|
left: 0;
|
|
right: auto;
|
|
bottom: auto;
|
|
}
|
|
|
|
/* Fullscreen: the Fullscreen API promotes this element to fill the viewport. */
|
|
.wrapper[data-fullscreen] {
|
|
height: 100vh;
|
|
height: 100dvh;
|
|
background: #fff;
|
|
}
|
|
|
|
/* Fallback fullscreen (iOS Safari — no Element.requestFullscreen): the API
|
|
can't promote the element, so pin it over the page ourselves. Above the
|
|
comparison toast (3000) and everything else except modals (9999+). */
|
|
.wrapper[data-fs-fallback] {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 5000;
|
|
}
|
|
|
|
.skeleton {
|
|
width: 100%;
|
|
height: 100%;
|
|
background:
|
|
/* Sweeps toward the card colour, which is a shade lighter than this
|
|
ground in both themes. Hardcoded white was a bright flash across a
|
|
dark page every 1.4s while the tiles loaded. */
|
|
linear-gradient(100deg,
|
|
rgba(var(--bg-card-rgb), 0) 40%,
|
|
rgba(var(--bg-card-rgb), .5) 50%,
|
|
rgba(var(--bg-card-rgb), 0) 60%) var(--bg-secondary);
|
|
background-size: 200% 100%;
|
|
animation: shimmer 1.4s infinite;
|
|
}
|
|
|
|
@keyframes shimmer {
|
|
to { background-position: -200% 0; }
|
|
}
|
|
|
|
/* Full-band click target. Transparent so the map shows through; reveals a
|
|
hint pill on hover/focus. Sits above the map but below the header's fade
|
|
and Compare button (which carry higher stacking in the header). */
|
|
.openBtn {
|
|
position: absolute;
|
|
inset: 0;
|
|
/* Above Leaflet's marker pane (600) so the whole band — pin included — is
|
|
one click target. Transparent, so map + pin still show through. */
|
|
z-index: 800;
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 0;
|
|
border: 0;
|
|
background: transparent;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: center;
|
|
}
|
|
|
|
/*
|
|
* Controls that float ON the map.
|
|
*
|
|
* The map tiles are light in both themes, so these deliberately do NOT follow
|
|
* the theme — they follow the map. The literal ink below is the point: paired
|
|
* with a hardcoded white background, `color: var(--text-primary)` resolved to
|
|
* #E9EEF0 in the dark theme and put near-white text on a near-white button.
|
|
* A themed token is the wrong tool for a surface that never changes.
|
|
*/
|
|
.openHint {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
margin-bottom: 16px;
|
|
padding: 8px 14px;
|
|
border-radius: 999px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
/* See "Controls that float ON the map" above. */
|
|
color: #1C2731;
|
|
background: rgba(255, 255, 255, .85);
|
|
-webkit-backdrop-filter: blur(6px);
|
|
backdrop-filter: blur(6px);
|
|
box-shadow: 0 2px 10px rgba(var(--shadow-rgb), .16);
|
|
opacity: 0;
|
|
transform: translateY(4px);
|
|
transition: opacity .16s ease, transform .16s ease;
|
|
}
|
|
|
|
.openBtn:hover .openHint,
|
|
.openBtn:focus-visible .openHint {
|
|
opacity: 1;
|
|
transform: none;
|
|
}
|
|
|
|
/* Diffuse fade: map dissolves to the card background before the title. */
|
|
.fade {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
top: 33%;
|
|
/* Above Leaflet's tile (200) and overlay (400) panes so the gradient is
|
|
actually visible, but below the marker pane (600) so the pin stays crisp
|
|
on top of the blend. */
|
|
z-index: 450;
|
|
pointer-events: none;
|
|
/* The card colour, not white.
|
|
This ramp was hardcoded white and ended at var(--bg-card). In the light
|
|
theme that is white into white and invisible, as intended. In the dark
|
|
theme it climbed to 95% WHITE and then met a near-black card — a bright
|
|
band across the full width, right where the map is supposed to dissolve
|
|
into the header. Fading to the same colour the gradient lands on is the
|
|
whole trick, and it only works if that colour is a token. */
|
|
background: linear-gradient(to bottom,
|
|
rgba(var(--bg-card-rgb), 0) 0%,
|
|
rgba(var(--bg-card-rgb), .35) 35%,
|
|
rgba(var(--bg-card-rgb), .75) 62%,
|
|
rgba(var(--bg-card-rgb), .95) 82%,
|
|
var(--bg-card) 100%);
|
|
}
|
|
|
|
.closeBtn {
|
|
position: absolute;
|
|
top: 0.75rem;
|
|
right: 0.75rem;
|
|
z-index: 1000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 40px;
|
|
height: 40px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
background: rgba(255, 255, 255, .92);
|
|
/* See "Controls that float ON the map" above. */
|
|
color: #1C2731;
|
|
cursor: pointer;
|
|
box-shadow: 0 2px 10px rgba(var(--shadow-rgb), .2);
|
|
}
|
|
|
|
.closeBtn:hover {
|
|
background: #fff;
|
|
}
|