Figma plugin for migrating Core UI Kit files from color variants to variable modes.
The plugin opens on a landing screen with two workflows.
Run in the library file. A single state-gated screen checks the variable structure and shows what's needed:
-
Needs preparation — offers a Prepare action that renames the
Main colorcollection toColorand strips thecolor/main/prefix off its variables. -
Needs tokens — the structure is correct but the
Colorcollection is missing the new color modes (info,warning,danger,success); the user is told to regenerate and publish tokens first. -
Not a library file — none of the known color collections exist.
-
Ready — shows the Run migration box. The user must pick which color mode replaces the old
supportvariants, then one run does everything over the whole file as a single undo step:- removes
neutral/support(and any semantic) color variants from component sets and cleans up variant names; - swaps every affected instance — including instances nested inside other instances — to the matching current variant and sets the right color mode;
- rebinds loose
Support colorbindings that live directly on layers (frames, text, etc.).
Progress is reported in phases (checking → components → instances).
AlertandValidationMessageare left untouched (they keep their color variants and hardcoded severity colors). ForTableColumncell/header subcomponents the swap runs but theneutralcolor mode is intentionally left unset. - removes
Run in a sketch file after the library has been migrated and republished. Updates instances stuck on the pre-migration library version and sets the appropriate color mode, and also rebinds loose Support color bindings. Lets you scope the work to the selection, current page, or whole file, and (like the library run) asks for the replacement mode for old support.
- Download this repository.
- In Figma, go to Plugins -> Development -> Import plugin from manifest...
- Select
manifest.jsonfrom this folder. - The plugin is now available in Figma under Plugins.
Each migration (color, and future ones such as typography) is a self-contained module that plugs into a shared harness.
src/
core/ types + harness (messaging, registry, message router)
migrations/
color/ the color migration (operations exposed as a MigrationModule)
main.ts startPlugin([colorMigration])
ui/
core/ bridge (messaging) + shell (migration picker, nav, busy overlay)
migrations/
color/ the color UI controller
main.ts initBridge(); initShell([createColorController()])
Backend messages use one generic envelope, { type: 'run', domain, operation, args } (plus a shared focus-node). The harness router dispatches run to the matching module's operation; results come back as an OperationResultPayload tagged with the domain. The UI shell renders the picker from the registered controllers and routes results to the active one.
The first screen is a picker that lists one button per registered migration. It is currently hidden: while only one migration (color) is registered, initShell skips the picker and opens that migration directly, and hides the "back to migrations" button (which would otherwise lead to an empty picker). The picker code is intentionally kept, not removed — as soon as a second migration is registered in ui/main.ts, controllers.length > 1 and the picker (and back button) reappear automatically with no further changes. See ui/core/shell.ts.
- Backend: add
src/migrations/<name>/index.tsexporting aMigrationModule(id,title,description,operations), and register it instartPlugin([...])insrc/main.ts. - UI: add
ui/migrations/<name>/controller.tsexposing the controller contract (id,title,description,icon,rootId,enter,onProgress,onResult) plus its markup inui/index.html(amigration-rootwith adata-go-to-migrationsback button), and register it inui/main.ts.
No central union or switch needs editing — both sides resolve migrations through their registries.