From c3d91a13ad44339ee85fb950395b6ba447b04ec8 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Thu, 29 Jan 2026 17:08:27 -0600 Subject: [PATCH] Improve example CSS files to better demonstrate host style integration Update `mcp-app.css`, `mcp-app.module.css`, and Svelte/Vue component styles across all `basic-server-*` examples (and `integration-server`) to use CSS variables instead of hardcoded values: - Remove component-local color variables (`--color-primary`, etc.) - Add derived spacing variables to `global.css` based on `--spacing-unit` - Replace hardcoded spacing with `--spacing-xs/sm/md/lg` variables - Add app-defined accent color variables (`--color-accent`, `--color-text-on-accent`) to `global.css` for brand customization - Use app accent colors for button styling - Update button hover to use `color-mix()` for theme-adaptive lightening - Change focus-visible to use `--color-ring-primary` outline This makes the templates more instructive for users and AI coding agents building new MCP Apps by showing how to consume host-provided variables semantically, define app-specific accent colors, and create adaptive styling that integrates with host themes. Co-Authored-By: Claude Opus 4.5 --- examples/basic-server-preact/src/global.css | 81 ++++++++++++++---- .../src/mcp-app.module.css | 35 ++++---- examples/basic-server-react/src/global.css | 81 ++++++++++++++---- .../basic-server-react/src/mcp-app.module.css | 35 ++++---- examples/basic-server-solid/src/global.css | 81 ++++++++++++++---- .../basic-server-solid/src/mcp-app.module.css | 35 ++++---- examples/basic-server-svelte/src/App.svelte | 35 ++++---- examples/basic-server-svelte/src/global.css | 81 ++++++++++++++---- .../basic-server-vanillajs/src/global.css | 81 ++++++++++++++---- .../basic-server-vanillajs/src/mcp-app.css | 35 ++++---- examples/basic-server-vue/src/App.vue | 35 ++++---- examples/basic-server-vue/src/global.css | 81 ++++++++++++++---- examples/integration-server/src/global.css | 81 ++++++++++++++---- .../integration-server/src/mcp-app.module.css | 35 ++++---- .../basic-preact.png | Bin 37738 -> 38378 bytes .../servers.spec.ts-snapshots/basic-react.png | Bin 37739 -> 38493 bytes .../servers.spec.ts-snapshots/basic-solid.png | Bin 38028 -> 38668 bytes .../basic-svelte.png | Bin 37722 -> 38363 bytes .../basic-vanillajs.png | Bin 38318 -> 38953 bytes .../servers.spec.ts-snapshots/basic-vue.png | Bin 37694 -> 38331 bytes .../servers.spec.ts-snapshots/integration.png | Bin 35976 -> 36777 bytes 21 files changed, 560 insertions(+), 252 deletions(-) diff --git a/examples/basic-server-preact/src/global.css b/examples/basic-server-preact/src/global.css index 3247d527..801291f4 100644 --- a/examples/basic-server-preact/src/global.css +++ b/examples/basic-server-preact/src/global.css @@ -1,5 +1,48 @@ :root { color-scheme: light dark; + + /* + * Fallbacks for host style variables used by this app. + * The host may provide these (and many more) via the host context. + */ + --color-text-primary: light-dark(#1f2937, #f3f4f6); + --color-text-inverse: light-dark(#f3f4f6, #1f2937); + --color-text-info: light-dark(#1d4ed8, #60a5fa); + --color-background-primary: light-dark(#ffffff, #1a1a1a); + --color-background-inverse: light-dark(#1a1a1a, #ffffff); + --color-background-info: light-dark(#eff6ff, #1e3a5f); + --color-ring-primary: light-dark(#3b82f6, #60a5fa); + --border-radius-md: 6px; + --border-width-regular: 1px; + --font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; + --font-weight-normal: 400; + --font-weight-bold: 700; + --font-text-md-size: 1rem; + --font-text-md-line-height: 1.5; + --font-heading-3xl-size: 2.25rem; + --font-heading-3xl-line-height: 1.1; + --font-heading-2xl-size: 1.875rem; + --font-heading-2xl-line-height: 1.2; + --font-heading-xl-size: 1.5rem; + --font-heading-xl-line-height: 1.25; + --font-heading-lg-size: 1.25rem; + --font-heading-lg-line-height: 1.3; + --font-heading-md-size: 1rem; + --font-heading-md-line-height: 1.4; + --font-heading-sm-size: 0.875rem; + --font-heading-sm-line-height: 1.4; + + /* Spacing derived from host typography */ + --spacing-unit: var(--font-text-md-size); + --spacing-xs: calc(var(--spacing-unit) * 0.25); + --spacing-sm: calc(var(--spacing-unit) * 0.5); + --spacing-md: var(--spacing-unit); + --spacing-lg: calc(var(--spacing-unit) * 1.5); + + /* App accent color (customize for your brand) */ + --color-accent: #2563eb; + --color-text-on-accent: #ffffff; } * { @@ -7,43 +50,43 @@ } html, body { - font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'); - font-size: var(--font-text-md-size, 1rem); - font-weight: var(--font-weight-normal, 400); - line-height: var(--font-text-md-line-height, 1.5); - color: var(--color-text-primary, light-dark(#1f2937, #f3f4f6)); + font-family: var(--font-sans); + font-size: var(--font-text-md-size); + font-weight: var(--font-weight-normal); + line-height: var(--font-text-md-line-height); + color: var(--color-text-primary); } h1 { - font-size: var(--font-heading-3xl-size, 2.25rem); - line-height: var(--font-heading-3xl-line-height, 1.1); + font-size: var(--font-heading-3xl-size); + line-height: var(--font-heading-3xl-line-height); } h2 { - font-size: var(--font-heading-2xl-size, 1.875rem); - line-height: var(--font-heading-2xl-line-height, 1.2); + font-size: var(--font-heading-2xl-size); + line-height: var(--font-heading-2xl-line-height); } h3 { - font-size: var(--font-heading-xl-size, 1.5rem); - line-height: var(--font-heading-xl-line-height, 1.25); + font-size: var(--font-heading-xl-size); + line-height: var(--font-heading-xl-line-height); } h4 { - font-size: var(--font-heading-lg-size, 1.25rem); - line-height: var(--font-heading-lg-line-height, 1.3); + font-size: var(--font-heading-lg-size); + line-height: var(--font-heading-lg-line-height); } h5 { - font-size: var(--font-heading-md-size, 1rem); - line-height: var(--font-heading-md-line-height, 1.4); + font-size: var(--font-heading-md-size); + line-height: var(--font-heading-md-line-height); } h6 { - font-size: var(--font-heading-sm-size, 0.875rem); - line-height: var(--font-heading-sm-line-height, 1.4); + font-size: var(--font-heading-sm-size); + line-height: var(--font-heading-sm-line-height); } code, pre, kbd { - font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace); + font-family: var(--font-mono); font-size: 1em; } b, strong { - font-weight: var(--font-weight-bold, 700); + font-weight: var(--font-weight-bold); } diff --git a/examples/basic-server-preact/src/mcp-app.module.css b/examples/basic-server-preact/src/mcp-app.module.css index 6bbbff61..ae08a8cc 100644 --- a/examples/basic-server-preact/src/mcp-app.module.css +++ b/examples/basic-server-preact/src/mcp-app.module.css @@ -1,8 +1,4 @@ .main { - --color-primary: #2563eb; - --color-primary-hover: #1d4ed8; - --color-notice-bg: #eff6ff; - width: 100%; max-width: 425px; box-sizing: border-box; @@ -13,7 +9,7 @@ } > * + * { - margin-top: 1.5rem; + margin-top: var(--spacing-lg); } } @@ -25,45 +21,50 @@ } > * + * { - margin-top: 0.5rem; + margin-top: var(--spacing-sm); } /* Server time row: flex layout for consistent mask width in E2E tests */ > p { display: flex; align-items: baseline; - gap: 0.25em; + gap: var(--spacing-xs); } /* Consistent font for form inputs (inherits from global.css) */ textarea, input { + display: block; font-family: inherit; font-size: inherit; } button { - padding: 0.5rem 1rem; + padding: var(--spacing-sm) var(--spacing-md); border: none; - border-radius: 6px; - color: white; - font-weight: bold; - background-color: var(--color-primary); + border-radius: var(--border-radius-md); + color: var(--color-text-on-accent); + font-weight: var(--font-weight-bold); + background-color: var(--color-accent); cursor: pointer; - &:hover, + &:hover { + background-color: color-mix(in srgb, var(--color-accent) 85%, var(--color-background-inverse)); + } + &:focus-visible { - background-color: var(--color-primary-hover); + outline: calc(var(--border-width-regular) * 2) solid var(--color-ring-primary); + outline-offset: var(--border-width-regular); } } } .notice { - padding: 0.5rem 0.75rem; - color: var(--color-primary); + padding: var(--spacing-sm) var(--spacing-md); + color: var(--color-text-info); text-align: center; font-style: italic; - background-color: var(--color-notice-bg); + background-color: var(--color-background-info); &::before { content: "ℹ️ "; diff --git a/examples/basic-server-react/src/global.css b/examples/basic-server-react/src/global.css index 3247d527..801291f4 100644 --- a/examples/basic-server-react/src/global.css +++ b/examples/basic-server-react/src/global.css @@ -1,5 +1,48 @@ :root { color-scheme: light dark; + + /* + * Fallbacks for host style variables used by this app. + * The host may provide these (and many more) via the host context. + */ + --color-text-primary: light-dark(#1f2937, #f3f4f6); + --color-text-inverse: light-dark(#f3f4f6, #1f2937); + --color-text-info: light-dark(#1d4ed8, #60a5fa); + --color-background-primary: light-dark(#ffffff, #1a1a1a); + --color-background-inverse: light-dark(#1a1a1a, #ffffff); + --color-background-info: light-dark(#eff6ff, #1e3a5f); + --color-ring-primary: light-dark(#3b82f6, #60a5fa); + --border-radius-md: 6px; + --border-width-regular: 1px; + --font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; + --font-weight-normal: 400; + --font-weight-bold: 700; + --font-text-md-size: 1rem; + --font-text-md-line-height: 1.5; + --font-heading-3xl-size: 2.25rem; + --font-heading-3xl-line-height: 1.1; + --font-heading-2xl-size: 1.875rem; + --font-heading-2xl-line-height: 1.2; + --font-heading-xl-size: 1.5rem; + --font-heading-xl-line-height: 1.25; + --font-heading-lg-size: 1.25rem; + --font-heading-lg-line-height: 1.3; + --font-heading-md-size: 1rem; + --font-heading-md-line-height: 1.4; + --font-heading-sm-size: 0.875rem; + --font-heading-sm-line-height: 1.4; + + /* Spacing derived from host typography */ + --spacing-unit: var(--font-text-md-size); + --spacing-xs: calc(var(--spacing-unit) * 0.25); + --spacing-sm: calc(var(--spacing-unit) * 0.5); + --spacing-md: var(--spacing-unit); + --spacing-lg: calc(var(--spacing-unit) * 1.5); + + /* App accent color (customize for your brand) */ + --color-accent: #2563eb; + --color-text-on-accent: #ffffff; } * { @@ -7,43 +50,43 @@ } html, body { - font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'); - font-size: var(--font-text-md-size, 1rem); - font-weight: var(--font-weight-normal, 400); - line-height: var(--font-text-md-line-height, 1.5); - color: var(--color-text-primary, light-dark(#1f2937, #f3f4f6)); + font-family: var(--font-sans); + font-size: var(--font-text-md-size); + font-weight: var(--font-weight-normal); + line-height: var(--font-text-md-line-height); + color: var(--color-text-primary); } h1 { - font-size: var(--font-heading-3xl-size, 2.25rem); - line-height: var(--font-heading-3xl-line-height, 1.1); + font-size: var(--font-heading-3xl-size); + line-height: var(--font-heading-3xl-line-height); } h2 { - font-size: var(--font-heading-2xl-size, 1.875rem); - line-height: var(--font-heading-2xl-line-height, 1.2); + font-size: var(--font-heading-2xl-size); + line-height: var(--font-heading-2xl-line-height); } h3 { - font-size: var(--font-heading-xl-size, 1.5rem); - line-height: var(--font-heading-xl-line-height, 1.25); + font-size: var(--font-heading-xl-size); + line-height: var(--font-heading-xl-line-height); } h4 { - font-size: var(--font-heading-lg-size, 1.25rem); - line-height: var(--font-heading-lg-line-height, 1.3); + font-size: var(--font-heading-lg-size); + line-height: var(--font-heading-lg-line-height); } h5 { - font-size: var(--font-heading-md-size, 1rem); - line-height: var(--font-heading-md-line-height, 1.4); + font-size: var(--font-heading-md-size); + line-height: var(--font-heading-md-line-height); } h6 { - font-size: var(--font-heading-sm-size, 0.875rem); - line-height: var(--font-heading-sm-line-height, 1.4); + font-size: var(--font-heading-sm-size); + line-height: var(--font-heading-sm-line-height); } code, pre, kbd { - font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace); + font-family: var(--font-mono); font-size: 1em; } b, strong { - font-weight: var(--font-weight-bold, 700); + font-weight: var(--font-weight-bold); } diff --git a/examples/basic-server-react/src/mcp-app.module.css b/examples/basic-server-react/src/mcp-app.module.css index 6bbbff61..ae08a8cc 100644 --- a/examples/basic-server-react/src/mcp-app.module.css +++ b/examples/basic-server-react/src/mcp-app.module.css @@ -1,8 +1,4 @@ .main { - --color-primary: #2563eb; - --color-primary-hover: #1d4ed8; - --color-notice-bg: #eff6ff; - width: 100%; max-width: 425px; box-sizing: border-box; @@ -13,7 +9,7 @@ } > * + * { - margin-top: 1.5rem; + margin-top: var(--spacing-lg); } } @@ -25,45 +21,50 @@ } > * + * { - margin-top: 0.5rem; + margin-top: var(--spacing-sm); } /* Server time row: flex layout for consistent mask width in E2E tests */ > p { display: flex; align-items: baseline; - gap: 0.25em; + gap: var(--spacing-xs); } /* Consistent font for form inputs (inherits from global.css) */ textarea, input { + display: block; font-family: inherit; font-size: inherit; } button { - padding: 0.5rem 1rem; + padding: var(--spacing-sm) var(--spacing-md); border: none; - border-radius: 6px; - color: white; - font-weight: bold; - background-color: var(--color-primary); + border-radius: var(--border-radius-md); + color: var(--color-text-on-accent); + font-weight: var(--font-weight-bold); + background-color: var(--color-accent); cursor: pointer; - &:hover, + &:hover { + background-color: color-mix(in srgb, var(--color-accent) 85%, var(--color-background-inverse)); + } + &:focus-visible { - background-color: var(--color-primary-hover); + outline: calc(var(--border-width-regular) * 2) solid var(--color-ring-primary); + outline-offset: var(--border-width-regular); } } } .notice { - padding: 0.5rem 0.75rem; - color: var(--color-primary); + padding: var(--spacing-sm) var(--spacing-md); + color: var(--color-text-info); text-align: center; font-style: italic; - background-color: var(--color-notice-bg); + background-color: var(--color-background-info); &::before { content: "ℹ️ "; diff --git a/examples/basic-server-solid/src/global.css b/examples/basic-server-solid/src/global.css index 3247d527..801291f4 100644 --- a/examples/basic-server-solid/src/global.css +++ b/examples/basic-server-solid/src/global.css @@ -1,5 +1,48 @@ :root { color-scheme: light dark; + + /* + * Fallbacks for host style variables used by this app. + * The host may provide these (and many more) via the host context. + */ + --color-text-primary: light-dark(#1f2937, #f3f4f6); + --color-text-inverse: light-dark(#f3f4f6, #1f2937); + --color-text-info: light-dark(#1d4ed8, #60a5fa); + --color-background-primary: light-dark(#ffffff, #1a1a1a); + --color-background-inverse: light-dark(#1a1a1a, #ffffff); + --color-background-info: light-dark(#eff6ff, #1e3a5f); + --color-ring-primary: light-dark(#3b82f6, #60a5fa); + --border-radius-md: 6px; + --border-width-regular: 1px; + --font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; + --font-weight-normal: 400; + --font-weight-bold: 700; + --font-text-md-size: 1rem; + --font-text-md-line-height: 1.5; + --font-heading-3xl-size: 2.25rem; + --font-heading-3xl-line-height: 1.1; + --font-heading-2xl-size: 1.875rem; + --font-heading-2xl-line-height: 1.2; + --font-heading-xl-size: 1.5rem; + --font-heading-xl-line-height: 1.25; + --font-heading-lg-size: 1.25rem; + --font-heading-lg-line-height: 1.3; + --font-heading-md-size: 1rem; + --font-heading-md-line-height: 1.4; + --font-heading-sm-size: 0.875rem; + --font-heading-sm-line-height: 1.4; + + /* Spacing derived from host typography */ + --spacing-unit: var(--font-text-md-size); + --spacing-xs: calc(var(--spacing-unit) * 0.25); + --spacing-sm: calc(var(--spacing-unit) * 0.5); + --spacing-md: var(--spacing-unit); + --spacing-lg: calc(var(--spacing-unit) * 1.5); + + /* App accent color (customize for your brand) */ + --color-accent: #2563eb; + --color-text-on-accent: #ffffff; } * { @@ -7,43 +50,43 @@ } html, body { - font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'); - font-size: var(--font-text-md-size, 1rem); - font-weight: var(--font-weight-normal, 400); - line-height: var(--font-text-md-line-height, 1.5); - color: var(--color-text-primary, light-dark(#1f2937, #f3f4f6)); + font-family: var(--font-sans); + font-size: var(--font-text-md-size); + font-weight: var(--font-weight-normal); + line-height: var(--font-text-md-line-height); + color: var(--color-text-primary); } h1 { - font-size: var(--font-heading-3xl-size, 2.25rem); - line-height: var(--font-heading-3xl-line-height, 1.1); + font-size: var(--font-heading-3xl-size); + line-height: var(--font-heading-3xl-line-height); } h2 { - font-size: var(--font-heading-2xl-size, 1.875rem); - line-height: var(--font-heading-2xl-line-height, 1.2); + font-size: var(--font-heading-2xl-size); + line-height: var(--font-heading-2xl-line-height); } h3 { - font-size: var(--font-heading-xl-size, 1.5rem); - line-height: var(--font-heading-xl-line-height, 1.25); + font-size: var(--font-heading-xl-size); + line-height: var(--font-heading-xl-line-height); } h4 { - font-size: var(--font-heading-lg-size, 1.25rem); - line-height: var(--font-heading-lg-line-height, 1.3); + font-size: var(--font-heading-lg-size); + line-height: var(--font-heading-lg-line-height); } h5 { - font-size: var(--font-heading-md-size, 1rem); - line-height: var(--font-heading-md-line-height, 1.4); + font-size: var(--font-heading-md-size); + line-height: var(--font-heading-md-line-height); } h6 { - font-size: var(--font-heading-sm-size, 0.875rem); - line-height: var(--font-heading-sm-line-height, 1.4); + font-size: var(--font-heading-sm-size); + line-height: var(--font-heading-sm-line-height); } code, pre, kbd { - font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace); + font-family: var(--font-mono); font-size: 1em; } b, strong { - font-weight: var(--font-weight-bold, 700); + font-weight: var(--font-weight-bold); } diff --git a/examples/basic-server-solid/src/mcp-app.module.css b/examples/basic-server-solid/src/mcp-app.module.css index 6bbbff61..ae08a8cc 100644 --- a/examples/basic-server-solid/src/mcp-app.module.css +++ b/examples/basic-server-solid/src/mcp-app.module.css @@ -1,8 +1,4 @@ .main { - --color-primary: #2563eb; - --color-primary-hover: #1d4ed8; - --color-notice-bg: #eff6ff; - width: 100%; max-width: 425px; box-sizing: border-box; @@ -13,7 +9,7 @@ } > * + * { - margin-top: 1.5rem; + margin-top: var(--spacing-lg); } } @@ -25,45 +21,50 @@ } > * + * { - margin-top: 0.5rem; + margin-top: var(--spacing-sm); } /* Server time row: flex layout for consistent mask width in E2E tests */ > p { display: flex; align-items: baseline; - gap: 0.25em; + gap: var(--spacing-xs); } /* Consistent font for form inputs (inherits from global.css) */ textarea, input { + display: block; font-family: inherit; font-size: inherit; } button { - padding: 0.5rem 1rem; + padding: var(--spacing-sm) var(--spacing-md); border: none; - border-radius: 6px; - color: white; - font-weight: bold; - background-color: var(--color-primary); + border-radius: var(--border-radius-md); + color: var(--color-text-on-accent); + font-weight: var(--font-weight-bold); + background-color: var(--color-accent); cursor: pointer; - &:hover, + &:hover { + background-color: color-mix(in srgb, var(--color-accent) 85%, var(--color-background-inverse)); + } + &:focus-visible { - background-color: var(--color-primary-hover); + outline: calc(var(--border-width-regular) * 2) solid var(--color-ring-primary); + outline-offset: var(--border-width-regular); } } } .notice { - padding: 0.5rem 0.75rem; - color: var(--color-primary); + padding: var(--spacing-sm) var(--spacing-md); + color: var(--color-text-info); text-align: center; font-style: italic; - background-color: var(--color-notice-bg); + background-color: var(--color-background-info); &::before { content: "ℹ️ "; diff --git a/examples/basic-server-svelte/src/App.svelte b/examples/basic-server-svelte/src/App.svelte index 9ef50f00..fde128dd 100644 --- a/examples/basic-server-svelte/src/App.svelte +++ b/examples/basic-server-svelte/src/App.svelte @@ -133,10 +133,6 @@ async function handleOpenLink() {