Skip to content

Migrate sanity-plugin-media styling from styled-components to vanilla-extract - #1847

Open
stipsan wants to merge 4 commits into
mainfrom
cursor/migrate-media-styled-components-d0d5
Open

Migrate sanity-plugin-media styling from styled-components to vanilla-extract#1847
stipsan wants to merge 4 commits into
mainfrom
cursor/migrate-media-styled-components-d0d5

Conversation

@stipsan

@stipsan stipsan commented Aug 6, 2026

Copy link
Copy Markdown
Member

What

Migrates sanity-plugin-media's styling from styled-components to vanilla-extract (zero-runtime CSS), following the migrate-styled-components-to-vanilla-extract skill.

How

  • Every styled(Component), styled.div, and createGlobalStyle usage is replaced with a colocated *.css.ts file (style() for static rules, createVar() + assignInlineVars() for theme/scheme-dependent values, globalStyle() for descendant/global selectors that vanilla-extract's selectors API doesn't allow, and plain conditional classes for boolean-driven variants like $selected/$updating/$picked).
  • Theme reads (({theme}) => theme.sanity.color...) are replaced with @sanity/ui's useTheme_v2() hook. Where the old flat color.spot.* had no v2 equivalent, the v2 replacement is color.avatar.<name>.bg; color.card.enabled.bg maps to the contextual color.bg.
  • Tag's ${TagContainer}:hover & cross-component selector and FileIcon's descendant text {...} rule both needed globalStyle() since vanilla-extract's selectors only allows selectors that resolve back to &.
  • Image's $scheme/$showCheckerboard transient props are renamed to scheme/showCheckerboard (no longer styled-components props) across all 5 call sites; the wrapper also picks up a default alt="" for accessibility now that it's a real <img> element in JSX.
  • src/types/sanity-ui.d.ts (the styled-components DefaultTheme augmentation) is deleted along with src/styled/GlobalStyles/index.tsx — the global scrollbar/dialog styles now live in GlobalStyles.css.ts, imported for its side effect directly where <GlobalStyle /> used to render.
  • tsdown.config.ts switches styledComponents: truevanillaExtract: true; vitest.config.ts registers vanillaExtractPlugin() and @vanilla-extract/css/disableRuntimeStyles.
  • package.json: drops the styled-components peer/devDependency, adds @vanilla-extract/css + @sanity/vanilla-extract-vite-plugin (devDependencies), @vanilla-extract/dynamic + clsx (dependencies).
  • Verified the package's sanity/@sanity/ui peer resolution in pnpm-lock.yaml still shares the single workspace-wide styled-components@6.4.4 variant (no fork).

Testing

  • pnpm format, pnpm lint, pnpm knip, pnpm build, pnpm test run all pass for the whole workspace (the test-studio build failure encountered mid-way was a pre-existing, unrelated sanity@next prerelease/auto-updates-CDN timing issue, reproducible on main with a fresh pnpm install, not something this PR introduces).
  • Manually verified visual fidelity against the pre-migration rendering in the test studio (pnpm dev): grid view, table view, folder cards/rows (yellow glyph + hover), the Create Folder dialog, and the Tags panel all render identically to before, with no unstyled elements or layout regressions.

Media browser grid view
Media browser table view
Folder card in grid view with yellow glyph
Folder row in table view
Create Folder dialog

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

…-extract

- Replace every styled(Component)/styled.div/createGlobalStyle usage with
  colocated .css.ts files (style(), createVar()/assignInlineVars() for
  theme/scheme-dependent values, globalStyle() for descendant/global
  selectors, and keyframe-free variants for boolean-driven styles)
- Read theme tokens via @sanity/ui's useTheme_v2() instead of the
  styled-components theme prop; drop the ambient styled-components theme
  augmentation (src/types/sanity-ui.d.ts)
- Rename the Image component's $scheme/$showCheckerboard transient props to
  scheme/showCheckerboard and update all call sites
- Switch tsdown.config.ts from styledComponents to vanillaExtract, register
  the vanilla-extract Vite plugin in vitest.config.ts, and update
  package.json dependencies (drop styled-components peer/devDependency, add
  @vanilla-extract/css, @vanilla-extract/dynamic, @sanity/vanilla-extract-vite-plugin, clsx)
- Add a patch changeset
@stipsan stipsan added the 🤖 bot label Aug 6, 2026 — with Cursor
@changeset-bot

