Migrates the color values that were renamed in 5.0.0: the Text and Icon color
props, the iconColor prop of the components that render an icon in a slot of
their own, the Box backgroundColor sprinkle, and the Coin borderColor prop,
along with the matching style-export keys.
The neutral text roles collapsed to a single primary/secondary pair and the
surface and scope names replaced the old bold/subtle/minimal shading, so
the values that only differed by shade are gone. Emphasis is a weight now
(weight="semibold", or a product-*-active text style), not a darker gray.
color| Removed value | Replacement | Notes |
|---|---|---|
base | primary | Also the value an omitted color resolves to. |
bold | primary | Emphasis is a weight now, not a darker gray. |
active | primary | Selected and current states use weight or another cue. |
subtle | secondary | |
minimal | secondary | minimal was near-invisible; secondary is a normal reading gray. Look over anywhere that relied on the old faintness. |
placeholder | secondary |
accent, caution, critical, information, success, invert, white,
black, code, disabled, and inherit are unchanged.
color| Removed value | Replacement |
|---|---|
base | primary |
bold | primary |
button | primary |
navigation | primary |
subtle | secondary |
minimal | secondary |
accent, caution, critical, feature, information, success, invert,
white, disabled, and inherit are unchanged. An Icon with no color still
takes the surrounding text color.
Text and Icon share the removed spellings but not every replacement — button
and navigation exist only on Icon — so the codemod matches on the component,
not the value.
iconColorCombobox, TextField, DropdownMenuItem, DropdownMenuCheckboxItem, the
Tabs and TabNav tab, and SelectPaletteCategory color the icon they render
in a slot of their own through an iconColor prop. It takes the Icon union, so
it uses the Icon table above:
| Removed value | Replacement |
|---|---|
base | primary |
bold | primary |
button | primary |
navigation | primary |
subtle | secondary |
minimal | secondary |
Only the prop is rewritten. The same value inside an options, tabs, or
categories array is data rather than an attribute and is left alone — see
Limitations.
Button's unsafe_iconColor is a different union and is untouched.
backgroundColor| Removed value | Replacement | Notes |
|---|---|---|
base | content | The plane content sits on. The new base is the page behind everything, so leaving this one alone would turn a white region gray — and rewriting one that was already migrated by hand turns it gray instead. Still valid in v5, so the rewrite is reported rather than silent: check each one. |
subtle | overlay-tint | Reported. A faint region tint, one step off the surface it sits on; use neutral-subtle if this was a filled chip. On Avatar, Badge, Coin, StatusIndicator and Tag, whose backgroundColor fills a chip, subtle becomes neutral-subtle silently. |
active | neutral-subtle | Was a pressed-state layer, a step stronger than subtle; a static background is the neutral subtle fill. |
overlay-active | neutral-subtle | Same, and reported: a pressed layer has no Box value, so if the element shows a pressed or selected state, set backgroundColor: layerPressed() from @customerio/pluma-components/css/utils in its styles instead. |
minimal | overlay-tint | Reported. A fainter tint than subtle that now shares its tint; use neutral-subtle if this was a filled chip, or content if the element should sit flush with the panel. On those chip components it becomes neutral-subtle, still reported. |
navigation | base | Navigation sits on the page plane. |
overlay | floating | |
invert | inverse | Reported: 4.x invert was a dark teal and inverse is the neutral plane that flips with the theme. If the teal was intended, set backgroundColor: fillScope('action', 'solid') from @customerio/pluma-components/css/utils in its styles instead. |
invert-bold | inverse | Same. |
invert-subtle | neutral | The solid neutral scope. Reported: 4.x invert-subtle was teal in dark. |
accent-minimal | accent-subtle | |
caution-bold | caution | |
caution-minimal | caution-subtle | |
critical-bold | critical | |
critical-minimal | critical-subtle | |
information-bold | information | |
information-minimal | information-subtle | |
success-bold | success | |
success-minimal | success-subtle | |
bold | removed | No replacement — the attribute is deleted and reported. |
accent, caution, critical, information, success and their -subtle
twins are unchanged; neutral, neutral-subtle, feature, feature-subtle,
block, content, floating, overlay-tint, recessed, and inverse are new.
A solid scope background sets the default text to that scope's solid text, a
-subtle background to its subtle text, and inverse to the inverted text, so
a Text inside one of these usually needs no color of its own.
borderColor| Removed value | Replacement |
|---|---|
base | neutral-subtle |
minimal | neutral-subtle |
bold | neutral |
active | neutral |
accent-minimal | accent-subtle |
caution-minimal | caution-subtle |
critical-minimal | critical-subtle |
information-minimal | information-subtle |
success-minimal | success-subtle |
invert | removed — the attribute is deleted and reported; a Coin with no borderColor paints no border. |
PlumaCoin.defaultVars from @customerio/pluma-components/css/components:
| Removed key | Replacement |
|---|---|
borderColorBase | borderColorNeutralSubtle |
borderColorMinimal | borderColorNeutralSubtle |
borderColorBold | borderColorNeutral |
borderColorActive | borderColorNeutral |
borderColorAccentMinimal | borderColorAccentSubtle |
borderColorCautionMinimal | borderColorCautionSubtle |
borderColorCriticalMinimal | borderColorCriticalSubtle |
borderColorInformationMinimal | borderColorInformationSubtle |
borderColorSuccessMinimal | borderColorSuccessSubtle |
borderColorInvert | removed — reported, not rewritten. |
Both PlumaCoin.defaultVars.borderColorBase and the destructured
const { defaultVars } = PlumaCoin form are handled, as is a namespace import
(import * as pluma from '…/css/components').
backgroundColor is rewritten on any component imported from a Pluma package —
it is a Box sprinkle every one of them forwards. borderColor is scoped to
Coin.
color is rewritten only where it is the Text or Icon union:
Text color — Avatar, CardTitle, Code, Coin,
ConditionViewerDetail, ConditionViewerTerm, DescriptionList,
DescriptionListDetails, DescriptionListTerm, Emphasis,
EmptyStateDescription, EmptyStateFooter, EmptyStateTitle,
FormattedDateTime, FormattedRelativeTime, Heading, KeyboardShortcut,
List, ListItem, PageHeaderDescription, PageHeaderTitle,
PanelDescription, PanelTitle, Paragraph, SkeletonText, Spinner,
Strong, Text, TextLabel, and Truncated.
Icon color — Icon, PageHeaderIcon, and SelectMenuOptionIcon.
iconColor — Combobox, DropdownMenuCheckboxItem, DropdownMenuItem,
SelectPaletteCategory, TabNavTab, TabsTab, and TextField.
The Ember tags are the same names with a Pluma prefix
(<PlumaText @color="subtle">).
Deliberately left alone, because their color prop is a different union: Badge,
StatusIndicator, TimelineItem, PageHeaderIconTile, Coin's
colorVariant, ConditionViewerItem's iconColorVariant, and Button's
unsafe_iconColor. Paragraph's variant prop is unchanged in 5.0.0.
Values it can't resolve to a literal are left as they are and collected in a "needs manual review" summary:
color, iconColor, backgroundColor, or borderColor coming from an
identifier, this.x, an @arg, a helper, or a call. Trace the binding —
these often live in a model, constant, or type union in another file.color={isMuted ? 'subtle' : fallback}): the
literal branches are migrated and the rest is reported.backgroundColor="bold", Coin borderColor="invert", and
defaultVars.borderColorInvert — because deleting a background or a ring is
a visible change worth a second look.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:
Run the react command over your vanilla-extract .css.ts files as well,
including in an Ember app — that is where the style-export keys live. The
ember command walks .gts, .gjs, and .hbs.
Both support dry runs. The React codemod uses jscodeshift flags, so --dry/-d
skips writes and -p prints the transformed output; the Ember codemod has its
own --dry:
A second run over files it migrated is a no-op. Every value it renames to but
one is new in v5; the exception is backgroundColor="navigation", which becomes
base, itself a 4.x value. The
run ledger runs the transform over what
it just wrote, records each base it wrote for navigation in
.pluma/codemod-ledger.json, and a later run leaves those sites alone.
One value does not survive a partly hand-migrated file, though.
backgroundColor="base" becomes "content", and base is still a valid v5
value (the page plane), so a base that someone already put there deliberately
looks exactly like one this codemod has yet to migrate and will be rewritten.
Every one of those rewrites is reported for that reason — work through them
before committing.
That is also the case to reach for --baseline on a migrated branch that keeps
merging pre-5.0 code in: before the merge, run it over the branch's own code to
record every base already there as intended, and a later run migrates only
the base the merge brought in:
There is no ordering constraint. The two codemods in this batch that emit new
elements emit props this one doesn't read:
label-icon-to-coin
produces <Coin colorVariant> around a bare <Icon>, and
label-to-badge
produces <Badge color> with hue values that share no spelling with anything in
the tables above. Both orders are pinned in the tests.
iconColor is only rewritten where it is an attribute. The same key inside a
data array — a Select or Combobox option, a Select palette category, a Tabs
or TabNav tabs entry, a Select option group — is an object property the
transform doesn't reach, in either framework. Rename those by hand using the
Icon table above.DropdownMenuItem's trailingIconColor takes the same Icon union and is not
covered.const { borderColorBase } = PlumaCoin.defaultVars).pluma-text-color-base became
pluma-text-color-primary). Anything matching on those strings — a test
selector, a hand-written stylesheet — is a manual fix.