fix(tradein/v2): keep top nav active over overlays, stronger «К оценке», backdrop-close
Three UX fixes on the /trade-in/v2 overlays (feedback): 1. TopNav stays interactive while a section overlay is open. The a11y audit #2081 put inert on the whole content wrap (which contains TopNav), so opening an overlay disabled the top tabs. Move inert off the wrap onto <main>/<footer> only; the nav is inert just for the full-screen LocationDrawer. Users can now switch sections directly from an open overlay. role=dialog/aria-modal/Esc/focus-trap kept. 2. «← К ОЦЕНКЕ» made a clear filled primary button (solid accentDeep bg, white text ≥4.9:1 AA, shadow, hover lift) instead of the weak ghost pill. Uses existing accent tokens only. 3. Backdrop click closes the overlay. Add a transparent sibling layer below the panel (z below it, starting under the nav so tabs stay clickable); clicks in the margin close it. Clicks inside the panel / on its buttons never reach it (sibling, not ancestor) so they don't close. Esc + focus restore preserved.
This commit is contained in:
parent
3b9dfdb44e
commit
35a0e29122
2 changed files with 182 additions and 118 deletions
|
|
@ -701,7 +701,6 @@ export default function TradeInV2Page() {
|
||||||
|
|
||||||
{/* CONTENT WRAP */}
|
{/* CONTENT WRAP */}
|
||||||
<div
|
<div
|
||||||
inert={nav !== 0 || drawerOpen ? true : undefined}
|
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
inset: 14,
|
inset: 14,
|
||||||
|
|
@ -710,7 +709,17 @@ export default function TradeInV2Page() {
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<nav aria-label="Разделы" style={{ display: "contents" }}>
|
{/* TopNav stays interactive while a section overlay is open so the
|
||||||
|
user can switch sections directly from the overlay. Only the
|
||||||
|
LocationDrawer (a full modal over the whole HUD) inerts it. The
|
||||||
|
dashboard body (<main>/<footer>) is still inerted behind ANY
|
||||||
|
overlay/drawer — preserving the focus + click guard from a11y
|
||||||
|
audit #2081 for everything except the top tabs. */}
|
||||||
|
<nav
|
||||||
|
aria-label="Разделы"
|
||||||
|
inert={drawerOpen ? true : undefined}
|
||||||
|
style={{ display: "contents" }}
|
||||||
|
>
|
||||||
<TopNav
|
<TopNav
|
||||||
active={nav}
|
active={nav}
|
||||||
onNavigate={setNav}
|
onNavigate={setNav}
|
||||||
|
|
@ -719,7 +728,10 @@ export default function TradeInV2Page() {
|
||||||
onLogout={logout}
|
onLogout={logout}
|
||||||
/>
|
/>
|
||||||
</nav>
|
</nav>
|
||||||
<main style={{ display: "contents" }}>
|
<main
|
||||||
|
inert={nav !== 0 || drawerOpen ? true : undefined}
|
||||||
|
style={{ display: "contents" }}
|
||||||
|
>
|
||||||
<HeroBar
|
<HeroBar
|
||||||
data={{ report, object: objectInfo }}
|
data={{ report, object: objectInfo }}
|
||||||
estimateId={mounted ? currentEstimateId : null}
|
estimateId={mounted ? currentEstimateId : null}
|
||||||
|
|
@ -749,7 +761,10 @@ export default function TradeInV2Page() {
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer style={{ display: "contents" }}>
|
<footer
|
||||||
|
inert={nav !== 0 || drawerOpen ? true : undefined}
|
||||||
|
style={{ display: "contents" }}
|
||||||
|
>
|
||||||
<Footer data={report} hasEstimate={hasEstimate} />
|
<Footer data={report} hasEstimate={hasEstimate} />
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,11 @@ interface SectionOverlayProps {
|
||||||
cache?: CacheData;
|
cache?: CacheData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Panel geometry (relative to the artboard). Kept as a constant so the click
|
||||||
|
// backdrop can align its top edge to the panel — the backdrop starts BELOW the
|
||||||
|
// TopNav so the top tabs stay clickable while an overlay is open.
|
||||||
|
const PANEL_TOP = 80;
|
||||||
|
|
||||||
export default function SectionOverlay({
|
export default function SectionOverlay({
|
||||||
active,
|
active,
|
||||||
onClose,
|
onClose,
|
||||||
|
|
@ -105,6 +110,28 @@ export default function SectionOverlay({
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{/* Click-outside backdrop. A transparent layer covering the artboard BELOW
|
||||||
|
the TopNav (top: PANEL_TOP) — clicking the margin area around the panel
|
||||||
|
closes the overlay (in addition to Esc). It is a SIBLING of the dialog
|
||||||
|
(not an ancestor) and sits one z-index below it, so clicks inside the
|
||||||
|
panel / on its buttons never reach this handler (they don't bubble to a
|
||||||
|
sibling) and therefore never close the overlay. Starting below the nav
|
||||||
|
keeps the top tabs clickable. aria-hidden + no tabIndex → invisible to
|
||||||
|
AT and keyboard; Esc remains the keyboard close path. */}
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
onClick={onClose}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: PANEL_TOP,
|
||||||
|
bottom: 0,
|
||||||
|
zIndex: 29,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
ref={dialogRef}
|
ref={dialogRef}
|
||||||
role="dialog"
|
role="dialog"
|
||||||
|
|
@ -115,7 +142,7 @@ export default function SectionOverlay({
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
left: 28,
|
left: 28,
|
||||||
right: 28,
|
right: 28,
|
||||||
top: 80,
|
top: PANEL_TOP,
|
||||||
bottom: 30,
|
bottom: 30,
|
||||||
zIndex: 30,
|
zIndex: 30,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
|
@ -131,8 +158,19 @@ export default function SectionOverlay({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<style>{`
|
<style>{`
|
||||||
.tiv2-so-back { color: ${tokens.ink}; transition: all .15s; }
|
.tiv2-so-back {
|
||||||
.tiv2-so-back:hover { border-color: ${tokens.accent}; color: ${tokens.accent}; }
|
transition: all .15s;
|
||||||
|
background: ${tokens.accentDeep};
|
||||||
|
box-shadow: 0 3px 10px rgba(46,139,255,.28);
|
||||||
|
}
|
||||||
|
.tiv2-so-back:hover {
|
||||||
|
box-shadow: 0 6px 16px rgba(46,139,255,.45);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.tiv2-so-back:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 2px 6px rgba(46,139,255,.3);
|
||||||
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
|
|
||||||
{/* HUD corner brackets */}
|
{/* HUD corner brackets */}
|
||||||
|
|
@ -198,23 +236,33 @@ export default function SectionOverlay({
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="tiv2-so-back"
|
className="tiv2-so-back"
|
||||||
|
aria-label="Вернуться к оценке"
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 8,
|
gap: 8,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
border: `1px solid ${tokens.line}`,
|
border: `1px solid ${tokens.accentDeep}`,
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
padding: "6px 12px",
|
padding: "8px 15px",
|
||||||
background: tokens.surface.w60,
|
// background + shadow live in .tiv2-so-back so :hover can override
|
||||||
// reset UA button defaults while keeping the original box look;
|
// them (an inline background would beat the hover rule on
|
||||||
// base colour lives in .tiv2-so-back so :hover can win
|
// specificity). Solid accentDeep keeps white text ≥4.5:1 (AA).
|
||||||
fontFamily: "inherit",
|
fontFamily: "inherit",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ fontSize: 13 }}>←</span>
|
|
||||||
<span
|
<span
|
||||||
style={{ fontSize: 10, letterSpacing: 1.5, color: tokens.body2 }}
|
style={{ fontSize: 15, color: tokens.onAccent, lineHeight: 1 }}
|
||||||
|
>
|
||||||
|
←
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: 600,
|
||||||
|
letterSpacing: 1.5,
|
||||||
|
color: tokens.onAccent,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
К ОЦЕНКЕ
|
К ОЦЕНКЕ
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -238,5 +286,6 @@ export default function SectionOverlay({
|
||||||
{active === 4 && <CacheView data={cache} />}
|
{active === 4 && <CacheView data={cache} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue