feat(web): apply host styles via skybridge({ hostStyles }) opt-in - #1012
feat(web): apply host styles via skybridge({ hostStyles }) opt-in#1012qchuchu wants to merge 1 commit into
Conversation
On the MCP Apps runtime, wire the ext-apps applyDocumentTheme / applyHostStyleVariables / applyHostFonts helpers into McpAppBridge so the host's theme, CSS variables and fonts flow onto the view. Gated behind a new opt-in flag threaded plugin -> mountView -> bridge; off by default so existing apps are never silently restyled. - plugin: SkybridgePluginOptions.hostStyles, forwarded into the generated mountView(...) call - mountView: MountViewOptions.hostStyles, seeds the bridge before render - McpAppBridge: applyStylesEnabled gate on the (idempotent) apply in updateContext - docs: "Match the Host Styles" section in build/view + useLayout theme tip - skills: host-styles note in the shared ui-guidelines reference Addresses the `styles` context-property gap in #14. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
| const { McpAppBridge } = await import("./bridges/mcp-app/bridge.js"); | ||
| McpAppBridge.getInstance({ | ||
| applyHostStyles: options?.hostStyles ?? false, | ||
| }); |
There was a problem hiding this comment.
When a view initializes a Skybridge store at module scope, createStore() constructs the bridge with host styling disabled before this dynamic import runs. The later getInstance({ applyHostStyles: true }) call cannot update the existing singleton, so the host theme, CSS variables, and fonts are never applied despite the explicit opt-in.
Knowledge Base Used: Core Web Bridges
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/src/web/mount-view.ts
Line: 62-65
Comment:
**Host-style opt-in is ignored**
When a view initializes a Skybridge store at module scope, `createStore()` constructs the bridge with host styling disabled before this dynamic import runs. The later `getInstance({ applyHostStyles: true })` call cannot update the existing singleton, so the host theme, CSS variables, and fonts are never applied despite the explicit opt-in.
**Knowledge Base Used:** [Core Web Bridges](https://app.greptile.com/skybridge/-/custom-context/knowledge-base/alpic-ai/skybridge/-/docs/core-web-bridges.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| if (context.styles?.css?.fonts) { | ||
| applyHostFonts(context.styles.css.fonts); |
There was a problem hiding this comment.
When the host sends different font CSS in a later host-context-changed event, applyHostFonts retains the existing host-font style element instead of replacing its contents. The original @font-face definitions remain active, so the view does not stay synchronized with the host's fonts.
Knowledge Base Used: Core Web Bridges
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/src/web/bridges/mcp-app/bridge.ts
Line: 279-280
Comment:
**Live font updates stay stale**
When the host sends different font CSS in a later `host-context-changed` event, `applyHostFonts` retains the existing host-font style element instead of replacing its contents. The original `@font-face` definitions remain active, so the view does not stay synchronized with the host's fonts.
**Knowledge Base Used:** [Core Web Bridges](https://app.greptile.com/skybridge/-/custom-context/knowledge-base/alpic-ai/skybridge/-/docs/core-web-bridges.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Greptile SummaryAdds an opt-in path for applying MCP host styling to generated views.
Confidence Score: 3/5The PR should not merge until host-style opt-in survives early bridge initialization and live font changes actually replace prior host fonts. Module-scope store creation can permanently initialize the bridge with styling disabled, while subsequent host font updates leave the first injected font rules in place. Files Needing Attention: packages/core/src/web/mount-view.ts, packages/core/src/web/bridges/mcp-app/bridge.ts Prompt To Fix All With AI### Issue 1
packages/core/src/web/mount-view.ts:62-65
**Host-style opt-in is ignored**
When a view initializes a Skybridge store at module scope, `createStore()` constructs the bridge with host styling disabled before this dynamic import runs. The later `getInstance({ applyHostStyles: true })` call cannot update the existing singleton, so the host theme, CSS variables, and fonts are never applied despite the explicit opt-in.
### Issue 2
packages/core/src/web/bridges/mcp-app/bridge.ts:279-280
**Live font updates stay stale**
When the host sends different font CSS in a later `host-context-changed` event, `applyHostFonts` retains the existing host-font style element instead of replacing its contents. The original `@font-face` definitions remain active, so the view does not stay synchronized with the host's fonts.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(web): apply host styles via skybrid..." | Re-trigger Greptile |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
Maybe templates should have this option enabled by default? And we would enable it by default definitively in v2? |
|
Well I don't know how it's going right now - maybe in the future host will require to have a minimum of styling that matches there website but for now, I don't see that much. But I thought it was a good idea to make some marketing (and we could actually add in the devtools the current "styles" that are applied on each host that we know, so that users can check how their app would render if they use the variables). |
What
On the MCP Apps runtime, hosts (Claude, etc.) can hand a view a set of design tokens — theme, CSS variables, and fonts — via
hostContext.styles. Skybridge received them but dropped them on the floor. This wires them onto the view, behind an opt-in flag.Enable it on the Vite plugin:
With it on, Skybridge applies (and keeps in sync on theme toggle):
:root— the standardized set (--color-*,--font-*,--border-radius-*,--shadow-*), referenced withvar(…)color-schemeon the document, so the host'slight-dark()values resolve and native controls matchvar(--font-sans)renders in the host typefaceReuses the ext-apps helpers (
applyDocumentTheme/applyHostStyleVariables/applyHostFonts) — no reimplementation.Why opt-in / default off
Turning it on lets the host set variables on
:rootand flipcolor-scheme, which restyles the view. Since the variables are applied inline on the document root, an app's own:root { --color-… }gets overridden. Default off means no existing app is silently restyled; enabling it is a deliberate one-liner.No-op under the ChatGPT Apps SDK runtime (it sends no tokens).
How it's wired
skybridge({ hostStyles })→ generatedmountView(component, { hostStyles })→McpAppBridge.getInstance({ applyHostStyles })→ gated apply inside the bridge'supdateContext(covers the initial connect snapshot and livehost-context-changedupdates).Changes
SkybridgePluginOptions.hostStyles, forwarded into the generated entryMountViewOptions.hostStyles, seeds the bridge before renderapplyStylesEnabledgate; idempotent applybuild/view,useLayouttheme tipui-guidelinesreferenceTracking
Addresses the
styles(80 CSS variables) context-property gap tracked in #14.🤖 Generated with Claude Code