Last 12 weeks · 2 commits
3 of 6 standards met
The old image installed a non-existent critical@9.0.0 from npm and hand-maintained X11 libs from the Puppeteer era, which broke the build. v9 uses the static engine by default (no browser) with Playwright as an optional peer dependency for the render engine. Base on mcr.microsoft.com/playwright, install critical from committed source, and pin Playwright via a single ARG.
Fills in the v9 test surface — and quietly fixes what the rewrite dropped Follow-up to #623. The rewrite was a clean break with a deliberately small surface, but two things were left thin: the parts it kept had almost no direct tests, and a handful of everyday stylesheet behaviors from v8 stopped working without anyone noticing. This PR closes both gaps. Everything new is static-engine only — deterministic, no browser — so it runs in milliseconds and the render suites stay untouched. The fixes came out of writing the tests Each of these was found by writing a test for a feature that was supposed to still work, and watching it fail: loaded nothing. A cache-busting query string was treated as part of the filename, so the file was never found. We now strip / before touching the disk (remote URLs keep theirs). URI stylesheets were ignored. `/assets/--no-minify--no-fold--helpparseArgs--no-cli.test.js--inline--json--explain-e--no-minify--out--writecss-option.test.jscssurl()fold-aware.test.js[data-critical-fold]foldAware:falseissues.test.js@layer@container:where:hasmcp.test.jsoptimize_critical_cssapi.test.jshtmlsrcminifyreportsource.test.js#fragmentdata:extractinlineImagesignoreincludeconcurrencycleanCSSpenthouserequestnode --test` — 58/58 pass. New tests are browser-free; the render suites skip cleanly where Chromium isn't installed. Covers #566, #580, #613; continues #623.
I wrote the first version of Critical back in 2014. The bet it made was load the page in a real headless browser, measure what is genuinely above the fold at a given viewport, and inline exactly that. Penthouse and Chrome did the heavy lifting. That bet was right at the time. What aged was everything around it. Critical grew to roughly 30 runtime dependencies, launched a browser on every single run even when it did not need one, and wore an API shaped by its era: Gulp streams, callbacks, vinyl files, a pile of small utility modules that Node has since absorbed into the standard library. Meanwhile the ground moved: Critters showed you can skip the browser entirely for prerendered HTML and match used CSS against the delivered DOM. Critters is archived now, and Beasties (maintained by the Nuxt team) carries that approach forward and is being wired into framework builds. Lightning CSS replaced a whole stack of JS tools (clean-css, postcss, cssnano-style passes) with one fast Rust core. Node itself grew up. , , , , - a dozen of Critical's dependencies became one-liners. Playwright became the modern automation surface, and serverless Chromium made real rendering viable inside a build step. And the newest shift: coding agents now decide when and how to run tools like this. That changes the contract. The output has to be legible to a program, not just to a person reading a terminal. So this is the first major rehaul of the project. Its a rebuild from the inside out. The core idea: two engines, routed automatically For years the choice was framed as accuracy (Critical, real browser) versus speed (Critters/Beasties, no browser). I no longer think that should be a choice. The delivered HTML tells you which one you need. Critical v9 ships two engines and picks between them for you: static - no browser. It matches CSS rules against the delivered DOM and keeps what is used. This is the fast path, and it runs in milliseconds. It is the right answer whenever the HTML you ship already contains your content (SSG, SSR, MPAs). render - a real browser at a real viewport, measuring what actually paints above the fold. This is the original Critical bet, modernized on Playwright. It is the right answer for single-page-app shells (an empty that only becomes a page after JS runs) and for when you want a tight, viewport-accurate set rather than all used CSS. With (the default), Critical sniffs the document. Fully rendered page, it runs the cheap correct engine. Empty app shell, it escalates to the browser, because there is nothing to match statically. Either way it tells you which path it took and why. What changed, at a glance Two engines with automatic routing (above). Lightning CSS for parse, minify, and dead-code elimination, replacing the clean-css/postcss stack. linkedom + css-tree for DOM and rule walking, replacing vinyl/oust/cheerio-era machinery. A modern CLI on with zero-config directory input, , and . Deterministic output. Same input, byte-identical bytes. Safe to diff in version control and assert on in CI. The async-load pattern is now the polyfill-free swap with a `optimize_critical_cssnode --testlightningcsslinkedomcss-treereport--json--explaincritical ./distCOMPARISON.mdnpm run benchgenerate(opts, cb)critical(opts){ css, html, uncritical }{ css, html, report }stream()target: { css, html, uncritical }result.cssresult.html--write--outpenthouserequestcleanCSSextractinlineImagesassetPathsrebaseignoreuserpassstrictwidthheightdimensionscssbasehtmlsrcuserAgentengineminifyfoldAwareextractnode --testnode --testdebugdebug_1.default is not a functionfile://ERR_ABORTEDtarget--write--outresult.cssresult.htmlinlineImagesconcurrencyincludeignore` options are gone, so those doc requests no longer apply.
Repository: addyosmani/critical. Description: Extract & Inline Critical-path CSS in HTML pages Stars: 10265, Forks: 389. Primary language: JavaScript. Languages: JavaScript (87.1%), HTML (6.1%), CSS (5.4%), Dockerfile (1%), TypeScript (0.4%). License: Apache-2.0. Topics: critical-css, critical-path-css, critical-path-styles, css, inline-css, inline-styles. Latest release: v8.0.0 (3mo ago). Open PRs: 3, open issues: 34. Last activity: 3w ago. Community health: 57%. Top contributors: bezoerb, addyosmani, XhmikosR, sindresorhus, dependabot[bot], pocketjoso, vladh, micahblu, patrikholcak, samccone and others.