changeset-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7f6175b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
sanity-plugin-media Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
plugins-studio Ready Ready Preview Aug 6, 2026 9:20am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
plugins-e2e-test-studio Ignored Ignored Aug 6, 2026 9:20am

Request Review

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

✅ E2E Tests

🟢 25 passed🟡 1 flakyview full reportview run

Studio: https://plugins-e2e-test-studio-c7uobuh0l.sanity.dev

Datasets: pr-1847-chromium-31088652430, pr-1847-firefox-31088652430

@socket-security

socket-security Bot commented Aug 6, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 31.92% 5493 / 17205
🔵 Statements 31.7% 5797 / 18284
🔵 Functions 27.81% 1431 / 5144
🔵 Branches 23.33% 2827 / 12117
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
plugins/sanity-plugin-media/src/components/AssetGridVirtualized/AssetGridVirtualized.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/AssetGridVirtualized/index.tsx 39.28% 23.07% 28.57% 37.03% 29-41, 47-48, 53-54, 70-85
plugins/sanity-plugin-media/src/components/CardAsset/CardAsset.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/CardAsset/index.tsx 93.15% 85.71% 100% 92.85% 185, 196-199, 221
plugins/sanity-plugin-media/src/components/CardFolder/CardFolder.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/CardFolder/index.tsx 11.11% 0% 0% 11.11% 20-35, 40-46
plugins/sanity-plugin-media/src/components/CardUpload/CardUpload.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/CardUpload/index.tsx 4.34% 0% 0% 4.54% 23, 27-156
plugins/sanity-plugin-media/src/components/DialogAssetEdit/index.tsx 69.79% 59.55% 66.66% 72.51% 34, 97, 122-123, 157, 169-172, 179-184, 191, 199, 207, 217, 226-254, 261, 283-287, 300, 316-318, 325-330, 386, 428
plugins/sanity-plugin-media/src/components/FileIcon/FileIcon.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/FileIcon/index.tsx 20% 0% 0% 20% 17-24, 29-43
plugins/sanity-plugin-media/src/components/FormFieldInputLabel/FormFieldInputLabel.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/FormFieldInputLabel/index.tsx 100% 75% 100% 100%
plugins/sanity-plugin-media/src/components/Image/Image.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/Image/index.tsx 0% 0% 0% 0% 16-26
plugins/sanity-plugin-media/src/components/SearchFacet/SearchFacet.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/SearchFacet/index.tsx 11.11% 100% 0% 11.11% 34-47, 52-89
plugins/sanity-plugin-media/src/components/SearchFacets/SearchFacets.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/SearchFacets/index.tsx 33.33% 20% 50% 30.43% 19-26, 37-54, 62-77
plugins/sanity-plugin-media/src/components/TableHeader/TableHeader.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/TableHeader/index.tsx 5.88% 0% 0% 6.25% 24-29, 34-111
plugins/sanity-plugin-media/src/components/TableRowAsset/TableRowAsset.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/TableRowAsset/index.tsx 3.12% 0% 0% 3.44% 81-121, 126-413
plugins/sanity-plugin-media/src/components/TableRowFolder/TableRowFolder.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/TableRowFolder/index.tsx 10% 0% 0% 10% 21-40, 45-51
plugins/sanity-plugin-media/src/components/TableRowUpload/index.tsx 4.34% 0% 0% 4.54% 19-155
plugins/sanity-plugin-media/src/components/Tag/Tag.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/Tag/index.tsx 7.4% 0% 0% 7.69% 28-32, 44-69, 74-194
plugins/sanity-plugin-media/src/components/UploadDropzone/UploadDropzone.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/components/UploadDropzone/index.tsx 33.33% 22.72% 22.22% 33.33% 23-36, 57-63, 68-76, 83-110
plugins/sanity-plugin-media/src/styled/GlobalStyles/GlobalStyles.css.ts 0% 0% 0% 0%
plugins/sanity-plugin-media/src/styled/GlobalStyles/index.tsx 100% 50% 100% 100%
Generated in workflow #8790 for commit 7f6175b by the Vitest Coverage Report Action

@stipsan
stipsan marked this pull request as ready for review August 6, 2026 08:39
@stipsan
stipsan requested a review from a team as a code owner August 6, 2026 08:39
@stipsan
stipsan requested review from Copilot and pedrobonamin and removed request for a team August 6, 2026 08:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates sanity-plugin-media off styled-components and onto vanilla-extract (zero-runtime CSS), aligning the plugin with the monorepo’s styling approach and build/test tooling for extracted CSS.

