Step 9: Restyle in Tailwind
Steps 3–6 made the pages look native by reusing WordPress's own stylesheet: about 175 KB of CSS inlined in every page. Once the markup matches, you can replace that stylesheet with the renderer's own Tailwind CSS, keeping the score. On Twenty Twenty-Five this took five small tasks and two days, and the score went up to 94.2% / 90.9%.
Keep a switch, and measure both sides
Add a style mode before changing any styles (src/lib/styleMode.ts, FLX_RENDER_STYLE_MODE):
wp: today's output, WordPress's full global styles inlined. This becomes the rollback.- the new mode: only the theme's preset variables (
--wp--preset--*, about 4 KB of the 175 KB) are inlined; everything else comes from the renderer's stylesheet.
In CI, capture the same pages in both modes, on the same content (run the second capture before any check that adds posts), and report both tables. Switch the default only when the new mode passes the same gate.
Order of work
- Tokens (
src/styles/tailwind.css,@theme inline): map each preset to a Tailwind token that reads the WordPress variable:bg-wp-base,text-wp-contrast,font-wp-manrope,text-wp-large,p-wp-50,max-w-wp-content. A site's palette, font and size choices then still apply. - Chrome in utilities (header, menu, footer, post lists, pagination, search form, 404): put the theme's layout rules in one place as class strings (
src/platforms/wp/twentytwentyfive/tw.ts: global padding, alignfull and alignwide, constrained children, block gap) and use them in the templates. Keep thewp-block-*class names as hooks for the menu script and plugins. The copied per-pagewp-container-*rules from steps 4 and 6 go away. - Templates and text blocks: rebuild the page and post templates from the theme's
templates/page.htmlandsingle.html. Port WordPress's block-library CSS for layout and text blocks into a cascade layer (src/styles/content.css), reading the same presets. Inside post content, restore browser defaults (margins, list bullets) withrevert: native content relies on them, and Tailwind's preflight removes them. - Media and site blocks (
media.css,site-blocks.css): images, galleries, covers, embeds, search, latest posts, social icons. Two values WordPress prints per block (the gallery gap) or on<body>(wp-embed-responsive, which embed aspect ratios depend on) must be supplied by the renderer. - Switch the default when both modes score the same. Keep
FLX_RENDER_STYLE_MODE=wpas a one-variable rollback.
A local loop before each CI run
A CI run takes about ten minutes. To iterate faster, save the payloads the CI run sent (they are in its artifacts), render them locally with the current build in both modes, and compare each page against a frozen copy of the production render and against the native HTML: a pixel match at 1280 and 390 px, plus an element-by-element diff of boxes and key computed styles (font, size, colour, spacing). Then use the CI run to confirm. The script used for Twenty Twenty-Five is compare.mjs in the project's ops folder (Playwright and pixelmatch); any similar script works.
Results on Twenty Twenty-Five
| Task | wp mode (live) |
new mode |
|---|---|---|
| tokens and switch | 80.9 / 77.4 | 51.7 / 56.4 (baseline) |
| chrome in utilities | 80.9 / 77.3 | 63.1 / 69.4 |
| templates, layout and text blocks | 91.2 / 86.4 | 86.6 / 86.3 |
| media and site blocks | 94.2 / 90.9 | 94.2 / 90.9 |
The template work raised both modes, because it fixed markup, not only styles. Page HTML went from about 187 KB to 17 KB (26 KB to 4 KB compressed). The stylesheet (52 KB, 10 KB compressed) is cached by the browser across pages under a versioned URL (Flx Render).
Known gap of the new mode: a site's Additional CSS and Global Styles edits other than preset values are not applied.
Check: the CI gate passes in the default mode, and the rollback mode's report matches it.
Published from rickey29/twenty-twenty-five at commit 837705d · Flx Modernize 1.11.0 · Flx Render 1.11.0