Skip to content

Critical CSS#

With critical_css on, BB Cache inlines the CSS a page needs for its first paint and loads every stylesheet asynchronously. The plugin does not generate critical CSS. It composes it per page from files that ship in a folder, normally inside the theme.

Tied to the originating theme. The per-page composition keys on two class patterns from the theme the plugin was built for: content-section--<layout> on each section and page-masthead on the masthead. A theme that does not print these classes gets base.css only. Making the patterns configurable is planned. The legacy per-template scheme described at the end of this page has no such dependency.

The fragment folder#

The folder is the critical_dir setting. Blank means <active theme>/assets/dist/css/critical (get_stylesheet_directory(), so the child theme when one is active).

File Required Used for
base.css Yes. Its presence switches composition on Rules for everything outside the sections: header, navigation, footer, typography, @font-face
section-<layout>.css One per layout that can appear on a page Rules for elements inside a section with the class content-section--<layout>
masthead.css When any page prints an element with the class page-masthead Rules for the masthead
post-<ID>.css No Extra rules for one post or page, appended last on that singular view

Fragments are expected to be minified CSS. A rule used by several layouts should be present in each of their fragments, so every fragment is complete on its own; the duplicates are removed at composition.

How a page is composed#

BB_Cache_Boost::critical_css() runs on the page's HTML during the Boost step:

  1. It takes the markup from <body> on and removes <style>, <script>, <noscript> and <template> blocks, so only class names in real markup count. A selector inside an inlined stylesheet is not a section on the page.
  2. It reads every class attribute that contains content-section--. The layout name is the part after the prefix, lower-cased, made of letters, digits, _ and -, and cut at the first --. For example content-section--quote_callout--shortcode is the layout quote_callout.
  3. If the markup contains page-masthead, the masthead is added.
  4. Layouts are ordered by first appearance in the body.
  5. The result is base.css, then each layout's fragment in that order, then post-<ID>.css if it exists.
  6. Duplicate rules are dropped: each rule is kept the first time it appears within the same chain of @media, @supports, @container or @layer wrappers. @font-face and @keyframes blocks are kept whole.
  7. The bb_cache_critical_css filter receives the composed CSS and the HTML.

Then, in the page:

  • The CSS is inlined as <style id="bb-critical"> and moved up in the head, to just before the first <link>, <style> or <script> (BB Cache's own inline loader scripts excepted), so it follows the charset and viewport metas.
  • Every <link rel="stylesheet"> is switched to media="print" with an onload handler that restores its media value. The original links are repeated inside a <noscript> before </head>.
  • Links that already have an onload attribute, have data-no-async, or are media="print" are left alone.

Page-scoped stylesheets#

Stylesheets that apply to one page or a few pages cannot be part of the shared fragments. Loaded asynchronously they would arrive late and shift what they style. The critical_inline setting lists such sheets by a substring or regex of their href. A matching sheet is inlined whole as <style data-bb-inline="<file name>"> when it is a local file of at most 24 KB (24,576 bytes). Relative url() paths are rewritten and any </style text in the sheet is escaped. Larger or non-local sheets fall back to the async treatment.

The default list is site-specific. Replace it with the paths of your own page-scoped sheets. See the settings reference.

When a fragment is missing#

If a section's layout has no section-<layout>.css (or the page has a masthead and there is no masthead.css), the page gets no critical CSS at all:

  • nothing is inlined and the stylesheets stay render-blocking
  • the head carries a marker naming the layouts
<meta name="bb-critical" content="no fragment for pricing_table, logo_wall">

Blocking stylesheets only delay the first paint. An incomplete inline block would let a section paint unstyled and then re-lay out when its sheet arrives, which is a layout shift of up to a full viewport. The plugin prefers the slower, stable page.

One exception: a layout without a fragment counts as covered when the same class attribute also carries a layout that has one. This covers sections that print two layout classes.

To find affected pages, search the HTML for the marker:

curl -s https://example.com/pricing/ | grep -o '<meta name="bb-critical"[^>]*>'

The marker is a meta tag, not a comment, because HTML minify removes comments.

Generating the fragments#

The generator is not part of this plugin yet. It lives in the originating project as tools/critical-css.js, a Node script that drives headless Chrome. The description below comes from that project's notes; it cannot be checked against this repository.

  • It renders a list of sample pages (a few per layout, plus mastheads and listing pages) and assigns each rule of the theme's own stylesheets to a file by where the matching elements live: outside any section goes to base.css, inside content-section--<layout> to section-<layout>.css, inside .page-masthead to masthead.css.
  • Single-class rules of the theme's core sheet go to base.css whether or not a sample page used them, so a utility class an editor turns on later is covered.
  • The fragments are written into the theme's assets/dist/css/critical folder and deployed with the theme. Nothing is generated on the server.

Until the generator ships with the plugin, you need your own way to produce the files. Any tool that yields the layout described under The fragment folder works; so does the legacy scheme below with any critical CSS tool that writes one file per template.

Regenerate after a change to the theme's CSS or templates: the inline copy does not update itself. Content changes need no regeneration unless a page starts using a layout that has no fragment.

Checking what exists#

Settings → BB Cache → Tools lists the folder and the files found with their size and age.

wp bb-cache critical status

Legacy scheme: one file per template#

While the folder has no base.css, the earlier scheme applies. The page gets the contents of one file:

  1. post-<ID>.css on a singular view, if it exists
  2. otherwise <key>.css
Request Key
Front page home
Page with a page template tpl-<template slug> (the template file name without templates/ and .php)
Page without a template page
Other singular single-<post type>
Post type archive archive-<post type>
Category, tag or taxonomy archive tax-<taxonomy>
Posts page blog
Search search
404 404
Anything else page

The bb_cache_critical_key filter can change the key. wp bb-cache critical targets prints up to three sample URLs per key as JSON, as input for a generator. If no file matches, the page keeps blocking stylesheets; this scheme writes no marker.

The per-section scheme replaced this one because pages of one template can differ in their sections: critical CSS generated from a few sample pages of a template missed sections that only other pages of that template carried.