Changes:

  • Replaced styled-components usage across UI components with colocated vanilla-extract *.css.ts files plus theme-driven createVar()/assignInlineVars() patterns.
  • Updated plugin build/test config to enable vanilla-extract (tsdown + Vitest plugin + runtime-style disabling).
  • Updated dependencies/exports to support extracted CSS (./bundle.css) and added clsx for class composition.

Reviewed changes

Copilot reviewed 39 out of 41 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Adds clsx to the shared catalog for workspace-wide reuse.
pnpm-lock.yaml Adds new deps for the migration and updates lock resolutions (including sanity@next tag resolution).
plugins/sanity-plugin-media/vitest.config.ts Registers vanilla-extract Vitest plugin and disables runtime styles in tests.
plugins/sanity-plugin-media/tsdown.config.ts Switches build from styled-components mode to vanilla-extract extraction.
plugins/sanity-plugin-media/src/types/sanity-ui.d.ts Removes styled-components theme augmentation (no longer needed).
plugins/sanity-plugin-media/src/styled/GlobalStyles/index.tsx Removes createGlobalStyle-based global styling component.
plugins/sanity-plugin-media/src/styled/GlobalStyles/GlobalStyles.css.ts Replaces prior global styles with vanilla-extract globalStyle() side effects.
plugins/sanity-plugin-media/src/components/UploadDropzone/UploadDropzone.css.ts Adds vanilla-extract styles for the dropzone containers.
plugins/sanity-plugin-media/src/components/UploadDropzone/index.tsx Replaces styled wrappers with vanilla-extract classes for the upload dropzone.
plugins/sanity-plugin-media/src/components/Tag/Tag.css.ts Ports Tag layout/hover visibility styling to vanilla-extract.
plugins/sanity-plugin-media/src/components/Tag/index.tsx Replaces styled wrappers with class-based wrappers using clsx.
plugins/sanity-plugin-media/src/components/TableRowUpload/index.tsx Renames transient styled-components props to regular props for Image.
plugins/sanity-plugin-media/src/components/TableRowFolder/TableRowFolder.css.ts Adds vanilla-extract styles/vars for folder row hover and glyph colors.
plugins/sanity-plugin-media/src/components/TableRowFolder/index.tsx Replaces styled components with wrapper functions + theme-driven vars.
plugins/sanity-plugin-media/src/components/TableRowAsset/TableRowAsset.css.ts Adds vanilla-extract styles/vars for row state, hover, and icon color.
plugins/sanity-plugin-media/src/components/TableRowAsset/index.tsx Replaces styled components with wrapper functions + theme/scheme-driven vars.
plugins/sanity-plugin-media/src/components/TableHeader/TableHeader.css.ts Adds vanilla-extract hover styling for the header context action.
plugins/sanity-plugin-media/src/components/TableHeader/index.tsx Replaces styled header wrapper with a class-based wrapper using vars.
plugins/sanity-plugin-media/src/components/SearchFacets/SearchFacets.css.ts Ports child spacing behavior using globalStyle() + a CSS var.
plugins/sanity-plugin-media/src/components/SearchFacets/index.tsx Replaces styled StackContainer with wrapper + theme-driven spacing var.
plugins/sanity-plugin-media/src/components/SearchFacet/SearchFacet.css.ts Adds vanilla-extract vars for scheme-driven background and radius.
plugins/sanity-plugin-media/src/components/SearchFacet/index.tsx Replaces styled container with wrapper + inline vars for bg/radius.
plugins/sanity-plugin-media/src/components/Image/index.tsx Converts Image from styled-components to a real <img> with VE classes/vars.
plugins/sanity-plugin-media/src/components/Image/Image.css.ts Implements base + checkerboard image styles via vanilla-extract.
plugins/sanity-plugin-media/src/components/FormFieldInputLabel/index.tsx Replaces styled icon color with vanilla-extract + theme-driven var.
plugins/sanity-plugin-media/src/components/FormFieldInputLabel/FormFieldInputLabel.css.ts Adds vanilla-extract var-backed styling for the error icon.
plugins/sanity-plugin-media/src/components/FileIcon/index.tsx Moves forced SVG text styling to VE + theme-driven font var.
plugins/sanity-plugin-media/src/components/FileIcon/FileIcon.css.ts Uses globalStyle() to force react-file-icon SVG text styles.
plugins/sanity-plugin-media/src/components/EditAssetTool/index.tsx Imports global styles via side-effect VE module instead of rendering <GlobalStyle />.
plugins/sanity-plugin-media/src/components/DialogAssetEdit/index.tsx Updates Image usage to new prop names (scheme, showCheckerboard).
plugins/sanity-plugin-media/src/components/CardUpload/index.tsx Replaces styled wrapper with VE class wrapper and updates Image props.
plugins/sanity-plugin-media/src/components/CardUpload/CardUpload.css.ts Adds vanilla-extract styling for upload card wrapper.
plugins/sanity-plugin-media/src/components/CardFolder/index.tsx Replaces styled wrappers/glyph with VE + theme var for yellow.
plugins/sanity-plugin-media/src/components/CardFolder/CardFolder.css.ts Adds vanilla-extract styles/vars for folder card + glyph.
plugins/sanity-plugin-media/src/components/CardAsset/index.tsx Replaces styled card containers/icons with VE + inline vars.
plugins/sanity-plugin-media/src/components/CardAsset/CardAsset.css.ts Adds vanilla-extract styles/vars for picked/updating/hover states.
plugins/sanity-plugin-media/src/components/Browser/index.tsx Imports global styles via side-effect VE module instead of rendering <GlobalStyle />.
plugins/sanity-plugin-media/src/components/AssetGridVirtualized/index.tsx Removes styled wrappers in Virtuoso grid components in favor of VE classes.
plugins/sanity-plugin-media/src/components/AssetGridVirtualized/AssetGridVirtualized.css.ts Adds vanilla-extract grid sizing/layout for the virtualized grid.
plugins/sanity-plugin-media/package.json Drops styled-components deps, adds vanilla-extract deps, and exports ./bundle.css.
.changeset/media-vanilla-extract.md Adds a patch changeset documenting the migration for consumers.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pnpm-lock.yaml Outdated
Comment thread plugins/sanity-plugin-media/src/components/TableRowFolder/index.tsx Outdated
sanity-plugin-media's styling now ships as vanilla-extract .css.ts files.
dev/e2e-studio bundles plugin source directly and didn't have the
@sanity/vanilla-extract-vite-plugin registered (unlike dev/test-studio),
so the deployed e2e studio failed at runtime with "Styles were unable to
be assigned to a file", breaking every e2e test (not just media's).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 43 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (1)

