Mobile: tour bottom-sheet + measured sizing; tidy profile tab-grid tiles

Onboarding tour (tourEngine.js):
- On narrow viewports (<640px) dock the popover as a fixed BOTTOM SHEET
  instead of anchoring it — kills the off-screen/jump-to-center glitches
  caused by short viewports, wrapping buttons and URL-bar resize. The
  spotlight still highlights the target, scrolled to center so it sits above
  the sheet.
- On desktop, measure the ACTUAL popover size (getBoundingClientRect) and
  feed it to computeTourPlacement, instead of the fixed 240/336 guesses that
  pushed tall popovers off-screen.

Profile modal mobile tab grid (CSS):
- Equal-height tiles via a min-height token + centered content, so long
  labels no longer leave a ragged grid.
- Move the unread dot to the tile's top-right corner (was landing in the
  grid gutter at the vertical-nav left edge).
- Tighter line-height/padding + non-shrinking glyph.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-07-08 06:34:44 -04:00
1 parent 55db677637
commit 0e2f81d222
3 files changed
+54 -4

No files matched your search

+17 -3
View File
@@ -3668,12 +3668,16 @@
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
justify-content: center;
text-align: center;
gap: var(--space-2xs);
width: 100%;
padding-block: var(--space-sm);
padding-inline: var(--space-2xs);
/* equal-height tiles: a consistent floor so short and 2-line labels align
(grid rows stretch to the tallest tile, so within a row they match) */
min-block-size: var(--profile-modal-nav-tile-min-height);
padding-block: var(--space-xs);
padding-inline: var(--space-3xs);
line-height: 1.15;
/* wrap labels at spaces, not mid-word */
overflow-wrap: break-word;
word-break: normal;
@@ -3684,6 +3688,16 @@
.profile-modal__nav > button svg {
width: var(--space-lg);
height: var(--space-lg);
flex-shrink: 0;
}
/* In the tile grid the unread dot rides the tile's top-right corner instead
of the vertical-nav left edge (where it landed in the grid gutter). */
.profile-modal__nav > button .profile-nav__unread-dot {
top: var(--space-2xs);
left: auto;
right: var(--space-2xs);
transform: none;
}
}
+2
View File
@@ -359,6 +359,8 @@
--notification-banner-max-inline: var(--size-28rem);
--profile-modal-nav-min-width: max-content;
--profile-modal-nav-max-width: max-content;
/* Equal-height floor for the mobile tab-grid tiles. */
--profile-modal-nav-tile-min-height: 4.75rem;
--profile-modal-scrollbar-offset: var(--space-2xs);
--modal-embed-height: 80vh;
--profile-history-max-height: 26.25rem;
+35 -1
View File
@@ -57,6 +57,20 @@ const TOUR_CSS = `
padding: 1rem;
transition: left 0.28s ease, top 0.28s ease;
}
/* Mobile: dock the popover as a bottom sheet instead of anchoring it to the
target. Anchored placement is fragile on phones (short viewports, wrapping
buttons, URL-bar resize) — a fixed sheet is always in a predictable spot and
the spotlight still highlights the (centered-scrolled) target above it. */
.bv-tour-popover.bv-tour-popover--sheet {
left: 0.75rem;
right: 0.75rem;
top: auto;
bottom: max(0.75rem, env(safe-area-inset-bottom, 0px));
transform: none;
width: auto;
max-width: 32rem;
margin-inline: auto;
}
.bv-tour-popover h3 { font-size: 1rem; font-weight: 700; margin: 0 0 0.375rem; }
.bv-tour-popover p { font-size: 0.875rem; margin: 0 0 0.75rem; opacity: 0.9; }
.bv-tour-dots { display: flex; gap: 0.375rem; margin-bottom: 0.75rem; }
@@ -171,10 +185,13 @@ export function createTour({
}
};
const isMobileViewport = () => (doc.defaultView?.innerWidth || 1200) < 640;
function measure(step) {
const target = step.target ? doc.querySelector(step.target) : null;
if (!target) {
// Centered card: zero-size cutout in the middle (pure dim).
state.popover.classList.remove("bv-tour-popover--sheet");
setVar(state.scrim, "--bv-tour-x", "50vw");
setVar(state.scrim, "--bv-tour-y", "50vh");
setVar(state.scrim, "--bv-tour-w", "0px");
@@ -187,8 +204,13 @@ export function createTour({
return;
}
const mobile = isMobileViewport();
try {
target.scrollIntoView({ block: "nearest", inline: "nearest" });
// Center the target on mobile so it sits above the bottom sheet.
target.scrollIntoView({
block: mobile ? "center" : "nearest",
inline: "nearest",
});
} catch (error) {
// older engines
}
@@ -200,11 +222,23 @@ export function createTour({
setVar(state.scrim, "--bv-tour-w", `${rect.width + pad * 2}px`);
setVar(state.scrim, "--bv-tour-h", `${rect.height + pad * 2}px`);
if (mobile) {
// Bottom sheet — no anchored-placement math to get wrong.
state.popover.classList.add("bv-tour-popover--sheet");
return;
}
// Desktop: anchor, but measure the ACTUAL popover so the fit checks are
// accurate (the old 240px/336px guesses pushed tall popovers off-screen).
state.popover.classList.remove("bv-tour-popover--sheet");
const popRect = state.popover.getBoundingClientRect();
const placementResult = computeTourPlacement({
rect,
viewportWidth: doc.defaultView?.innerWidth || 1200,
viewportHeight: doc.defaultView?.innerHeight || 800,
preferred: step.placement || "",
popWidth: popRect.width || 336,
popHeight: popRect.height || 240,
});
setVar(state.popover, "--bv-tour-px", `${placementResult.px}px`);
setVar(state.popover, "--bv-tour-py", `${placementResult.py}px`);