From 0b182cb7d6585512aa745d42973c86bb651a3d6b Mon Sep 17 00:00:00 2001 From: Peter Hedenskog Date: Tue, 12 May 2026 06:20:09 +0200 Subject: [PATCH] Page-x-ray: per-section colors and round CLS to 3 decimals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The section headers all shared a single grey background, so six identical bars stacked down the table looked off — felt more like visual noise than visual grouping. Give each section its own quiet pastel/text pair (Content blue, Render blocking amber, Visual metrics lavender, Core Web Vitals mint, CPU peach, Captures grey) so the eye can find the right group at a glance. While in there, format Cumulative Layout Shift to 3 decimals instead of dumping the raw float (was rendering as 0.05641193152186011, now 0.056) — that's the granularity that matters for comparison and matches the sitespeed.io HTML report. Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com --- public/js/compare/templates.js | 30 +++++++++++++++++++----------- src/css/page-xray.css | 32 ++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/public/js/compare/templates.js b/public/js/compare/templates.js index fe5e8bc..a566c14 100644 --- a/public/js/compare/templates.js +++ b/public/js/compare/templates.js @@ -66,8 +66,9 @@ function pageXrayTemplate(d) { const p1 = d.p1, p2 = d.p2, config = d.config; const showRuns = d.runs1.length > 1 || d.runs2.length > 1; - function section(title) { - return '' + h(title) + ''; + function section(title, kind) { + const cls = 'pageXraySection' + (kind ? ' pageXraySection--' + kind : ''); + return '' + h(title) + ''; } let html = ''; @@ -123,7 +124,7 @@ function pageXrayTemplate(d) { '' + h(p2.meta.connectivity) + ''; } - html += section('Content'); + html += section('Content', 'content'); html += 'Total' + '' + p1.requests + ' (' + formatBytes(p1.transferSize) + ' / ' + formatBytes(p1.contentSize) + ')' + '' + p2.requests + ' (' + formatBytes(p2.transferSize) + ' / ' + formatBytes(p2.contentSize) + ')'; @@ -144,7 +145,7 @@ function pageXrayTemplate(d) { '' + img2.requests + ' (' + formatBytes(img2.transferSize) + ')'; if (p1.renderBlocking && p2.renderBlocking) { - html += section('Render blocking'); + html += section('Render blocking', 'blocking'); html += 'Render blocking' + '' + p1.renderBlocking.blocking + '' + '' + p2.renderBlocking.blocking + ''; @@ -176,11 +177,18 @@ function pageXrayTemplate(d) { '' + formatTime(p2.visualMetrics[key]) + ''; } }); - if (vmHtml) html += section('Visual metrics') + vmHtml; + if (vmHtml) html += section('Visual metrics', 'visual') + vmHtml; } if (p1.googleWebVitals && p2.googleWebVitals) { - html += section('Core Web Vitals'); + // CLS is a unitless float that browsers report at full precision + // (e.g. 0.05641193152186011). Round to three decimals — that's + // the granularity that matters for comparison and matches the + // convention used in the sitespeed.io HTML report. + function fmtCLS(v) { + return typeof v === 'number' ? v.toFixed(3) : (v == null ? '' : v); + } + html += section('Core Web Vitals', 'cwv'); html += 'First Contentful Paint' + '' + formatTime(p1.googleWebVitals.firstContentfulPaint) + '' + '' + formatTime(p2.googleWebVitals.firstContentfulPaint) + ''; @@ -191,8 +199,8 @@ function pageXrayTemplate(d) { '' + formatTime(p1.googleWebVitals.totalBlockingTime) + '' + '' + formatTime(p2.googleWebVitals.totalBlockingTime) + ''; html += 'Cumulative Layout Shift' + - '' + p1.googleWebVitals.cumulativeLayoutShift + '' + - '' + p2.googleWebVitals.cumulativeLayoutShift + ''; + '' + fmtCLS(p1.googleWebVitals.cumulativeLayoutShift) + '' + + '' + fmtCLS(p2.googleWebVitals.cumulativeLayoutShift) + ''; } if (p1.cpu && p2.cpu && p1.cpu.longTasks && p2.cpu.longTasks) { @@ -212,7 +220,7 @@ function pageXrayTemplate(d) { '' + p1.cpu.longTasks.tasks + '' + '' + p2.cpu.longTasks.tasks + ''; } - if (cpuHtml) html += section('CPU') + cpuHtml; + if (cpuHtml) html += section('CPU', 'cpu') + cpuHtml; } if (d.cpuCategories1 && d.cpuCategories2) { @@ -221,7 +229,7 @@ function pageXrayTemplate(d) { // CPU has no long-task data but we still want the disclosure rows // visually under a heading). if (!(p1.cpu && p2.cpu && p1.cpu.longTasks && p2.cpu.longTasks)) { - html += section('CPU'); + html += section('CPU', 'cpu'); } html += ' CPU time spent by category ' + '