diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 9abcf78e92..6068fecda0 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -110,6 +110,8 @@ jobs:
run: pnpm exec pixel-storybook
env:
PIXEL_KEY: ${{ secrets.PIXEL_KEY }}
+ # On pull_request, github.sha is a synthetic merge commit, not the PR head.
+ PIXEL_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
# Auto-approve on mainline to avoid blocking CI after squash merges.
PIXEL_AUTO_REVIEW: ${{ github.ref == 'refs/heads/main' }}
diff --git a/.storybook/preview.ts b/.storybook/preview.ts
index ad5833c39f..ef464fb77a 100644
--- a/.storybook/preview.ts
+++ b/.storybook/preview.ts
@@ -1,5 +1,6 @@
///
+import { isPixel } from "@coder/pixel-storybook/storyapi";
import codiconCssUrl from "@vscode/codicons/dist/codicon.css?url";
import { createElement } from "react";
@@ -29,6 +30,11 @@ if (typeof window !== "undefined") {
});
}
+// Lets us skip motion animation during Pixel captures.
+if (typeof document !== "undefined" && isPixel()) {
+ document.documentElement.setAttribute("data-pixel", "true");
+}
+
// Inject codicon stylesheet immediately (before any components render)
// Must be a element with id "vscode-codicon-stylesheet" for vscode-elements
if (
diff --git a/packages/ui/src/components/Icon/Icon.css b/packages/ui/src/components/Icon/Icon.css
index 42a3429401..9967622c11 100644
--- a/packages/ui/src/components/Icon/Icon.css
+++ b/packages/ui/src/components/Icon/Icon.css
@@ -13,3 +13,8 @@
animation: none;
}
}
+
+/* data-pixel is set in .storybook/preview.ts via isPixel(). */
+:root[data-pixel] .ui-icon--spin {
+ animation: none;
+}
diff --git a/packages/ui/src/components/Menu/Menu.css b/packages/ui/src/components/Menu/Menu.css
index c4a10c0e0a..ec56a68507 100644
--- a/packages/ui/src/components/Menu/Menu.css
+++ b/packages/ui/src/components/Menu/Menu.css
@@ -140,6 +140,14 @@
}
}
+/* data-pixel is set in .storybook/preview.ts via isPixel(). */
+@media (prefers-reduced-motion: no-preference) {
+ :where(:root[data-pixel]) .ui-menu[data-state="open"],
+ :where(:root[data-pixel]) .ui-menu[data-state="closed"] {
+ animation: none;
+ }
+}
+
@media (forced-colors: active) {
.ui-menu__item[data-highlighted],
.ui-menu__item[data-state="open"] {
diff --git a/packages/ui/src/components/ProgressBar/ProgressBar.css b/packages/ui/src/components/ProgressBar/ProgressBar.css
index dc31caec85..5dcdd4453b 100644
--- a/packages/ui/src/components/ProgressBar/ProgressBar.css
+++ b/packages/ui/src/components/ProgressBar/ProgressBar.css
@@ -52,3 +52,8 @@
background: Highlight;
}
}
+
+/* data-pixel is set in .storybook/preview.ts via isPixel(). */
+:root[data-pixel] .ui-progress-bar--indeterminate .ui-progress-bar__indicator {
+ animation: none;
+}
diff --git a/packages/ui/src/components/Spinner/Spinner.css b/packages/ui/src/components/Spinner/Spinner.css
index 023c91a016..eb32eb4ba4 100644
--- a/packages/ui/src/components/Spinner/Spinner.css
+++ b/packages/ui/src/components/Spinner/Spinner.css
@@ -50,3 +50,8 @@
border-inline-end-color: Highlight;
}
}
+
+/* data-pixel is set in .storybook/preview.ts via isPixel(). */
+:root[data-pixel] .ui-spinner {
+ animation: none;
+}
diff --git a/packages/ui/src/vscode-parity.stories.tsx b/packages/ui/src/vscode-parity.stories.tsx
index d37c2067f4..a9a7e49a93 100644
--- a/packages/ui/src/vscode-parity.stories.tsx
+++ b/packages/ui/src/vscode-parity.stories.tsx
@@ -24,7 +24,7 @@ import { ProgressBar } from "./components/ProgressBar/ProgressBar";
import { SearchInput } from "./components/SearchInput/SearchInput";
import { Spinner } from "./components/Spinner/Spinner";
import { StatusPill } from "./components/StatusPill/StatusPill";
-import { openMenu, PIXEL_ALL_THEMES } from "./storybook";
+import { PIXEL_ALL_THEMES } from "./storybook";
import type { Meta, StoryObj } from "@storybook/react-vite";
@@ -177,8 +177,7 @@ const Parity = (): React.JSX.Element => (
);
-/* The reference menu renders inline; ours is a real portalled DropdownMenu,
- so the play function opens it under its trigger. */
+/* defaultOpen + an invisible trigger mirror the reference's `show`. */
const MenuParity = (): React.JSX.Element => (
(
fontSize: "13px",
}}
>
-
+
-
+
Start workspace
@@ -228,7 +227,4 @@ export const SideBySide: Story = {};
export const Menu: Story = {
render: () => ,
- play: async ({ canvasElement }) => {
- await openMenu(canvasElement, "Menu");
- },
};