Skip to content

perf(ui): keep lazy-page CSS out of the Control UI entry stylesheet#110687

Merged
steipete merged 1 commit into
mainfrom
claude/css-budget
Jul 18, 2026
Merged

perf(ui): keep lazy-page CSS out of the Control UI entry stylesheet#110687
steipete merged 1 commit into
mainfrom
claude/css-budget

Conversation

@steipete

Copy link
Copy Markdown
Contributor

What Problem This Solves

Main is red on every push since 74880cb: all four QA Smoke CI profiles fail the Control UI performance gate with "startup CSS gzip: 42.0 KiB exceeds 42.0 KiB" (run 29643357786). Bisected by building ui/ at each commit: no single culprit — #110605 (+0.21 KiB) and #110654 (+0.14 KiB) tipped a budget that was already 97% full.

The structural cause: the entry stylesheet (ui/src/styles.css) aggregates approval.css, config.css, config-quick.css, and lobster-pet.css even though every consumer is a lazy route chunk (pages own route.ts dynamic imports) or a lazily defined element (lobster-pet.ts is already import()ed by the sidebar).

Why This Change Was Made

Move each stylesheet to its owning lazy module — the established pattern (chat, agents, cron pages already import their own CSS). The shell's pre-hydration approval booting state (app-host.ts renders .approval-page--booting before the lazy page loads) keeps its subset in the entry via the new approval-boot.css. Consumer map verified by selector-leak grep: config-quick.css also feeds profile-page.ts (lazy), lobster-pet.css classes are used by about/memory/config views (all lazy) — each gets its own import.

Startup CSS drops 42.0 → 36.0 KiB gzip; the budget tightens 42 → 38 KiB to lock in the recovery, matching the startup-JS precedent (#110528: lazy-load + tighten). Total CSS grows 2.4 KiB from per-chunk duplication — the intended trade for startup weight.

User Impact

Main CI green again; Control UI first paint ships 6 KiB less CSS; the budget gate keeps future page styles from silently re-filling the entry.

Evidence

  • node scripts/check-control-ui-performance.mjs after vite build: startup CSS 36.0 KiB / 38.0 limit, one request, no violations (before: 42.0/42.0 violation reproduced locally).
  • Bisect table (entry CSS gzip, local build): f98bcb7 41.63 → 39ddf71 41.84 → 74880cb 41.98 KiB.
  • Full vitest.ui.config.ts run: no new failures (the only failing suites are the three storage tests already proven to fail identically on clean main under local Node 26).
  • Selector-leak audit for every moved stylesheet; .approval-page--booting shell dependency handled via the boot subset split.
  • Codex autoreview: running on this bundle; will be clean before landing.

The entry stylesheet aggregated approval, config, config-quick, and
lobster-pet styles even though every consumer is a lazy route chunk or
lazily defined element, and two small UI landings tipped startup CSS to
exactly the 42.0 KiB budget, failing all QA Smoke profiles on main (run
29643357786). Move each stylesheet to its owning lazy module — pages
already own their CSS this way (chat, agents, cron) — and keep only the
shell's pre-hydration approval booting subset in the entry via the new
approval-boot.css. Startup CSS drops 42.0 -> 36.0 KiB gzip; tighten the
budget 42 -> 38 KiB to lock in the recovery, matching the startup-JS
precedent from #110528.
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui scripts Repository scripts size: S maintainer Maintainer-authored PR labels Jul 18, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1f2d302550

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -1,3 +1,5 @@
import "../../styles/config.css";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Move shared config-form CSS out of the config route

Importing config.css only from openclaw-config-page strands selectors that are still shared outside the config route: ui/src/pages/channels/view.config.ts renders renderNode() with the cfg-* classes, and ui/src/pages/plugins/view.ts renders mcp-server-form, while rg "config.css" ui/src shows no production import from those routes. Visiting Channels channel config or Plugins MCP management directly will now load their chunks without the form/MCP CSS that used to be in the entry stylesheet, so those forms lose their intended layout/states; split those shared selectors into a component/global stylesheet imported by the non-config consumers, or keep that subset in the entry.

Useful? React with 👍 / 👎.

Comment thread ui/src/styles.css
@import "./styles/approval.css";
@import "./styles/config.css";
@import "./styles/config-quick.css";
@import "./styles/approval-boot.css";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep logs fill-height CSS with the logs route

Removing config-quick.css from the entry also removes the only desktop logs layout rules: that file still owns .content:has(openclaw-logs-page), openclaw-logs-page, .logs-card, and .log-stream, while ui/src/pages/logs/route.ts only imports logs-page.ts and no logs module imports config-quick.css. On the /logs page, which renders renderSettingsWorkspace(body, { fillHeight: true }), the stream will fall back to the generic max-height: calc(100vh - 280px) instead of filling the remaining viewport; move those selectors to a logs stylesheet imported by the logs page, or keep the logs subset in the entry.

Useful? React with 👍 / 👎.

@steipete
steipete merged commit d3173f6 into main Jul 18, 2026
132 of 136 checks passed
@steipete
steipete deleted the claude/css-budget branch July 18, 2026 12:26
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

coder-master-0915 pushed a commit to coder-master-0915/openclaw that referenced this pull request Jul 18, 2026
…penclaw#110687)

The entry stylesheet aggregated approval, config, config-quick, and
lobster-pet styles even though every consumer is a lazy route chunk or
lazily defined element, and two small UI landings tipped startup CSS to
exactly the 42.0 KiB budget, failing all QA Smoke profiles on main (run
29643357786). Move each stylesheet to its owning lazy module — pages
already own their CSS this way (chat, agents, cron) — and keep only the
shell's pre-hydration approval booting subset in the entry via the new
approval-boot.css. Startup CSS drops 42.0 -> 36.0 KiB gzip; tighten the
budget 42 -> 38 KiB to lock in the recovery, matching the startup-JS
precedent from openclaw#110528.
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 19, 2026
…penclaw#110687)

The entry stylesheet aggregated approval, config, config-quick, and
lobster-pet styles even though every consumer is a lazy route chunk or
lazily defined element, and two small UI landings tipped startup CSS to
exactly the 42.0 KiB budget, failing all QA Smoke profiles on main (run
29643357786). Move each stylesheet to its owning lazy module — pages
already own their CSS this way (chat, agents, cron) — and keep only the
shell's pre-hydration approval booting subset in the entry via the new
approval-boot.css. Startup CSS drops 42.0 -> 36.0 KiB gzip; tighten the
budget 42 -> 38 KiB to lock in the recovery, matching the startup-JS
precedent from openclaw#110528.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui maintainer Maintainer-authored PR scripts Repository scripts size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant