Skip to content

perf(mcp-apps-shell): drop the shell document's own Tailwind browser compiler - #27

Merged
fforres merged 4 commits into
skywardfrom
perf/shell-drop-tailwind-jit
Oct 18, 2026
Merged

fforres merged 4 commits into
skywardfrom
perf/shell-drop-tailwind-jit

Conversation

@fforres

@fforres fforres commented Oct 18, 2026

Copy link
Copy Markdown

Why

The shell document imported @tailwindcss/browser, but generated code renders in the inner srcdoc frame, which has its own compiler. In the shell, this second compiler only styled chrome that the build-time sheet already covers. It ran against Tailwind's default theme, and its later sheet overrode ours.

Confirmed in a browser test before the change: font-mono in the shell's data view computed to ui-monospace, SFMono-Regular, Menlo, … instead of Geist Mono.

What changes

  • Removes import "@tailwindcss/browser" from shell-app.tsx, and refreshes two comments that named it.
  • The built shell drops from 2,733,014 B to about 2,452,700 B raw, and from 755,362 B to 684,898 B gzip. The compiler bundle was shipped twice: once run in the shell, once as a string for the srcdoc.
  • The inner frame is unchanged; it keeps its runtime compiler.

Tests

mcp-app.browser.test.ts has two new tests:

  • No Tailwind compiler in the shell document: no Create compiler measure in the shell frame.
  • Shell monospace text is Geist Mono.

Both failed before the change and pass after. The full file passes, 26 of 26.

https://claude.ai/code/session_019ZE3j9YzRKxT4LaiDCBv6S

…compiler

The shell document imported @tailwindcss/browser although generated code now
renders in the inner frame, which has its own compiler. The shell chrome is
styled by the build-time sheet, so the second compiler only duplicated rules
against the default theme (font-mono fell back to the system monospace) and
shipped the 282 KB bundle twice in the shell HTML.

Claude-Session: https://claude.ai/code/session_019ZE3j9YzRKxT4LaiDCBv6S
Add a React-free precompileArtifact() that runs the Sucrase JSX transform and a
Tailwind build against the same input the sandboxed frame compiles at runtime.
Class candidates come from a bracket-aware source scanner, and code that builds
class names dynamically is detected so it keeps the runtime path. A generated
fingerprint over the compiler, extractor, theme and pinned versions marks
compiled output as current. Nothing is wired yet.

Pin tailwindcss and @tailwindcss/browser to one catalog version so the
precompiled CSS and the runtime fallback agree.

Claude-Session: https://claude.ai/code/session_019ZE3j9YzRKxT4LaiDCBv6S
…nder reads

Add an optional ArtifactCompiler port to the executor config. When a host
provides one, artifacts.save compiles the source and stores the fingerprint,
JS and CSS in three new nullable columns. The compile step is fail-open: a
failure saves the artifact with empty compiled columns. artifacts.get stays
light and does not read the compiled columns. The new artifacts.getForRender
returns them, and recompiles and writes back output stored under an older
fingerprint, guarded on the source it compiled so a concurrent save wins.

The HTTP get endpoint now returns an optional compiled field. The save payload
is unchanged, so clients cannot supply compiled output. host-cloudflare wires
the compiler through a lazy import. Includes the cloud schema and migration
regeneration for the new columns.

Claude-Session: https://claude.ai/code/session_019ZE3j9YzRKxT4LaiDCBv6S
…in the frame

When an artifact arrives with output from the server compiler that matches the
shell's own fingerprint, the renderer frame embeds the stored CSS and runs the
stored JS. It loads neither the Tailwind browser compiler nor Sucrase, so the
srcdoc is smaller and the first paint is already styled. Any mismatch, or a
missing half (classes built at runtime keep Tailwind, uncompilable code keeps
Sucrase), falls back to the previous behavior per half.

Sucrase moves out of the inner renderer into its own bundle served as
virtual:executor-inner-compiler and only included when needed. The console
passes the stored compiled output to the shell on both the tool input and the
tool result. The artifact-style skill now tells the model to write every class
as a complete literal.

Claude-Session: https://claude.ai/code/session_019ZE3j9YzRKxT4LaiDCBv6S
@fforres
fforres merged commit 5d36745 into skyward Oct 18, 2026
3 of 39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant