Repository source: docs/plans/archive/forte_css_isolation_step4_implementation_summary.md
Forte CSS Isolation Step 4 Implementation Summary
Stage 1 - Renderer/layout support for an extra stylesheet
Changes:
TemplateRenderer::renderStandalonePage()gains an optionalarray $additionalCssPaths = []parameter; each path is fingerprinted via the existingassetPath()and passed to the layout asadditionalCssPaths.templates/standalone_layout.phpemits one additional<link rel="stylesheet">per entry inadditionalCssPaths, after the primarysite.csslink.
Verification:
php -lclean on both changed files.curl http://127.0.0.1:8001/forte— exactly one<link rel="stylesheet">present (unchanged), 200 OK.curl http://127.0.0.1:8001/threads/{id}/forte— exactly one<link rel="stylesheet">present (unchanged), 200 OK.No caller passes the new parameter yet, so both routes render byte-identical to pre-Stage-1 output.
Notes:
Parameter defaults to
[]so no otherrenderStandalonePage()behavior changed.
Stage 2 - Introduce forte.css alongside site.css
Changes:
Added
public/assets/forte.css, a byte-for-byte copy of the.paned-*/body.paned-reader-bodyblock (site.css lines 3425-3878).Application::renderForte()andApplication::renderForteBoard()now pass['/assets/forte.css']as the new$additionalCssPathsargument.site.cssleft untouched in this stage (rules temporarily exist in both files).
Verification:
diff public/assets/forte.css <(sed -n '3425,3878p' public/assets/site.css)— identical.curl http://127.0.0.1:8001/forteandcurl http://127.0.0.1:8001/threads/{id}/forteboth now emit two<link rel="stylesheet">tags (site.*.cssthenforte.*.css), both 200 OK and correctly content-hashed.Headless-Chromium screenshots of
/fortein light mode and OS dark mode match the pre-Stage-2 (post dark-mode-fix) screenshots pixel-for-pixel; toolbar/sort/reply-toggle buttons and profile-link color remain correct.Classic UI pages never reference
forte.css(unaffected; not reloaded in this stage).
Notes:
site.cssstill contains the duplicated block; Stage 3 removes it now thatforte.cssis confirmed live.
Stage 3 - Remove the paned block from site.css
Changes:
Deleted the
.paned-*/body.paned-reader-bodyblock (previously lines 3425-3878) frompublic/assets/site.css; file now ends after.sqlite-tab-panel[hidden].
Verification:
grep -c paned public/assets/site.css— 0.curl http://127.0.0.1:8001/forteand the/threads/{id}/forteroute still emit both stylesheet links (200 OK); headless-Chromium screenshot of/forteunder OS dark mode is pixel-identical to Stage 2's (toolbar/sort/reply-toggle buttons and link color still correct — now sourced solely fromforte.css).Classic UI:
curl http://127.0.0.1:8001/threads/{id}shows the current (post-edit)site.*.csshash; headless-Chromium screenshot of that page under dark mode renders correctly.Aside (not a regression from this change, noted for the record):
curl http://127.0.0.1:8001/and a couple of other root-level routes are served from pre-existing static snapshots inpublic/*.html(from an earlierbuild_static_artifacts.phprun, unrelated to this feature) that PHP's built-in server prefers over the live route when a same-named file exists, so they don't reflect livesite.cssedits until rebuilt./threads/{id}and all Forte routes have no such static twin and always hit the live app.
Notes:
No other
site.cssrule referenced.paned-*selectors, so nothing else needed adjustment.
Stage 4 - Confirm static-build asset fingerprinting picks up forte.css
Changes:
None — verification only, as anticipated in the plan.
Verification:
Ran
FORUM_PUBLIC_ARTIFACT_ROOT=<scratch dir> php scripts/build_static_artifacts.phpagainst a scratch copy ofpublic/.Output
assets/containsforte.00b2e8fed466.css, whose hash matchessha256sum public/assets/forte.css(first 12 hex chars:00b2e8fed466) —AssetFingerprint::copyFingerprintedAssets()picked up the new file with no special-casing needed, exactly as expected from its generic directory scan.
Notes:
No code changes were required for this stage; the generic asset-fingerprinting path already covered the new file.