plugins/sanity-plugin-media/src/components/TableRowFolder/index.tsx:40

  • FolderBadge sets its own style prop after spreading ...props, which will drop any style passed by callers (and differs from ContainerGrid, which merges styles). Merging keeps the wrapper component safer and consistent if style is ever provided.

- Scope the GlobalStyles.css.ts rules (custom scrollbar, dark Dialog
  background) under a class toggled on document.body only while the media
  Browser/Edit tool is mounted, restoring the old createGlobalStyle
  mount/unmount lifecycle instead of applying them Studio-wide forever.
  Reference-counted so overlapping instances don't disable each other early.
- Merge the incoming `style` prop in TableRowFolder's FolderBadge wrapper
  instead of overwriting it, matching every other wrapper in the plugin.

Addresses Cursor Bugbot and Copilot review feedback on PR #1847.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 41 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (1)

plugins/sanity-plugin-media/src/components/Image/index.tsx:13

  • Props now derives from ComponentProps<'img'>, which makes src optional. The previous styled-component version required src: string, and keeping it required helps prevent accidentally rendering a broken <img> (especially since this is an internal wrapper that assumes a real asset URL).
type Props = Omit<ComponentProps<'img'>, 'crossOrigin'> & {
  scheme?: ThemeColorSchemeKey
  showCheckerboard?: boolean
}

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7f6175b. Configure here.

backgroundClip: 'padding-box',
})

globalStyle(`.${globalStylesActive} ${selector}::-webkit-scrollbar-thumb:hover`, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scoped global selectors gain duplicate class dots

Medium Severity

Manually prefixing globalStylesActive with a dot produces invalid double-dot selectors after vanilla-extract scopes the registered class. Consequently, the dialog background and custom scrollbar rules never match.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7f6175b. Configure here.

'&::before': {
background: spotYellowVar,
borderRadius: '8px 8px 0 0',
content: '',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Folder tabs lack generated pseudo-elements

Low Severity

Using an empty JavaScript string for CSS content emits no valid generated content, so both folder ::before tabs disappear. Vanilla-extract requires a quoted CSS string such as content: '""'.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7f6175b. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants