From harness-claude
Optimizes browser's 5-stage critical rendering path (DOM/CSSOM/Render Tree/Layout/Paint) to cut time-to-first-paint by spotting render-blocking resources, measuring critical metrics, and inlining CSS. Use for blank screens, slow paints, or Lighthouse flags.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Understand and optimize the browser's 5-stage pixel pipeline — Parse HTML to DOM, Parse CSS to CSSOM, Render Tree construction, Layout, Paint and Composite — to minimize time-to-first-paint and eliminate render-blocking bottlenecks.
Measures and optimizes Largest Contentful Paint (LCP) by decomposing into TTFB, resource load delay, load time, and render delay phases with targeted fixes like preload links and TTFB reduction. For Lighthouse >2.5s or slow hero images.
Provides page speed optimization guidelines and HTML/CSS/JS patterns to meet Core Web Vitals targets (LCP<2.5s, FID<100ms, CLS<0.1) for funnel pages.
Optimizes website and web app performance by measuring Core Web Vitals with Lighthouse, analyzing bundle sizes and bottlenecks, and implementing caching, code splitting, and asset optimizations.
Share bugs, ideas, or general feedback.
Understand and optimize the browser's 5-stage pixel pipeline — Parse HTML to DOM, Parse CSS to CSSOM, Render Tree construction, Layout, Paint and Composite — to minimize time-to-first-paint and eliminate render-blocking bottlenecks.
<head> and you need to determine which are render-blockingMap the critical rendering path. Open Chrome DevTools Performance panel with CPU throttling set to 4x slowdown. Record a page load. Identify these 5 stages in the flame chart:
display: none, <head>)Identify render-blocking resources. In DevTools Network panel, filter by "Render-blocking" or look for CSS <link> tags and synchronous <script> tags in the <head>. Every render-blocking resource adds its download and parse time to time-to-first-paint.
Measure the three critical path metrics:
Inline critical CSS. Extract the CSS required for above-the-fold content (tools: Critical, Critters, PurgeCSS) and inline it in a <style> tag in <head>. Keep inlined CSS under 14KB — this matches TCP's initial congestion window (10 segments x ~1,460 bytes), meaning it arrives in the first network roundtrip.
<head>
<style>
/* Critical CSS — only above-the-fold styles, under 14KB */
.hero {
display: flex;
min-height: 60vh;
}
.nav {
position: sticky;
top: 0;
background: #fff;
}
</style>
<link
rel="preload"
href="/styles/main.css"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
/>
<noscript><link rel="stylesheet" href="/styles/main.css" /></noscript>
</head>
Eliminate parser-blocking scripts. Add defer or async to every <script> tag that does not need to run before first paint. Use defer for scripts that depend on DOM order; use async for independent scripts like analytics.
<!-- Parser-blocking (bad) — blocks HTML parsing until downloaded and executed -->
<script src="/app.js"></script>
<!-- Deferred (good) — downloads in parallel, executes after HTML parsing completes -->
<script src="/app.js" defer></script>
<!-- Async (good for independent scripts) — downloads in parallel, executes when ready -->
<script src="/analytics.js" async></script>
Use media queries on non-critical CSS. A print stylesheet should not block rendering on screen:
<!-- Render-blocking for all media (bad) -->
<link rel="stylesheet" href="/print.css" />
<!-- Only blocks rendering when printing (good) -->
<link rel="stylesheet" href="/print.css" media="print" />
Enable streaming HTML. Configure the server to flush the <head> section immediately (including critical CSS and preload hints) before the backend finishes generating the <body>. This allows the browser to start fetching critical resources while the server is still processing.
The browser processes each frame through five stages, and each stage depends on the output of the previous one:
DOM Construction — The HTML parser reads bytes, decodes characters, tokenizes tags, and builds nodes into the Document Object Model. This is incremental — the browser can start constructing the DOM before the entire HTML document arrives.
CSSOM Construction — CSS is not incremental. The browser must fully parse all CSS before it can compute styles, because later rules can override earlier ones (the cascade). This is why CSS is render-blocking: the browser will not paint until the CSSOM is complete.
Render Tree — Combines DOM and CSSOM. Walks the DOM, finds matching CSSOM rules for each visible node, and produces a tree of visible elements with computed styles. Elements with display: none are excluded entirely. Elements with visibility: hidden are included (they occupy space).
Layout (Reflow) — Converts the render tree's relative units (%, em, vh) into absolute pixel positions. Computes exact box geometry for every element. This is computationally expensive for deep DOM trees.
Paint and Composite — Rasterizes each layer into pixels, then composites layers together respecting z-order, transforms, and opacity. GPU-accelerated compositing handles transform and opacity changes without repaint.
Google.com achieves first paint in under 1 second on 3G networks. The technique: the entire critical CSS (~4KB) is inlined in the <head>, and the HTML payload for the search page is under 14KB total. This means the complete above-the-fold content arrives in the first TCP roundtrip (initial congestion window = 10 TCP segments = ~14KB). No additional network roundtrips are needed before the browser can construct DOM, CSSOM, render tree, and paint. External CSS and JavaScript are loaded with defer or dynamically injected after first paint.
Shopify reduced their critical rendering path from 8 critical resources to 3 by auditing their storefront themes. The original CRP included 4 external CSS files and 4 synchronous JS files in <head>. They inlined critical CSS (6KB), deferred all JavaScript, and combined remaining CSS into one file loaded with media="print" and swapped to media="all" on load. The result: Start Render improved by 50% (from 3.2s to 1.6s on 3G) because the critical path length dropped from 4 roundtrips to 1.
Render-blocking CSS without media queries. Loading print.css without media="print" forces the browser to download and parse it before rendering anything on screen. Every CSS <link> in <head> without a specific media query blocks rendering for all media types.
Synchronous scripts in <head> without defer or async. A <script src="..."> tag without attributes blocks HTML parsing entirely — the parser stops, downloads the script, executes it, then resumes parsing. This can add seconds to first paint if the script is large or on a slow connection.
Over-inlining CSS. Inlining the entire CSS framework (200KB+ of Tailwind or Bootstrap) defeats the purpose. Inlined CSS cannot be cached separately, so users re-download it on every page navigation. Inline only the critical above-the-fold CSS; load the rest asynchronously.
Invisible text during font loading. Using font-display: block (the default for many font services) makes text invisible until the web font downloads. On slow connections, users see a blank content area for 1-3 seconds. Use font-display: swap or font-display: optional to show fallback text immediately.
The browser assigns fetch priority to resources based on their type and position:
<link> in <head> — Highest priority, render-blocking<script> in <head> — High priority, parser-blocking<script defer> — Low priority during parse, executes in order after DOMContentLoaded<script async> — High priority download, executes immediately when ready (out of order)<img> above the fold — High priority (browsers use heuristics to detect viewport position)<img> below the fold — Low priority<link rel="preload"> — High priority early fetch without blocking renderUse fetchpriority="high" to boost critical images (hero images, LCP candidates) and fetchpriority="low" to deprioritize below-the-fold resources that the browser might otherwise fetch eagerly.
// Measure critical path timing with Navigation Timing API
const timing = performance.getEntriesByType('navigation')[0];
const ttfb = timing.responseStart - timing.requestStart;
const domParsing = timing.domInteractive - timing.responseStart;
const cssBlocking = timing.domContentLoadedEventStart - timing.domInteractive;
console.log(`TTFB: ${ttfb}ms, DOM parsing: ${domParsing}ms, CSS blocking: ${cssBlocking}ms`);