Skip to content

Commit 0193cd5

Browse files
V48 (impl-only): Kill product shell x-jitter on load
Reserve scrollbar gutter, fix chip value widths, one-line summary, and keep pagination chrome mounted while loading.
1 parent 97a5fc0 commit 0193cd5

3 files changed

Lines changed: 29 additions & 13 deletions

File tree

apps/uapi/app/globals.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,14 @@ nextjs-portal {
454454
@apply border-border;
455455
}
456456

457+
/*
458+
* Reserve vertical scrollbar gutter so product tables/chips do not get a
459+
* tiny x-wise jump when content loads and the page scrollbar appears.
460+
*/
461+
html {
462+
scrollbar-gutter: stable;
463+
}
464+
457465
body {
458466
@apply bg-background text-foreground;
459467
}

apps/uapi/components/bitcode/pipeline/BitcodeTransactionsOverview/BitcodeTransactionsOverview.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ function shouldReduceMotion(prefersReduced: boolean | null): boolean {
4343
return false;
4444
}
4545

46-
/** Value slot: fixed-height pulse or opacity-only number — never changes chip y. */
46+
/** Shared value box — same width pending/ready so chip row never x-jitters. */
47+
const OVERVIEW_VALUE_CLASS =
48+
'inline-block w-[2.75rem] text-right leading-none tabular-nums text-neutral-100';
49+
50+
/** Value slot: fixed-size pulse or opacity-only number — never reflows x or y. */
4751
function OverviewStatValue({
4852
ready,
4953
reduceMotion,
@@ -53,21 +57,20 @@ function OverviewStatValue({
5357
reduceMotion: boolean;
5458
children: React.ReactNode;
5559
}) {
56-
// Fixed min width so 0 → 13 does not reflow the chip row y (or wrap).
5760
if (!ready) {
5861
return (
5962
<span
60-
className="inline-block h-2.5 w-5 shrink-0 bg-white/[0.12] motion-safe:animate-pulse"
63+
className="inline-block h-2.5 w-[2.75rem] shrink-0 bg-white/[0.12] motion-safe:animate-pulse"
6164
aria-hidden="true"
6265
/>
6366
);
6467
}
6568
if (reduceMotion) {
66-
return <span className="inline-block min-w-[1.25rem] leading-none text-neutral-100">{children}</span>;
69+
return <span className={OVERVIEW_VALUE_CLASS}>{children}</span>;
6770
}
6871
return (
6972
<motion.span
70-
className="inline-block min-w-[1.25rem] leading-none text-neutral-100"
73+
className={OVERVIEW_VALUE_CLASS}
7174
initial={{ opacity: 0 }}
7275
animate={{ opacity: 1 }}
7376
transition={{ duration: 0.42, ease: productEntranceEase }}

apps/uapi/components/bitcode/routes/ProductRouteShell/ProductRouteShell.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,19 +186,21 @@ function ProductRouteMetricChips({
186186
}) {
187187
const reduceMotion = shouldReduceMetricMotion(useReducedMotion());
188188

189+
// Fixed value box (pending + ready) so chip row width never reflows on load.
190+
const valueClass =
191+
"inline-block w-[5.5rem] truncate text-right text-[0.7rem] font-semibold leading-none tabular-nums text-white";
192+
189193
const chips = metrics.map((metric) => {
190194
const valueSlot = !ready ? (
191195
<span
192-
className="inline-block h-2.5 w-6 shrink-0 bg-white/[0.12] motion-safe:animate-pulse"
196+
className="inline-block h-2.5 w-[5.5rem] shrink-0 bg-white/[0.12] motion-safe:animate-pulse"
193197
aria-hidden="true"
194198
/>
195199
) : reduceMotion ? (
196-
<span className="max-w-[9rem] truncate text-[0.7rem] font-semibold leading-none text-white phone:max-w-none">
197-
{metric.value}
198-
</span>
200+
<span className={valueClass}>{metric.value}</span>
199201
) : (
200202
<motion.span
201-
className="max-w-[9rem] truncate text-[0.7rem] font-semibold leading-none text-white phone:max-w-none"
203+
className={valueClass}
202204
initial={{ opacity: 0 }}
203205
animate={{ opacity: 1 }}
204206
transition={{ duration: 0.42, ease: productEntranceEase }}
@@ -211,7 +213,9 @@ function ProductRouteMetricChips({
211213
const chipBody = (
212214
<>
213215
<dt className="leading-none text-neutral-500">{metric.label}</dt>
214-
<dd className="flex min-w-0 items-center">{valueSlot}</dd>
216+
<dd className="flex w-[5.5rem] shrink-0 items-center justify-end">
217+
{valueSlot}
218+
</dd>
215219
</>
216220
);
217221
return metric.description ? (
@@ -282,11 +286,12 @@ export function ProductRouteShell({
282286
<h1 className="min-w-0 shrink-0 text-lg font-semibold tracking-tight text-white phone:text-xl tablet:text-2xl">
283287
{title}
284288
</h1>
285-
<div className="min-w-0 flex-1 overflow-x-auto">
289+
<div className="min-w-0 flex-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
286290
<ProductRouteMetricChips metrics={metrics} ready={metricsReady} />
287291
</div>
288292
</div>
289-
<p className="line-clamp-3 max-w-3xl text-xs leading-5 text-neutral-400 phone:line-clamp-2 tablet:text-sm">
293+
{/* Full-width one-liner from tablet up; may wrap only on small widths. */}
294+
<p className="w-full min-w-0 text-xs leading-5 text-neutral-400 tablet:truncate tablet:whitespace-nowrap tablet:text-sm">
290295
{summary}
291296
</p>
292297
</ProductEntranceItem>

0 commit comments

Comments
 (0)