From 12a709922a2de1eb311b6de90d2b82b1ece8ff2d Mon Sep 17 00:00:00 2001 From: Jon Gallant Date: Sun, 5 Jul 2026 22:34:18 -0700 Subject: [PATCH] feat(canvas-kit): make the canvas follow the user's light/dark theme The host injects its theme-aware Primer tokens only after first paint, and the kit fallbacks were dark-only, so a light-theme user saw a dark flash. Worse, --ck-bg-inset aliased --background-color-emphasis (a dark chip color in light mode), which painted chips, filter pills, tab counts and the slider track as dark boxes on a light canvas. canvas-kit/theme.css (all 6 vendored copies, synced from the create-canvas-app kit; bumped to 2026-07-05.1): - Derive --ck-bg-inset from --ck-bg-muted + --ck-fg instead of the wrong emphasis token (dark stays ~#363c43, light becomes ~#d6d8db). - Add a prefers-color-scheme:light fallback plus color-scheme, both gated on :root:not([data-color-mode]) so the host stays fully in control the moment it injects its tokens. - Theme-neutral skeleton shimmer. code-tutor: map --lvl-*/--q-* and category colors to semantic and data Primer tokens so the hues track the theme; soften two dark-only shadows. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../code-tutor/canvas-kit/.kit-version.json | 4 +- extensions/code-tutor/canvas-kit/theme.css | 59 ++++++++++++++++--- extensions/code-tutor/canvas-kit/version.mjs | 2 +- extensions/code-tutor/web/app.mjs | 16 ++--- extensions/code-tutor/web/styles.css | 22 +++---- .../canvas-kit/.kit-version.json | 4 +- .../language-tutor/canvas-kit/theme.css | 59 ++++++++++++++++--- .../language-tutor/canvas-kit/version.mjs | 2 +- .../canvas-kit/.kit-version.json | 4 +- .../news-aggregator/canvas-kit/theme.css | 59 ++++++++++++++++--- .../news-aggregator/canvas-kit/version.mjs | 2 +- .../canvas-kit/.kit-version.json | 4 +- extensions/random-animal/canvas-kit/theme.css | 59 ++++++++++++++++--- .../random-animal/canvas-kit/version.mjs | 2 +- .../stock-ticker/canvas-kit/.kit-version.json | 4 +- extensions/stock-ticker/canvas-kit/theme.css | 59 ++++++++++++++++--- .../stock-ticker/canvas-kit/version.mjs | 2 +- .../canvas-kit/.kit-version.json | 4 +- extensions/wiki-discover/canvas-kit/theme.css | 59 ++++++++++++++++--- .../wiki-discover/canvas-kit/version.mjs | 2 +- 20 files changed, 344 insertions(+), 84 deletions(-) diff --git a/extensions/code-tutor/canvas-kit/.kit-version.json b/extensions/code-tutor/canvas-kit/.kit-version.json index 8ded639..769d8b2 100644 --- a/extensions/code-tutor/canvas-kit/.kit-version.json +++ b/extensions/code-tutor/canvas-kit/.kit-version.json @@ -1,5 +1,5 @@ { - "version": "2026-07-04.1", - "syncedAt": "2026-07-05T16:16:06.308Z", + "version": "2026-07-05.1", + "syncedAt": "2026-07-06T05:14:31.801Z", "source": "create-canvas-app/kit" } diff --git a/extensions/code-tutor/canvas-kit/theme.css b/extensions/code-tutor/canvas-kit/theme.css index 1627e49..532994f 100644 --- a/extensions/code-tutor/canvas-kit/theme.css +++ b/extensions/code-tutor/canvas-kit/theme.css @@ -2,15 +2,31 @@ * * Baseline canvas styling built on the Primer design tokens the runtime injects * onto the canvas document (see github-app src/lib/extensionCanvasThemeContract.json). - * Every token has a hardcoded dark fallback so the canvas still looks right if a - * token is ever missing. Author canvas-specific styles on top of these. + * + * Theme awareness. The tokens the host injects are computed from the *current* + * user theme, so a light-theme user gets light values and a dark-theme user gets + * dark ones. Author against the --ck-* aliases below and your canvas follows the + * user's theme for free. Two rules keep that honest: + * 1. Never hardcode a dark-only color. Use a --ck-* alias, an injected token, or + * color-mix() of one so the value tracks the theme. (--ck-bg-inset derives + * itself from --ck-bg-muted + --ck-fg for exactly this reason.) + * 2. The dark fallbacks below only apply when a token is missing. Because the + * host injects its theme *after* first paint, a light user would otherwise + * see a dark flash, so the block after :root follows the OS color scheme + * until the host takes over (it sets data-color-mode when it does). */ :root { /* semantic aliases -> injected tokens (fallbacks = GitHub dark) */ --ck-bg: var(--background-color-default, #0d1117); --ck-bg-muted: var(--background-color-muted, #161b22); - --ck-bg-inset: var(--background-color-emphasis, #010409); + /* Recessed control surface (tabs track, wells, chips). Derived from the two + theme-aware tokens above rather than --background-color-emphasis, which is a + high-contrast chip color that is *dark* in light mode (#25292e) and would + paint every inset surface as a dark box on a light canvas. This mix stays a + touch more prominent than a card in either theme (dark ~#363c43, light + ~#d6d8db). */ + --ck-bg-inset: color-mix(in srgb, var(--ck-bg-muted) 85%, var(--ck-fg) 15%); --ck-fg: var(--text-color-default, #f0f6fc); --ck-muted: var(--text-color-muted, #8b949e); --ck-link: var(--text-color-link, #58a6ff); @@ -33,6 +49,33 @@ --ck-gap: 12px; } +/* Theme-aware fallback (see header note 2). The host injects its Primer tokens + only after first paint. Until then (and in any non-app context that never + injects) follow the webview's OS color scheme instead of forcing dark. Both + rules are gated on :not([data-color-mode]): the host sets that attribute + alongside its tokens and explicit color-scheme, so the instant it themes the + canvas these back off and the host is fully in control. --ck-bg-inset is + omitted on purpose; it derives from --ck-bg-muted + --ck-fg, which are + redefined here, so it tracks the light values automatically. */ +:root:not([data-color-mode]) { color-scheme: light dark; } + +@media (prefers-color-scheme: light) { + :root:not([data-color-mode]) { + --ck-bg: var(--background-color-default, #ffffff); + --ck-bg-muted: var(--background-color-muted, #f6f8fa); + --ck-fg: var(--text-color-default, #1f2328); + --ck-muted: var(--text-color-muted, #59636e); + --ck-link: var(--text-color-link, #0969da); + --ck-accent: var(--text-color-accent, #0969da); + --ck-danger: var(--text-color-danger, #d1242f); + --ck-success: var(--text-color-success, #1a7f37); + --ck-attention: var(--text-color-attention, #9a6700); + --ck-border: var(--border-color-default, #d1d9e0); + --ck-border-muted: var(--border-color-muted, #d1d9e0); + --ck-focus: var(--color-focus-outline, var(--outline-color-focus-default, #0969da)); + } +} + * { box-sizing: border-box; } html, body { @@ -143,8 +186,8 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold border: 1px solid var(--ck-border); color: var(--ck-muted); } -/* Generic semantic badge variants — map your own statuses to these in the view - (e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add +/* Generic semantic badge variants (map your own statuses to these in the view; + e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add app-specific status names here; this is the shared kit theme. */ .ck-badge-success { color: var(--ck-success); border-color: var(--border-color-success-emphasis, #238636); } .ck-badge-accent { color: var(--text-color-accent, #a371f7); border-color: var(--border-color-accent-emphasis, #8957e5); } @@ -170,7 +213,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold .ck-empty { color: var(--ck-muted); text-align: center; padding: 28px 0; } /* ---- loading: spinner + skeleton ----------------------------------------- */ -/* Spinner: pair with a Lucide "loader-circle" icon — +/* Spinner: pair with a Lucide "loader-circle" icon: html`<${Icon} name="loader-circle" class="ck-spinner" />`. */ .ck-spinner { animation: ck-spin 0.9s linear infinite; transform-origin: center; } @keyframes ck-spin { to { transform: rotate(360deg); } } @@ -188,7 +231,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold position: absolute; inset: 0; transform: translateX(-100%); - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent); + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ck-fg) 8%, transparent), transparent); animation: ck-shimmer 1.2s ease-in-out infinite; } @keyframes ck-shimmer { to { transform: translateX(100%); } } @@ -217,7 +260,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold /* ---- motion safety ------------------------------------------------------- */ /* Global guard: respect the OS "reduce motion" setting for every animation a canvas adds (kit spinners/skeletons included). Author-added keyframes inherit - this automatically — you do not need to repeat it per canvas. */ + this automatically, so you do not need to repeat it per canvas. */ @media (prefers-reduced-motion: reduce) { *, *::before, diff --git a/extensions/code-tutor/canvas-kit/version.mjs b/extensions/code-tutor/canvas-kit/version.mjs index 1db3bee..93471a9 100644 --- a/extensions/code-tutor/canvas-kit/version.mjs +++ b/extensions/code-tutor/canvas-kit/version.mjs @@ -10,4 +10,4 @@ // convention (the skill ships as files, not an npm version); a commit short-sha // works too. Re-exported from client.mjs so a canvas can read it at runtime. -export const KIT_VERSION = "2026-07-04.1"; +export const KIT_VERSION = "2026-07-05.1"; diff --git a/extensions/code-tutor/web/app.mjs b/extensions/code-tutor/web/app.mjs index f99b4e2..4ffa207 100644 --- a/extensions/code-tutor/web/app.mjs +++ b/extensions/code-tutor/web/app.mjs @@ -20,14 +20,14 @@ const LEVEL_COLOR = { }; const CATEGORY_META = { - algorithm: { icon: "workflow", label: "Algorithm", color: "#58a6ff" }, - "data-structure": { icon: "boxes", label: "Data structure", color: "#bc8cff" }, - complexity: { icon: "gauge", label: "Complexity", color: "#e3b341" }, - theory: { icon: "brain", label: "Theory", color: "#56d4dd" }, - pattern: { icon: "layers", label: "Pattern", color: "#3fb950" }, - paradigm: { icon: "git-fork", label: "Paradigm", color: "#f778ba" }, - concurrency: { icon: "waypoints", label: "Concurrency", color: "#f0883e" }, - system: { icon: "network", label: "System", color: "#a371f7" }, + algorithm: { icon: "workflow", label: "Algorithm", color: "var(--color-data-blue-emphasis, #58a6ff)" }, + "data-structure": { icon: "boxes", label: "Data structure", color: "var(--color-data-purple-emphasis, #bc8cff)" }, + complexity: { icon: "gauge", label: "Complexity", color: "var(--color-data-yellow-emphasis, #e3b341)" }, + theory: { icon: "brain", label: "Theory", color: "var(--color-data-teal-emphasis, #56d4dd)" }, + pattern: { icon: "layers", label: "Pattern", color: "var(--color-data-green-emphasis, #3fb950)" }, + paradigm: { icon: "git-fork", label: "Paradigm", color: "var(--color-data-pink-emphasis, #f778ba)" }, + concurrency: { icon: "waypoints", label: "Concurrency", color: "var(--color-data-orange-emphasis, #f0883e)" }, + system: { icon: "network", label: "System", color: "var(--color-data-plum-emphasis, #a371f7)" }, }; const catMeta = (c) => CATEGORY_META[c] ?? { icon: "book-open", label: c || "Topic", color: "var(--ck-accent)" }; diff --git a/extensions/code-tutor/web/styles.css b/extensions/code-tutor/web/styles.css index ee216b6..6026180 100644 --- a/extensions/code-tutor/web/styles.css +++ b/extensions/code-tutor/web/styles.css @@ -17,16 +17,18 @@ --r-md: 9px; --r-sm: 6px; - /* level palette */ - --lvl-eli5: #3fb950; - --lvl-curious: #58a6ff; - --lvl-engineer: #d29922; - --lvl-wizard: #bc8cff; + /* Level palette. Semantic kit tokens (not raw hex) so each hue tracks the + user's theme: the injected values darken for contrast on light and brighten + on dark. Wizard uses the injected purple data token. */ + --lvl-eli5: var(--ck-success); + --lvl-curious: var(--ck-accent); + --lvl-engineer: var(--ck-attention); + --lvl-wizard: var(--color-data-purple-emphasis, #8250df); /* quality palette */ - --q-good: #3fb950; - --q-ok: #d29922; - --q-bad: #f85149; + --q-good: var(--ck-success); + --q-ok: var(--ck-attention); + --q-bad: var(--ck-danger); } body { padding: 0; } @@ -104,7 +106,7 @@ button.cs-chip:hover { border-color: var(--ck-accent); color: var(--ck-fg); } .cs-slider input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--ck-bg); border: 3px solid var(--lvl); cursor: pointer; - box-shadow: 0 1px 4px rgba(0,0,0,0.4); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.28); } .cs-slider input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--ck-bg); border: 3px solid var(--lvl); cursor: pointer; @@ -137,7 +139,7 @@ button.cs-chip:hover { border-color: var(--ck-accent); color: var(--ck-fg); } background: color-mix(in srgb, var(--ck-bg-muted) 94%, transparent); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-bottom: 1px solid var(--ck-border); - box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28); + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18); transform: translateY(-110%); opacity: 0; visibility: hidden; transition: transform 0.18s ease, opacity 0.18s ease, visibility 0.18s; --lvl: var(--ck-accent); diff --git a/extensions/language-tutor/canvas-kit/.kit-version.json b/extensions/language-tutor/canvas-kit/.kit-version.json index d86ff17..bf4f36d 100644 --- a/extensions/language-tutor/canvas-kit/.kit-version.json +++ b/extensions/language-tutor/canvas-kit/.kit-version.json @@ -1,5 +1,5 @@ { - "version": "2026-07-04.1", - "syncedAt": "2026-07-05T16:16:13.213Z", + "version": "2026-07-05.1", + "syncedAt": "2026-07-06T05:14:31.945Z", "source": "create-canvas-app/kit" } diff --git a/extensions/language-tutor/canvas-kit/theme.css b/extensions/language-tutor/canvas-kit/theme.css index 1627e49..532994f 100644 --- a/extensions/language-tutor/canvas-kit/theme.css +++ b/extensions/language-tutor/canvas-kit/theme.css @@ -2,15 +2,31 @@ * * Baseline canvas styling built on the Primer design tokens the runtime injects * onto the canvas document (see github-app src/lib/extensionCanvasThemeContract.json). - * Every token has a hardcoded dark fallback so the canvas still looks right if a - * token is ever missing. Author canvas-specific styles on top of these. + * + * Theme awareness. The tokens the host injects are computed from the *current* + * user theme, so a light-theme user gets light values and a dark-theme user gets + * dark ones. Author against the --ck-* aliases below and your canvas follows the + * user's theme for free. Two rules keep that honest: + * 1. Never hardcode a dark-only color. Use a --ck-* alias, an injected token, or + * color-mix() of one so the value tracks the theme. (--ck-bg-inset derives + * itself from --ck-bg-muted + --ck-fg for exactly this reason.) + * 2. The dark fallbacks below only apply when a token is missing. Because the + * host injects its theme *after* first paint, a light user would otherwise + * see a dark flash, so the block after :root follows the OS color scheme + * until the host takes over (it sets data-color-mode when it does). */ :root { /* semantic aliases -> injected tokens (fallbacks = GitHub dark) */ --ck-bg: var(--background-color-default, #0d1117); --ck-bg-muted: var(--background-color-muted, #161b22); - --ck-bg-inset: var(--background-color-emphasis, #010409); + /* Recessed control surface (tabs track, wells, chips). Derived from the two + theme-aware tokens above rather than --background-color-emphasis, which is a + high-contrast chip color that is *dark* in light mode (#25292e) and would + paint every inset surface as a dark box on a light canvas. This mix stays a + touch more prominent than a card in either theme (dark ~#363c43, light + ~#d6d8db). */ + --ck-bg-inset: color-mix(in srgb, var(--ck-bg-muted) 85%, var(--ck-fg) 15%); --ck-fg: var(--text-color-default, #f0f6fc); --ck-muted: var(--text-color-muted, #8b949e); --ck-link: var(--text-color-link, #58a6ff); @@ -33,6 +49,33 @@ --ck-gap: 12px; } +/* Theme-aware fallback (see header note 2). The host injects its Primer tokens + only after first paint. Until then (and in any non-app context that never + injects) follow the webview's OS color scheme instead of forcing dark. Both + rules are gated on :not([data-color-mode]): the host sets that attribute + alongside its tokens and explicit color-scheme, so the instant it themes the + canvas these back off and the host is fully in control. --ck-bg-inset is + omitted on purpose; it derives from --ck-bg-muted + --ck-fg, which are + redefined here, so it tracks the light values automatically. */ +:root:not([data-color-mode]) { color-scheme: light dark; } + +@media (prefers-color-scheme: light) { + :root:not([data-color-mode]) { + --ck-bg: var(--background-color-default, #ffffff); + --ck-bg-muted: var(--background-color-muted, #f6f8fa); + --ck-fg: var(--text-color-default, #1f2328); + --ck-muted: var(--text-color-muted, #59636e); + --ck-link: var(--text-color-link, #0969da); + --ck-accent: var(--text-color-accent, #0969da); + --ck-danger: var(--text-color-danger, #d1242f); + --ck-success: var(--text-color-success, #1a7f37); + --ck-attention: var(--text-color-attention, #9a6700); + --ck-border: var(--border-color-default, #d1d9e0); + --ck-border-muted: var(--border-color-muted, #d1d9e0); + --ck-focus: var(--color-focus-outline, var(--outline-color-focus-default, #0969da)); + } +} + * { box-sizing: border-box; } html, body { @@ -143,8 +186,8 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold border: 1px solid var(--ck-border); color: var(--ck-muted); } -/* Generic semantic badge variants — map your own statuses to these in the view - (e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add +/* Generic semantic badge variants (map your own statuses to these in the view; + e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add app-specific status names here; this is the shared kit theme. */ .ck-badge-success { color: var(--ck-success); border-color: var(--border-color-success-emphasis, #238636); } .ck-badge-accent { color: var(--text-color-accent, #a371f7); border-color: var(--border-color-accent-emphasis, #8957e5); } @@ -170,7 +213,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold .ck-empty { color: var(--ck-muted); text-align: center; padding: 28px 0; } /* ---- loading: spinner + skeleton ----------------------------------------- */ -/* Spinner: pair with a Lucide "loader-circle" icon — +/* Spinner: pair with a Lucide "loader-circle" icon: html`<${Icon} name="loader-circle" class="ck-spinner" />`. */ .ck-spinner { animation: ck-spin 0.9s linear infinite; transform-origin: center; } @keyframes ck-spin { to { transform: rotate(360deg); } } @@ -188,7 +231,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold position: absolute; inset: 0; transform: translateX(-100%); - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent); + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ck-fg) 8%, transparent), transparent); animation: ck-shimmer 1.2s ease-in-out infinite; } @keyframes ck-shimmer { to { transform: translateX(100%); } } @@ -217,7 +260,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold /* ---- motion safety ------------------------------------------------------- */ /* Global guard: respect the OS "reduce motion" setting for every animation a canvas adds (kit spinners/skeletons included). Author-added keyframes inherit - this automatically — you do not need to repeat it per canvas. */ + this automatically, so you do not need to repeat it per canvas. */ @media (prefers-reduced-motion: reduce) { *, *::before, diff --git a/extensions/language-tutor/canvas-kit/version.mjs b/extensions/language-tutor/canvas-kit/version.mjs index 1db3bee..93471a9 100644 --- a/extensions/language-tutor/canvas-kit/version.mjs +++ b/extensions/language-tutor/canvas-kit/version.mjs @@ -10,4 +10,4 @@ // convention (the skill ships as files, not an npm version); a commit short-sha // works too. Re-exported from client.mjs so a canvas can read it at runtime. -export const KIT_VERSION = "2026-07-04.1"; +export const KIT_VERSION = "2026-07-05.1"; diff --git a/extensions/news-aggregator/canvas-kit/.kit-version.json b/extensions/news-aggregator/canvas-kit/.kit-version.json index 56eba9b..90bdd70 100644 --- a/extensions/news-aggregator/canvas-kit/.kit-version.json +++ b/extensions/news-aggregator/canvas-kit/.kit-version.json @@ -1,5 +1,5 @@ { - "version": "2026-07-04.1", - "syncedAt": "2026-07-05T16:16:13.492Z", + "version": "2026-07-05.1", + "syncedAt": "2026-07-06T05:14:32.084Z", "source": "create-canvas-app/kit" } diff --git a/extensions/news-aggregator/canvas-kit/theme.css b/extensions/news-aggregator/canvas-kit/theme.css index 1627e49..532994f 100644 --- a/extensions/news-aggregator/canvas-kit/theme.css +++ b/extensions/news-aggregator/canvas-kit/theme.css @@ -2,15 +2,31 @@ * * Baseline canvas styling built on the Primer design tokens the runtime injects * onto the canvas document (see github-app src/lib/extensionCanvasThemeContract.json). - * Every token has a hardcoded dark fallback so the canvas still looks right if a - * token is ever missing. Author canvas-specific styles on top of these. + * + * Theme awareness. The tokens the host injects are computed from the *current* + * user theme, so a light-theme user gets light values and a dark-theme user gets + * dark ones. Author against the --ck-* aliases below and your canvas follows the + * user's theme for free. Two rules keep that honest: + * 1. Never hardcode a dark-only color. Use a --ck-* alias, an injected token, or + * color-mix() of one so the value tracks the theme. (--ck-bg-inset derives + * itself from --ck-bg-muted + --ck-fg for exactly this reason.) + * 2. The dark fallbacks below only apply when a token is missing. Because the + * host injects its theme *after* first paint, a light user would otherwise + * see a dark flash, so the block after :root follows the OS color scheme + * until the host takes over (it sets data-color-mode when it does). */ :root { /* semantic aliases -> injected tokens (fallbacks = GitHub dark) */ --ck-bg: var(--background-color-default, #0d1117); --ck-bg-muted: var(--background-color-muted, #161b22); - --ck-bg-inset: var(--background-color-emphasis, #010409); + /* Recessed control surface (tabs track, wells, chips). Derived from the two + theme-aware tokens above rather than --background-color-emphasis, which is a + high-contrast chip color that is *dark* in light mode (#25292e) and would + paint every inset surface as a dark box on a light canvas. This mix stays a + touch more prominent than a card in either theme (dark ~#363c43, light + ~#d6d8db). */ + --ck-bg-inset: color-mix(in srgb, var(--ck-bg-muted) 85%, var(--ck-fg) 15%); --ck-fg: var(--text-color-default, #f0f6fc); --ck-muted: var(--text-color-muted, #8b949e); --ck-link: var(--text-color-link, #58a6ff); @@ -33,6 +49,33 @@ --ck-gap: 12px; } +/* Theme-aware fallback (see header note 2). The host injects its Primer tokens + only after first paint. Until then (and in any non-app context that never + injects) follow the webview's OS color scheme instead of forcing dark. Both + rules are gated on :not([data-color-mode]): the host sets that attribute + alongside its tokens and explicit color-scheme, so the instant it themes the + canvas these back off and the host is fully in control. --ck-bg-inset is + omitted on purpose; it derives from --ck-bg-muted + --ck-fg, which are + redefined here, so it tracks the light values automatically. */ +:root:not([data-color-mode]) { color-scheme: light dark; } + +@media (prefers-color-scheme: light) { + :root:not([data-color-mode]) { + --ck-bg: var(--background-color-default, #ffffff); + --ck-bg-muted: var(--background-color-muted, #f6f8fa); + --ck-fg: var(--text-color-default, #1f2328); + --ck-muted: var(--text-color-muted, #59636e); + --ck-link: var(--text-color-link, #0969da); + --ck-accent: var(--text-color-accent, #0969da); + --ck-danger: var(--text-color-danger, #d1242f); + --ck-success: var(--text-color-success, #1a7f37); + --ck-attention: var(--text-color-attention, #9a6700); + --ck-border: var(--border-color-default, #d1d9e0); + --ck-border-muted: var(--border-color-muted, #d1d9e0); + --ck-focus: var(--color-focus-outline, var(--outline-color-focus-default, #0969da)); + } +} + * { box-sizing: border-box; } html, body { @@ -143,8 +186,8 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold border: 1px solid var(--ck-border); color: var(--ck-muted); } -/* Generic semantic badge variants — map your own statuses to these in the view - (e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add +/* Generic semantic badge variants (map your own statuses to these in the view; + e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add app-specific status names here; this is the shared kit theme. */ .ck-badge-success { color: var(--ck-success); border-color: var(--border-color-success-emphasis, #238636); } .ck-badge-accent { color: var(--text-color-accent, #a371f7); border-color: var(--border-color-accent-emphasis, #8957e5); } @@ -170,7 +213,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold .ck-empty { color: var(--ck-muted); text-align: center; padding: 28px 0; } /* ---- loading: spinner + skeleton ----------------------------------------- */ -/* Spinner: pair with a Lucide "loader-circle" icon — +/* Spinner: pair with a Lucide "loader-circle" icon: html`<${Icon} name="loader-circle" class="ck-spinner" />`. */ .ck-spinner { animation: ck-spin 0.9s linear infinite; transform-origin: center; } @keyframes ck-spin { to { transform: rotate(360deg); } } @@ -188,7 +231,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold position: absolute; inset: 0; transform: translateX(-100%); - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent); + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ck-fg) 8%, transparent), transparent); animation: ck-shimmer 1.2s ease-in-out infinite; } @keyframes ck-shimmer { to { transform: translateX(100%); } } @@ -217,7 +260,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold /* ---- motion safety ------------------------------------------------------- */ /* Global guard: respect the OS "reduce motion" setting for every animation a canvas adds (kit spinners/skeletons included). Author-added keyframes inherit - this automatically — you do not need to repeat it per canvas. */ + this automatically, so you do not need to repeat it per canvas. */ @media (prefers-reduced-motion: reduce) { *, *::before, diff --git a/extensions/news-aggregator/canvas-kit/version.mjs b/extensions/news-aggregator/canvas-kit/version.mjs index 1db3bee..93471a9 100644 --- a/extensions/news-aggregator/canvas-kit/version.mjs +++ b/extensions/news-aggregator/canvas-kit/version.mjs @@ -10,4 +10,4 @@ // convention (the skill ships as files, not an npm version); a commit short-sha // works too. Re-exported from client.mjs so a canvas can read it at runtime. -export const KIT_VERSION = "2026-07-04.1"; +export const KIT_VERSION = "2026-07-05.1"; diff --git a/extensions/random-animal/canvas-kit/.kit-version.json b/extensions/random-animal/canvas-kit/.kit-version.json index 50110d5..fd4b6d3 100644 --- a/extensions/random-animal/canvas-kit/.kit-version.json +++ b/extensions/random-animal/canvas-kit/.kit-version.json @@ -1,5 +1,5 @@ { - "version": "2026-07-04.1", - "syncedAt": "2026-07-05T16:16:13.630Z", + "version": "2026-07-05.1", + "syncedAt": "2026-07-06T05:14:32.246Z", "source": "create-canvas-app/kit" } diff --git a/extensions/random-animal/canvas-kit/theme.css b/extensions/random-animal/canvas-kit/theme.css index 1627e49..532994f 100644 --- a/extensions/random-animal/canvas-kit/theme.css +++ b/extensions/random-animal/canvas-kit/theme.css @@ -2,15 +2,31 @@ * * Baseline canvas styling built on the Primer design tokens the runtime injects * onto the canvas document (see github-app src/lib/extensionCanvasThemeContract.json). - * Every token has a hardcoded dark fallback so the canvas still looks right if a - * token is ever missing. Author canvas-specific styles on top of these. + * + * Theme awareness. The tokens the host injects are computed from the *current* + * user theme, so a light-theme user gets light values and a dark-theme user gets + * dark ones. Author against the --ck-* aliases below and your canvas follows the + * user's theme for free. Two rules keep that honest: + * 1. Never hardcode a dark-only color. Use a --ck-* alias, an injected token, or + * color-mix() of one so the value tracks the theme. (--ck-bg-inset derives + * itself from --ck-bg-muted + --ck-fg for exactly this reason.) + * 2. The dark fallbacks below only apply when a token is missing. Because the + * host injects its theme *after* first paint, a light user would otherwise + * see a dark flash, so the block after :root follows the OS color scheme + * until the host takes over (it sets data-color-mode when it does). */ :root { /* semantic aliases -> injected tokens (fallbacks = GitHub dark) */ --ck-bg: var(--background-color-default, #0d1117); --ck-bg-muted: var(--background-color-muted, #161b22); - --ck-bg-inset: var(--background-color-emphasis, #010409); + /* Recessed control surface (tabs track, wells, chips). Derived from the two + theme-aware tokens above rather than --background-color-emphasis, which is a + high-contrast chip color that is *dark* in light mode (#25292e) and would + paint every inset surface as a dark box on a light canvas. This mix stays a + touch more prominent than a card in either theme (dark ~#363c43, light + ~#d6d8db). */ + --ck-bg-inset: color-mix(in srgb, var(--ck-bg-muted) 85%, var(--ck-fg) 15%); --ck-fg: var(--text-color-default, #f0f6fc); --ck-muted: var(--text-color-muted, #8b949e); --ck-link: var(--text-color-link, #58a6ff); @@ -33,6 +49,33 @@ --ck-gap: 12px; } +/* Theme-aware fallback (see header note 2). The host injects its Primer tokens + only after first paint. Until then (and in any non-app context that never + injects) follow the webview's OS color scheme instead of forcing dark. Both + rules are gated on :not([data-color-mode]): the host sets that attribute + alongside its tokens and explicit color-scheme, so the instant it themes the + canvas these back off and the host is fully in control. --ck-bg-inset is + omitted on purpose; it derives from --ck-bg-muted + --ck-fg, which are + redefined here, so it tracks the light values automatically. */ +:root:not([data-color-mode]) { color-scheme: light dark; } + +@media (prefers-color-scheme: light) { + :root:not([data-color-mode]) { + --ck-bg: var(--background-color-default, #ffffff); + --ck-bg-muted: var(--background-color-muted, #f6f8fa); + --ck-fg: var(--text-color-default, #1f2328); + --ck-muted: var(--text-color-muted, #59636e); + --ck-link: var(--text-color-link, #0969da); + --ck-accent: var(--text-color-accent, #0969da); + --ck-danger: var(--text-color-danger, #d1242f); + --ck-success: var(--text-color-success, #1a7f37); + --ck-attention: var(--text-color-attention, #9a6700); + --ck-border: var(--border-color-default, #d1d9e0); + --ck-border-muted: var(--border-color-muted, #d1d9e0); + --ck-focus: var(--color-focus-outline, var(--outline-color-focus-default, #0969da)); + } +} + * { box-sizing: border-box; } html, body { @@ -143,8 +186,8 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold border: 1px solid var(--ck-border); color: var(--ck-muted); } -/* Generic semantic badge variants — map your own statuses to these in the view - (e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add +/* Generic semantic badge variants (map your own statuses to these in the view; + e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add app-specific status names here; this is the shared kit theme. */ .ck-badge-success { color: var(--ck-success); border-color: var(--border-color-success-emphasis, #238636); } .ck-badge-accent { color: var(--text-color-accent, #a371f7); border-color: var(--border-color-accent-emphasis, #8957e5); } @@ -170,7 +213,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold .ck-empty { color: var(--ck-muted); text-align: center; padding: 28px 0; } /* ---- loading: spinner + skeleton ----------------------------------------- */ -/* Spinner: pair with a Lucide "loader-circle" icon — +/* Spinner: pair with a Lucide "loader-circle" icon: html`<${Icon} name="loader-circle" class="ck-spinner" />`. */ .ck-spinner { animation: ck-spin 0.9s linear infinite; transform-origin: center; } @keyframes ck-spin { to { transform: rotate(360deg); } } @@ -188,7 +231,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold position: absolute; inset: 0; transform: translateX(-100%); - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent); + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ck-fg) 8%, transparent), transparent); animation: ck-shimmer 1.2s ease-in-out infinite; } @keyframes ck-shimmer { to { transform: translateX(100%); } } @@ -217,7 +260,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold /* ---- motion safety ------------------------------------------------------- */ /* Global guard: respect the OS "reduce motion" setting for every animation a canvas adds (kit spinners/skeletons included). Author-added keyframes inherit - this automatically — you do not need to repeat it per canvas. */ + this automatically, so you do not need to repeat it per canvas. */ @media (prefers-reduced-motion: reduce) { *, *::before, diff --git a/extensions/random-animal/canvas-kit/version.mjs b/extensions/random-animal/canvas-kit/version.mjs index 1db3bee..93471a9 100644 --- a/extensions/random-animal/canvas-kit/version.mjs +++ b/extensions/random-animal/canvas-kit/version.mjs @@ -10,4 +10,4 @@ // convention (the skill ships as files, not an npm version); a commit short-sha // works too. Re-exported from client.mjs so a canvas can read it at runtime. -export const KIT_VERSION = "2026-07-04.1"; +export const KIT_VERSION = "2026-07-05.1"; diff --git a/extensions/stock-ticker/canvas-kit/.kit-version.json b/extensions/stock-ticker/canvas-kit/.kit-version.json index 292997b..80c9034 100644 --- a/extensions/stock-ticker/canvas-kit/.kit-version.json +++ b/extensions/stock-ticker/canvas-kit/.kit-version.json @@ -1,5 +1,5 @@ { - "version": "2026-07-04.1", - "syncedAt": "2026-07-05T16:16:13.766Z", + "version": "2026-07-05.1", + "syncedAt": "2026-07-06T05:14:32.390Z", "source": "create-canvas-app/kit" } diff --git a/extensions/stock-ticker/canvas-kit/theme.css b/extensions/stock-ticker/canvas-kit/theme.css index 1627e49..532994f 100644 --- a/extensions/stock-ticker/canvas-kit/theme.css +++ b/extensions/stock-ticker/canvas-kit/theme.css @@ -2,15 +2,31 @@ * * Baseline canvas styling built on the Primer design tokens the runtime injects * onto the canvas document (see github-app src/lib/extensionCanvasThemeContract.json). - * Every token has a hardcoded dark fallback so the canvas still looks right if a - * token is ever missing. Author canvas-specific styles on top of these. + * + * Theme awareness. The tokens the host injects are computed from the *current* + * user theme, so a light-theme user gets light values and a dark-theme user gets + * dark ones. Author against the --ck-* aliases below and your canvas follows the + * user's theme for free. Two rules keep that honest: + * 1. Never hardcode a dark-only color. Use a --ck-* alias, an injected token, or + * color-mix() of one so the value tracks the theme. (--ck-bg-inset derives + * itself from --ck-bg-muted + --ck-fg for exactly this reason.) + * 2. The dark fallbacks below only apply when a token is missing. Because the + * host injects its theme *after* first paint, a light user would otherwise + * see a dark flash, so the block after :root follows the OS color scheme + * until the host takes over (it sets data-color-mode when it does). */ :root { /* semantic aliases -> injected tokens (fallbacks = GitHub dark) */ --ck-bg: var(--background-color-default, #0d1117); --ck-bg-muted: var(--background-color-muted, #161b22); - --ck-bg-inset: var(--background-color-emphasis, #010409); + /* Recessed control surface (tabs track, wells, chips). Derived from the two + theme-aware tokens above rather than --background-color-emphasis, which is a + high-contrast chip color that is *dark* in light mode (#25292e) and would + paint every inset surface as a dark box on a light canvas. This mix stays a + touch more prominent than a card in either theme (dark ~#363c43, light + ~#d6d8db). */ + --ck-bg-inset: color-mix(in srgb, var(--ck-bg-muted) 85%, var(--ck-fg) 15%); --ck-fg: var(--text-color-default, #f0f6fc); --ck-muted: var(--text-color-muted, #8b949e); --ck-link: var(--text-color-link, #58a6ff); @@ -33,6 +49,33 @@ --ck-gap: 12px; } +/* Theme-aware fallback (see header note 2). The host injects its Primer tokens + only after first paint. Until then (and in any non-app context that never + injects) follow the webview's OS color scheme instead of forcing dark. Both + rules are gated on :not([data-color-mode]): the host sets that attribute + alongside its tokens and explicit color-scheme, so the instant it themes the + canvas these back off and the host is fully in control. --ck-bg-inset is + omitted on purpose; it derives from --ck-bg-muted + --ck-fg, which are + redefined here, so it tracks the light values automatically. */ +:root:not([data-color-mode]) { color-scheme: light dark; } + +@media (prefers-color-scheme: light) { + :root:not([data-color-mode]) { + --ck-bg: var(--background-color-default, #ffffff); + --ck-bg-muted: var(--background-color-muted, #f6f8fa); + --ck-fg: var(--text-color-default, #1f2328); + --ck-muted: var(--text-color-muted, #59636e); + --ck-link: var(--text-color-link, #0969da); + --ck-accent: var(--text-color-accent, #0969da); + --ck-danger: var(--text-color-danger, #d1242f); + --ck-success: var(--text-color-success, #1a7f37); + --ck-attention: var(--text-color-attention, #9a6700); + --ck-border: var(--border-color-default, #d1d9e0); + --ck-border-muted: var(--border-color-muted, #d1d9e0); + --ck-focus: var(--color-focus-outline, var(--outline-color-focus-default, #0969da)); + } +} + * { box-sizing: border-box; } html, body { @@ -143,8 +186,8 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold border: 1px solid var(--ck-border); color: var(--ck-muted); } -/* Generic semantic badge variants — map your own statuses to these in the view - (e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add +/* Generic semantic badge variants (map your own statuses to these in the view; + e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add app-specific status names here; this is the shared kit theme. */ .ck-badge-success { color: var(--ck-success); border-color: var(--border-color-success-emphasis, #238636); } .ck-badge-accent { color: var(--text-color-accent, #a371f7); border-color: var(--border-color-accent-emphasis, #8957e5); } @@ -170,7 +213,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold .ck-empty { color: var(--ck-muted); text-align: center; padding: 28px 0; } /* ---- loading: spinner + skeleton ----------------------------------------- */ -/* Spinner: pair with a Lucide "loader-circle" icon — +/* Spinner: pair with a Lucide "loader-circle" icon: html`<${Icon} name="loader-circle" class="ck-spinner" />`. */ .ck-spinner { animation: ck-spin 0.9s linear infinite; transform-origin: center; } @keyframes ck-spin { to { transform: rotate(360deg); } } @@ -188,7 +231,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold position: absolute; inset: 0; transform: translateX(-100%); - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent); + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ck-fg) 8%, transparent), transparent); animation: ck-shimmer 1.2s ease-in-out infinite; } @keyframes ck-shimmer { to { transform: translateX(100%); } } @@ -217,7 +260,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold /* ---- motion safety ------------------------------------------------------- */ /* Global guard: respect the OS "reduce motion" setting for every animation a canvas adds (kit spinners/skeletons included). Author-added keyframes inherit - this automatically — you do not need to repeat it per canvas. */ + this automatically, so you do not need to repeat it per canvas. */ @media (prefers-reduced-motion: reduce) { *, *::before, diff --git a/extensions/stock-ticker/canvas-kit/version.mjs b/extensions/stock-ticker/canvas-kit/version.mjs index 1db3bee..93471a9 100644 --- a/extensions/stock-ticker/canvas-kit/version.mjs +++ b/extensions/stock-ticker/canvas-kit/version.mjs @@ -10,4 +10,4 @@ // convention (the skill ships as files, not an npm version); a commit short-sha // works too. Re-exported from client.mjs so a canvas can read it at runtime. -export const KIT_VERSION = "2026-07-04.1"; +export const KIT_VERSION = "2026-07-05.1"; diff --git a/extensions/wiki-discover/canvas-kit/.kit-version.json b/extensions/wiki-discover/canvas-kit/.kit-version.json index 45178c7..4685d99 100644 --- a/extensions/wiki-discover/canvas-kit/.kit-version.json +++ b/extensions/wiki-discover/canvas-kit/.kit-version.json @@ -1,5 +1,5 @@ { - "version": "2026-07-04.1", - "syncedAt": "2026-07-05T16:16:13.900Z", + "version": "2026-07-05.1", + "syncedAt": "2026-07-06T05:14:32.552Z", "source": "create-canvas-app/kit" } diff --git a/extensions/wiki-discover/canvas-kit/theme.css b/extensions/wiki-discover/canvas-kit/theme.css index 1627e49..532994f 100644 --- a/extensions/wiki-discover/canvas-kit/theme.css +++ b/extensions/wiki-discover/canvas-kit/theme.css @@ -2,15 +2,31 @@ * * Baseline canvas styling built on the Primer design tokens the runtime injects * onto the canvas document (see github-app src/lib/extensionCanvasThemeContract.json). - * Every token has a hardcoded dark fallback so the canvas still looks right if a - * token is ever missing. Author canvas-specific styles on top of these. + * + * Theme awareness. The tokens the host injects are computed from the *current* + * user theme, so a light-theme user gets light values and a dark-theme user gets + * dark ones. Author against the --ck-* aliases below and your canvas follows the + * user's theme for free. Two rules keep that honest: + * 1. Never hardcode a dark-only color. Use a --ck-* alias, an injected token, or + * color-mix() of one so the value tracks the theme. (--ck-bg-inset derives + * itself from --ck-bg-muted + --ck-fg for exactly this reason.) + * 2. The dark fallbacks below only apply when a token is missing. Because the + * host injects its theme *after* first paint, a light user would otherwise + * see a dark flash, so the block after :root follows the OS color scheme + * until the host takes over (it sets data-color-mode when it does). */ :root { /* semantic aliases -> injected tokens (fallbacks = GitHub dark) */ --ck-bg: var(--background-color-default, #0d1117); --ck-bg-muted: var(--background-color-muted, #161b22); - --ck-bg-inset: var(--background-color-emphasis, #010409); + /* Recessed control surface (tabs track, wells, chips). Derived from the two + theme-aware tokens above rather than --background-color-emphasis, which is a + high-contrast chip color that is *dark* in light mode (#25292e) and would + paint every inset surface as a dark box on a light canvas. This mix stays a + touch more prominent than a card in either theme (dark ~#363c43, light + ~#d6d8db). */ + --ck-bg-inset: color-mix(in srgb, var(--ck-bg-muted) 85%, var(--ck-fg) 15%); --ck-fg: var(--text-color-default, #f0f6fc); --ck-muted: var(--text-color-muted, #8b949e); --ck-link: var(--text-color-link, #58a6ff); @@ -33,6 +49,33 @@ --ck-gap: 12px; } +/* Theme-aware fallback (see header note 2). The host injects its Primer tokens + only after first paint. Until then (and in any non-app context that never + injects) follow the webview's OS color scheme instead of forcing dark. Both + rules are gated on :not([data-color-mode]): the host sets that attribute + alongside its tokens and explicit color-scheme, so the instant it themes the + canvas these back off and the host is fully in control. --ck-bg-inset is + omitted on purpose; it derives from --ck-bg-muted + --ck-fg, which are + redefined here, so it tracks the light values automatically. */ +:root:not([data-color-mode]) { color-scheme: light dark; } + +@media (prefers-color-scheme: light) { + :root:not([data-color-mode]) { + --ck-bg: var(--background-color-default, #ffffff); + --ck-bg-muted: var(--background-color-muted, #f6f8fa); + --ck-fg: var(--text-color-default, #1f2328); + --ck-muted: var(--text-color-muted, #59636e); + --ck-link: var(--text-color-link, #0969da); + --ck-accent: var(--text-color-accent, #0969da); + --ck-danger: var(--text-color-danger, #d1242f); + --ck-success: var(--text-color-success, #1a7f37); + --ck-attention: var(--text-color-attention, #9a6700); + --ck-border: var(--border-color-default, #d1d9e0); + --ck-border-muted: var(--border-color-muted, #d1d9e0); + --ck-focus: var(--color-focus-outline, var(--outline-color-focus-default, #0969da)); + } +} + * { box-sizing: border-box; } html, body { @@ -143,8 +186,8 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold border: 1px solid var(--ck-border); color: var(--ck-muted); } -/* Generic semantic badge variants — map your own statuses to these in the view - (e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add +/* Generic semantic badge variants (map your own statuses to these in the view; + e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add app-specific status names here; this is the shared kit theme. */ .ck-badge-success { color: var(--ck-success); border-color: var(--border-color-success-emphasis, #238636); } .ck-badge-accent { color: var(--text-color-accent, #a371f7); border-color: var(--border-color-accent-emphasis, #8957e5); } @@ -170,7 +213,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold .ck-empty { color: var(--ck-muted); text-align: center; padding: 28px 0; } /* ---- loading: spinner + skeleton ----------------------------------------- */ -/* Spinner: pair with a Lucide "loader-circle" icon — +/* Spinner: pair with a Lucide "loader-circle" icon: html`<${Icon} name="loader-circle" class="ck-spinner" />`. */ .ck-spinner { animation: ck-spin 0.9s linear infinite; transform-origin: center; } @keyframes ck-spin { to { transform: rotate(360deg); } } @@ -188,7 +231,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold position: absolute; inset: 0; transform: translateX(-100%); - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent); + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ck-fg) 8%, transparent), transparent); animation: ck-shimmer 1.2s ease-in-out infinite; } @keyframes ck-shimmer { to { transform: translateX(100%); } } @@ -217,7 +260,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold /* ---- motion safety ------------------------------------------------------- */ /* Global guard: respect the OS "reduce motion" setting for every animation a canvas adds (kit spinners/skeletons included). Author-added keyframes inherit - this automatically — you do not need to repeat it per canvas. */ + this automatically, so you do not need to repeat it per canvas. */ @media (prefers-reduced-motion: reduce) { *, *::before, diff --git a/extensions/wiki-discover/canvas-kit/version.mjs b/extensions/wiki-discover/canvas-kit/version.mjs index 1db3bee..93471a9 100644 --- a/extensions/wiki-discover/canvas-kit/version.mjs +++ b/extensions/wiki-discover/canvas-kit/version.mjs @@ -10,4 +10,4 @@ // convention (the skill ships as files, not an npm version); a commit short-sha // works too. Re-exported from client.mjs so a canvas can read it at runtime. -export const KIT_VERSION = "2026-07-04.1"; +export const KIT_VERSION = "2026-07-05.1";