diff --git a/docs/assets/control-plane-dispatch-fallback-dark.svg b/docs/assets/control-plane-dispatch-fallback-dark.svg new file mode 100644 index 0000000..bb87d64 --- /dev/null +++ b/docs/assets/control-plane-dispatch-fallback-dark.svg @@ -0,0 +1,92 @@ + + Central control plane pipeline + A central control plane dispatches operation packages such as Dependabot and Optimization through every organization to every repo, then correlates the outcomes into an operational report excerpt. + + + + + + + + + + + + + + + + + + + + + + + + Control plane + + + + + + + + Dependabot + + + + + + Optimization + + + Org A + Org B + Org C + + + + Repo 1Repo 2Repo 3Repo 4 + + + + + REPORT EXCERPT + 2026-08-27 07:02:56 UTC + Execution health + + + RUNS · 24H + 732 + FAILURE RATE + 34.2% + + + + + + + VALUE OVER TIME + + + + + + ADOPTED + + + + + + + + + + \ No newline at end of file diff --git a/docs/assets/control-plane-dispatch-fallback.svg b/docs/assets/control-plane-dispatch-fallback.svg index e93d20f..8fc8bfd 100644 --- a/docs/assets/control-plane-dispatch-fallback.svg +++ b/docs/assets/control-plane-dispatch-fallback.svg @@ -8,7 +8,6 @@ [fill="#1f883d"]{fill:var(--success)} [fill="#cf222e"]{fill:var(--danger)} [fill="#bf8700"]{fill:var(--attention)} [fill="#8250df"]{fill:var(--accent)} [fill="#0969da"]{fill:var(--info)} [stroke="#ffffff"]{stroke:var(--surface)} [stroke="#d0d7de"]{stroke:var(--border)} [stroke="#afb8c1"]{stroke:var(--connector)} [stroke="#8250df"]{stroke:var(--accent)} [stroke="#1f883d"]{stroke:var(--success)} [stroke="#cf222e"]{stroke:var(--danger)} [stroke="#bf8700"]{stroke:var(--attention)} [stroke="#0969da"]{stroke:var(--info)} - @media(prefers-color-scheme:dark){:root{--surface:#161b22;--foreground:#f0f6fc;--muted:#8b949e;--border:#30363d;--connector:#484f58;--success:#3fb950;--danger:#f85149;--attention:#d29922;--accent:#a371f7;--info:#58a6ff;--shadow:#010409}} diff --git a/docs/assets/control-plane-dispatch-mobile-dark.svg b/docs/assets/control-plane-dispatch-mobile-dark.svg new file mode 100644 index 0000000..ea17011 --- /dev/null +++ b/docs/assets/control-plane-dispatch-mobile-dark.svg @@ -0,0 +1,88 @@ + + Central control plane pipeline + A central control plane dispatches operation packages such as Dependabot and Optimization downward through every organization to every repo, then correlates the outcomes into an operational report excerpt. + + + + + + + + + + + + + + + + + + + + + + + + + + + Dependabot + + + + + + + Optimization + + + Org A + Org B + Org C + + + + Repo 1Repo 2Repo 3Repo 4 + + + + + REPORT EXCERPT + 2026-08-27 07:02:56 UTC + Execution health + + + RUNS · 24H + 732 + FAILURE RATE + 34.2% + + + + + + + VALUE OVER TIME + + + + + + ADOPTED + + + + + + + + + + \ No newline at end of file diff --git a/docs/assets/control-plane-dispatch-mobile.svg b/docs/assets/control-plane-dispatch-mobile.svg index 0f11fa2..c180ef5 100644 --- a/docs/assets/control-plane-dispatch-mobile.svg +++ b/docs/assets/control-plane-dispatch-mobile.svg @@ -8,7 +8,6 @@ [fill="#1f883d"]{fill:var(--success)} [fill="#cf222e"]{fill:var(--danger)} [fill="#bf8700"]{fill:var(--attention)} [fill="#8250df"]{fill:var(--accent)} [fill="#0969da"]{fill:var(--info)} [stroke="#ffffff"]{stroke:var(--surface)} [stroke="#d0d7de"]{stroke:var(--border)} [stroke="#afb8c1"]{stroke:var(--connector)} [stroke="#8250df"]{stroke:var(--accent)} [stroke="#1f883d"]{stroke:var(--success)} [stroke="#cf222e"]{stroke:var(--danger)} [stroke="#bf8700"]{stroke:var(--attention)} [stroke="#0969da"]{stroke:var(--info)} - @media(prefers-color-scheme:dark){:root{--surface:#161b22;--foreground:#f0f6fc;--muted:#8b949e;--border:#30363d;--connector:#484f58;--success:#3fb950;--danger:#f85149;--attention:#d29922;--accent:#a371f7;--info:#58a6ff;--shadow:#010409}} diff --git a/docs/assets/control-plane-dispatch.animation.mjs b/docs/assets/control-plane-dispatch.animation.mjs index 6059324..02f21df 100644 --- a/docs/assets/control-plane-dispatch.animation.mjs +++ b/docs/assets/control-plane-dispatch.animation.mjs @@ -1,19 +1,37 @@ +// Colors are resolved to a concrete light/dark palette in JS (rather than relying on an +// embedded `@media (prefers-color-scheme: dark)` stylesheet inside each SVG) because Safari +// does not evaluate that media query for SVGs referenced via a data URI or ``/`` +// element: https://bugs.webkit.org/show_bug.cgi?id=199134 const SPARKLE_ASSET_ID = "octicon-sparkle-fill"; const SPARKLE_SIZE = 16; -const SPARKLE_SVG = ``; -const SPARKLE_DATA_URI = `data:image/svg+xml,${encodeURIComponent(SPARKLE_SVG)}`; +const sparkleSvg = (dark) => + ``; +const sparkleDataUri = (dark) => `data:image/svg+xml,${encodeURIComponent(sparkleSvg(dark))}`; const STATUS_PENDING_ASSET_ID = "status-pending"; const STATUS_SUCCESS_ASSET_ID = "status-success"; const STATUS_SIZE = 20; -const STATUS_PENDING_SVG = ``; -const STATUS_SUCCESS_SVG = ``; -const STATUS_PENDING_DATA_URI = `data:image/svg+xml,${encodeURIComponent(STATUS_PENDING_SVG)}`; -const STATUS_SUCCESS_DATA_URI = `data:image/svg+xml,${encodeURIComponent(STATUS_SUCCESS_SVG)}`; +const statusPendingSvg = (dark) => + ``; +const statusSuccessSvg = (dark) => + ``; +const statusPendingDataUri = (dark) => `data:image/svg+xml,${encodeURIComponent(statusPendingSvg(dark))}`; +const statusSuccessDataUri = (dark) => `data:image/svg+xml,${encodeURIComponent(statusSuccessSvg(dark))}`; const REPORT_UPDATED_ASSET_ID = "report-updated"; const REPORT_WIDTH = 274; const REPORT_HEIGHT = 272; -const REPORT_UPDATED_SVG = `REPORT EXCERPT2026-08-27 07:07:56 UTCExecution healthRUNS · 24H748FAILURE RATE0.0%VALUE OVER TIMEADOPTED`; -const REPORT_UPDATED_DATA_URI = `data:image/svg+xml,${encodeURIComponent(REPORT_UPDATED_SVG)}`; +const reportUpdatedSvg = (dark) => { + const surface = dark ? "#161b22" : "#fff"; + const foreground = dark ? "#f0f6fc" : "#1f2328"; + const muted = dark ? "#8b949e" : "#656d76"; + const border = dark ? "#30363d" : "#d0d7de"; + const success = dark ? "#3fb950" : "#1f883d"; + const danger = dark ? "#f85149" : "#cf222e"; + const attention = dark ? "#d29922" : "#bf8700"; + const accent = dark ? "#a371f7" : "#8250df"; + const info = dark ? "#58a6ff" : "#0969da"; + return `REPORT EXCERPT2026-08-27 07:07:56 UTCExecution healthRUNS · 24H748FAILURE RATE0.0%VALUE OVER TIMEADOPTED`; +}; +const reportUpdatedDataUri = (dark) => `data:image/svg+xml,${encodeURIComponent(reportUpdatedSvg(dark))}`; const linear = { i: { x: 0.667, y: 1 }, o: { x: 0.333, y: 0 } }; const staticValue = (value) => ({ a: 0, k: value }); @@ -236,7 +254,16 @@ function createMobileLayers() { return layers; } -export default function createAnimationData() { +function pipelineAssets(prefersDark) { + return [ + { id: SPARKLE_ASSET_ID, w: SPARKLE_SIZE, h: SPARKLE_SIZE, u: "", p: sparkleDataUri(prefersDark), e: 1 }, + { id: STATUS_PENDING_ASSET_ID, w: STATUS_SIZE, h: STATUS_SIZE, u: "", p: statusPendingDataUri(prefersDark), e: 1 }, + { id: STATUS_SUCCESS_ASSET_ID, w: STATUS_SIZE, h: STATUS_SIZE, u: "", p: statusSuccessDataUri(prefersDark), e: 1 }, + { id: REPORT_UPDATED_ASSET_ID, w: REPORT_WIDTH, h: REPORT_HEIGHT, u: "", p: reportUpdatedDataUri(prefersDark), e: 1 }, + ]; +} + +export default function createAnimationData(prefersDark = false) { return { v: "5.12.2", fr: 60, @@ -246,18 +273,13 @@ export default function createAnimationData() { h: 560, nm: "Central Agentic Ops pipeline", ddd: 0, - assets: [ - { id: SPARKLE_ASSET_ID, w: SPARKLE_SIZE, h: SPARKLE_SIZE, u: "", p: SPARKLE_DATA_URI, e: 1 }, - { id: STATUS_PENDING_ASSET_ID, w: STATUS_SIZE, h: STATUS_SIZE, u: "", p: STATUS_PENDING_DATA_URI, e: 1 }, - { id: STATUS_SUCCESS_ASSET_ID, w: STATUS_SIZE, h: STATUS_SIZE, u: "", p: STATUS_SUCCESS_DATA_URI, e: 1 }, - { id: REPORT_UPDATED_ASSET_ID, w: REPORT_WIDTH, h: REPORT_HEIGHT, u: "", p: REPORT_UPDATED_DATA_URI, e: 1 }, - ], + assets: pipelineAssets(prefersDark), layers: createLayers(), markers: [], }; } -export function createMobileAnimationData() { +export function createMobileAnimationData(prefersDark = false) { return { v: "5.12.2", fr: 60, @@ -267,12 +289,7 @@ export function createMobileAnimationData() { h: 886, nm: "Central Agentic Ops mobile pipeline", ddd: 0, - assets: [ - { id: SPARKLE_ASSET_ID, w: SPARKLE_SIZE, h: SPARKLE_SIZE, u: "", p: SPARKLE_DATA_URI, e: 1 }, - { id: STATUS_PENDING_ASSET_ID, w: STATUS_SIZE, h: STATUS_SIZE, u: "", p: STATUS_PENDING_DATA_URI, e: 1 }, - { id: STATUS_SUCCESS_ASSET_ID, w: STATUS_SIZE, h: STATUS_SIZE, u: "", p: STATUS_SUCCESS_DATA_URI, e: 1 }, - { id: REPORT_UPDATED_ASSET_ID, w: REPORT_WIDTH, h: REPORT_HEIGHT, u: "", p: REPORT_UPDATED_DATA_URI, e: 1 }, - ], + assets: pipelineAssets(prefersDark), layers: createMobileLayers(), markers: [], }; diff --git a/docs/components/HierarchyHero.astro b/docs/components/HierarchyHero.astro index 969f7b7..294803c 100644 --- a/docs/components/HierarchyHero.astro +++ b/docs/components/HierarchyHero.astro @@ -1,8 +1,9 @@ --- -import { Image } from "astro:assets"; import type { HTMLAttributes } from "astro/types"; import dispatchFallback from "../assets/control-plane-dispatch-fallback.svg"; +import dispatchFallbackDark from "../assets/control-plane-dispatch-fallback-dark.svg"; import mobileDispatchFallback from "../assets/control-plane-dispatch-mobile.svg"; +import mobileDispatchFallbackDark from "../assets/control-plane-dispatch-mobile-dark.svg"; import OpsWizard from "./OpsWizard.astro"; const PAGE_TITLE_ID = "_top"; @@ -58,12 +59,34 @@ const { title = data.title, tagline, actions = [] } = data.hero || {};
- + + + +
@@ -78,18 +101,24 @@ const { title = data.title, tagline, actions = [] } = data.hero || {}; const animations = new WeakMap>(); const observers = new WeakMap(); + // Safari does not evaluate `prefers-color-scheme` inside SVGs referenced from a data URI, so + // the animation's embedded icon colors are resolved here (in the host document) instead: + // https://bugs.webkit.org/show_bug.cgi?id=199134 + const darkSchemeQuery = window.matchMedia("(prefers-color-scheme: dark)"); + function initializeHierarchyAnimations() { if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; document.querySelectorAll(".dispatch-motion").forEach((container) => { if (animations.has(container)) return; + const isMobile = container instanceof HTMLElement && container.dataset.layout === "mobile"; const animation = lottie.loadAnimation({ container, renderer: "svg", loop: true, autoplay: false, - animationData: container instanceof HTMLElement && container.dataset.layout === "mobile" ? createMobileAnimationData() : createAnimationData(), + animationData: isMobile ? createMobileAnimationData(darkSchemeQuery.matches) : createAnimationData(darkSchemeQuery.matches), rendererSettings: { preserveAspectRatio: "xMidYMid meet", progressiveLoad: true, @@ -119,6 +148,10 @@ const { title = data.title, tagline, actions = [] } = data.hero || {}; initializeHierarchyAnimations(); document.addEventListener("astro:page-load", initializeHierarchyAnimations); document.addEventListener("astro:before-swap", destroyHierarchyAnimations); + darkSchemeQuery.addEventListener("change", () => { + destroyHierarchyAnimations(); + initializeHierarchyAnimations(); + });