Color variant rename codemod

Migrates the palette hues that were renamed in 5.0.0 — grey, raspberry, clementine, plum, and purple no longer exist as color values. The codemod updates the component props that took them, the per-hue style exports, and the data-visualization palette names. It also migrates Badge's three removed colors, which became a variant + color pair.

Value mappings

table
Removed valueReplacement value
greygray
raspberrypink
clementineorange
plumfuchsia
purpleviolet

Primitive token paths have a wider migration table in the palette guide, including green_verdant to verdant while semantic success keeps using green.

Props it rewrites

table
ComponentProps
Badge, PageHeaderLabel, PageHeaderIconTile, StatusIndicatorcolor
TimelineItemcolor, trackColor
CoincolorVariant
ConditionViewerItemiconColorVariant

Ember reads the same args on <PlumaBadge>, <PlumaPageHeaderLabel>, <PlumaPageHeaderIconTile>, <PlumaStatusIndicator>, <PlumaTimelineItem>, <PlumaCoin>, and <PlumaConditionViewerItem>.

Direct and aliased imports from the bare, framework, and per-component Pluma entry points are all recognized. Avatar's autoColor picks its hue internally and has no authored value to migrate.

Badge's removed colors

Badge gained a variant prop (solid, subtle — the default — and outline), and the three colors that used to encode a variant were removed. The same applies to PageHeaderLabel, which renders a Badge and forwards both props:

table
RemovedReplacement
color="accent"variant="solid" color="accent"
color="neutral-outline"variant="outline" color="neutral"
color="accent-outline"variant="outline" color="accent"

The color is rewritten in place and variant is appended, in both frameworks: <PlumaBadge @color="neutral-outline" /> becomes <PlumaBadge @color="neutral" @variant="outline" />, and the same on <PlumaPageHeaderLabel>.

Style exports

Member accesses on the per-component namespaces exported from @customerio/pluma-components/css are renamed when the namespace can be identified — a named import, an alias, or a import * as namespace.

table
NamespaceRenamed keys
PlumaBadgebadgeColorGrey, badgeColorRaspberry, badgeColorClementine, badgeColorPlum, badgeColorPurple
PlumaCoinbackgroundColor*, color*, iconColor*, coinColorVariantBackground*, coinColorVariantForeground* for each of the five hues
PlumaStatusIndicatorstatusIndicatorColorGrey, statusIndicatorColorPurple
PlumaTimelinetimelineItemColorGrey, timelineItemColorRaspberry, timelineItemColorClementine, timelineItemColorPlum, timelineItemColorPurple

Each key takes the new hue name: badgeColorGrey becomes badgeColorGray, coinColorVariantBackgroundPurple becomes coinColorVariantBackgroundViolet, timelineItemColorPlum becomes timelineItemColorFuchsia, and so on.

Some Badge keys read like a hue rename but were removed outright, so they are reported instead: badgeColorAccent, badgeColorNeutralOutline, badgeColorAccentOutline, and every badgeStatusIndicatorColor* key — the status dot now takes its color from the badge's own color variables.

Data visualization

sequential() from @customerio/pluma-components/data-viz lost two palette names: sequential('grey') becomes sequential('gray') and sequential('purple') becomes sequential('violet'). Accesses on the sequentialPalettes record (sequentialPalettes.grey) are renamed the same way. categorical(), semantic(), and diverging() kept their argument vocabulary and are left alone.

What it flags instead of rewriting

Values it can't resolve to a literal are left unchanged and collected in a "needs manual review" summary:

  • A dynamic color (color={statusColor}, @color={{this.color}}, @color={{@color}}). Trace the binding to its source; these values often live in a model, a constant map, or a type union in another file.
  • A branch of a ternary or {{if}} holding one of the removed Badge colors — splitting a value into two props isn't possible from inside an expression.
  • An element that already has a variant alongside a removed color.
  • A computed style-export access (PlumaBadge.classNames[key]).
  • A sequential() call with a non-literal palette name.

Running it

Run it through the Pluma CLI from inside your consuming app. Point the React codemod at source directories (./src, ./app) rather than a workspace root — jscodeshift walks everything it is handed, including node_modules:

pnx @customerio/pluma-cli@latest upgrade --codemod color-variant-rename react ./src
pnx @customerio/pluma-cli@latest upgrade --codemod color-variant-rename ember ./app ./addon

An Ember app wants both: the ember subcommand for its templates, and the react subcommand for the style-export and data-viz renames in its .ts/.js files.

Both codemods support dry runs. The React codemod uses jscodeshift flags, so --dry/-d skips writes and -p prints the transformed output:

pnx @customerio/pluma-cli@latest upgrade --codemod color-variant-rename react --dry -p ./src

The Ember codemod has its own --dry flag:

pnx @customerio/pluma-cli@latest upgrade --codemod color-variant-rename ember --dry ./app

Running it again

A second run over files this codemod migrated is a no-op: the hues it renames to are never names it renames from, and a Badge that already has a variant is treated as migrated. The run ledger checks this on every run, by running the transform over what it just wrote, and for this codemod finds nothing to record.

One value is valid in both vocabularies with different meanings, though: color="accent". In 4.x it was the solid accent badge, and the codemod adds variant="solid"; in 5.0 it is the subtle default. A Badge written against 5.0 on a migrated branch reads exactly like one this codemod has yet to migrate. On a branch that keeps merging pre-5.0 code in, run --baseline over the branch's own code before the merge, so a later run adds variant only to the badges the merge brought in:

pnx @customerio/pluma-cli@latest upgrade --codemod color-variant-rename react --baseline ./src

Order relative to the 3.0.0 codemods

Run this codemod after label-icon-to-coin. That codemod renames LabelIcon's color to Coin's colorVariant and carries the value across verbatim, and LabelIcon is not a component this codemod matches — so running this one first leaves a stale colorVariant="grey" behind, with no warning to tell you about it.

Run this codemod after label-to-badge as well, which the version order already does. label-to-badge converts <Label> to <Badge> and Badge's isOutline/isEmphasized to 3.0 values (grey, neutral-outline, accent, accent-outline), and this codemod migrates those to the 5.0 ones. Running this one first leaves the 3.0 values on the converted badges.

Limitations

  • Only angle-bracket / JSX invocations are handled.
  • Color values assembled at runtime (a lookup table, a model field, a switch) are reported, not resolved.
  • Style-export and sequential() renames inside the JavaScript region of a .gts/.gjs file are not rewritten — the Ember pass only reads templates. They are rare; fix them by hand using the tables above.
  • Type imports are not rewritten. BadgeColor, CoinColorVariant, and friends keep their names, but a hand-written union of hue literals in your own code needs the same rename.