migrating-to-westpac-ui-v1

Installation
SKILL.md

Migrating to @westpac/ui v1.0

Guide for migrating existing applications from pre-1.0 @westpac/ui to v1.0. For fresh installations, use the installing-westpac-ui skill instead.

When to Use

Reference this guide when a user is using and older version of @westpac/ui (check package.json) and wants to upgrade to v1.0. This includes understanding the major breaking changes, running the token codemod, updating Tailwind CSS configuration, and fixing component API changes.

Major Changes from v0.x

v1.0 introduces a new architecture separating UI components from style configuration:

  • @westpac/style-config is a new required package that provides:
    • Tailwind tokens
    • Light/dark theming
    • CSS + W3C design tokens
  • Tailwind CSS upgraded from v3 to v4
  • All color tokens renamed to support multi-brand + dual-theme

Steps

  1. Upgrade Tailwind CSS to v4

Remove your existing tailwind.config.js and follow the Tailwind CSS v4 Upgrade Guide. If the automated upgrade command doesn't work, follow the manual instructions on that page.

  1. Install Updated Dependencies
npm i @westpac/ui @westpac/style-config tailwindcss@4 postcss tailwind-variants@~3.1.1
  1. Update CSS Imports

Replace your existing Tailwind/GEL CSS setup with:

@import 'tailwindcss';
@import '@westpac/style-config/tailwind';

/* Import brand themes you need: */
@import '@westpac/style-config/themes/wbc';
@import '@westpac/style-config/themes/stg';
@import '@westpac/style-config/themes/bom';
@import '@westpac/style-config/themes/bsa';

/* Brand font @font-face declarations (update paths to your font files) */
/* WBC fonts */
@font-face {
  src:
    url('/fonts/Westpac-Bold-v2.007.woff2') format('woff2'),
    url('/fonts/Westpac-Bold-v2.007.woff') format('woff');
  font-family: 'Westpac';
  font-weight: 100 900;
  font-style: normal;
}

/* STG fonts */
@font-face {
  src:
    url('/fonts/dragonbold-bold-webfont.woff2') format('woff2'),
    url('/fonts/dragonbold-bold-webfont.woff') format('woff');
  font-family: 'Dragon Bold';
  font-weight: 100 900;
  font-style: normal;
}
  1. Update Brand Attribute

The brand attribute has changed from data-theme to data-brand:

Before
<html data-theme="wbc"></html>
After
<html data-brand="wbc" data-theme="light"></html>

Note: Dark mode (data-theme="dark") is disabled in this release.

  1. Update ESLint Config (Optional if using @westpac/eslint-config)
// eslint.config.mjs
import eslintConfig from '@westpac/eslint-config/nextjs';
import { defineConfig } from 'eslint/config';

export default defineConfig([
  ...eslintConfig,
  {
    settings: {
      'better-tailwindcss': {
        entryPoint: 'src/globals.css',
      },
    },
  },
]);
  1. Run the Token Codemod
# Install jscodeshift if needed
npm install -g jscodeshift

# Run the codemod against your source files
npx jscodeshift --parser=tsx -t node_modules/@westpac/ui/scripts/codemods/gel-tokens-tailwind-v1.cjs <path>/**/*.tsx

Change References

  1. Token Updates
    • 1.1 Update Examples
    • 1.2 Running the Token Codemod
  2. Import Path Changes
    • 2.1 Style Constants
  3. Component API Changes
    • 3.1 Accordion
    • 3.2 ButtonGroup
    • 3.3 DatePicker
    • 3.4 Compacta
    • 3.5 Repeater
    • 3.6 BottomSheet
    • 3.7 Pictogram
  4. Deprecated Components & APIs
  5. Form Migration Example
  6. Removed Symbols & Logos
    • 6.1 Logos
    • 6.2 Symbols

1. Token Updates

All color tokens have been renamed to support the multi-brand + dual-theme system. This is the largest change and affects all custom Tailwind classes in your codebase.

1.1 Update Examples

Before
<div className="bg-white">
  <div className="bg-primary rounded-full">
    <AccountIcon color="white" />
  </div>
  <h2 className="text-text">
    My title
    <BankIcon color="link" className="ml-2" />
  </h2>
  <p className="text-text">Lorem ipsum dolor sit amet</p>
</div>
After
<div className="bg-background-white">
  <div className="bg-surface-primary rounded-full">
    <AccountIcon color="mono" />
  </div>
  <h2 className="text-text-body">
    My title
    <BankIcon color="primary" className="ml-2" />
  </h2>
  <p className="text-text-body">Lorem ipsum dolor sit amet</p>
</div>

1.2 Running the Token Codemod

A codemod is provided to automate the token migration:

# Install jscodeshift if needed
npm install -g jscodeshift

# Run the codemod against your source files
npx jscodeshift --parser=tsx -t node_modules/@westpac/ui/scripts/codemods/gel-tokens-tailwind-v1.cjs <path>/**/*.tsx

Important notes:

  • The --parser parameter may need to be babel, flow, ts, or tsx depending on your project
  • You may need to run the codemod multiple times depending on project structure
  • For classes that can't be automatically mapped, the codemod injects [REPLACE_TOKEN] — work with your designer to replace these with the correct token
  • As a temporary measure, you can replace unknown tokens with a close match or hex value (e.g., text-blacktext-[#000])

The codemod script source: gel-tokens-tailwind-v1.cjs

2. Import Path Changes

2.1 Style Constants

Before
import { BREAKPOINTS, SPACING_UNIT } from '@westpac/ui/theme-constants';
After
import { BREAKPOINTS, SPACING_UNIT } from '@westpac/style-config/constants';

3. Component API Changes

3.1 Accordion — uses react-aria useDisclose

defaultExpandedKeys now works off the id value of AccordionItem:

Before
<Accordion defaultExpandedKeys={['FoR']}>
  <AccordionItem title="Founding of Rome">Content</AccordionItem>
</Accordion>
After
<Accordion defaultExpandedKeys={['FoR']}>
  <AccordionItem id="FoR" title="Founding of Rome">
    Content
  </AccordionItem>
</Accordion>

3.2 ButtonGroup — compositional API

Before
<ButtonGroup
  value={value}
  buttons={[
    { value: 'Left', label: 'Left' },
    { value: 'Middle', label: 'Middle' },
    { value: 'Right', label: 'Right' },
  ]}
/>
After
<ButtonGroup selectedKeys={value}>
  <ButtonGroupButton id="Left">Left</ButtonGroupButton>
  <ButtonGroupButton id="Middle">Middle</ButtonGroupButton>
  <ButtonGroupButton id="Right">Right</ButtonGroupButton>
</ButtonGroup>

3.3 DatePicker — react-aria based

disableDates prop replaced with isDateUnavailable function:

Before
<DatePicker disableDates={['2023-10-10']} />
After
import { DateValue } from '@internationalized/date';

const disableDates = ['2023-10-20'];
const isDateUnavailable = (date: DateValue) => disableDates.some(d => d.toString() === date.toString());

<DatePicker isDateUnavailable={isDateUnavailable} />;

3.4 Compacta — simplified API

Moved from render-prop pattern to compositional children with CompactaItem:

Before
<Compacta initialCompactas={[{ id: '1234', title: { primary: 'test' } }, {}]} onAdd={() => {}} onRemove={() => {}}>
  {({ id, setPrimaryTitle }) => (
    <Form>
      <FormGroup>
        <Label htmlFor={`primary-${id}`}>Primary</Label>
        <Input name={`primary-${id}`} onChange={e => setPrimaryTitle(e.target.value)} />
      </FormGroup>
    </Form>
  )}
</Compacta>
After
<Compacta onAdd={handleAdd}>
  {items.map((item, index) => (
    <CompactaItem
      key={index}
      title={{ primary: item.primary, secondary: item.secondary, tertiary: item.tertiary }}
      onRemove={() => removeItem(index)}
    >
      <Field label="Primary" hintMessage="Primary title text">
        <Input {...register(`items.${index}.primary`)} />
      </Field>
    </CompactaItem>
  ))}
</Compacta>

3.5 Repeater — aligned with Compacta

Same pattern change as Compacta — uses RepeaterItem children:

Before
<Repeater>
  <FormGroup>
    <FormLabel htmlFor={`input${id}`}>Label</FormLabel>
    <Input name={`input${id}`} />
  </FormGroup>
</Repeater>
After
<Repeater onAdd={handleAdd}>
  {items.map((item, index) => (
    <RepeaterItem key={index} title={{ primary: item.primary }} onRemove={() => removeItem(index)}>
      <Field label="Label">
        <Input {...register(`items.${index}.label`)} />
      </Field>
    </RepeaterItem>
  ))}
</Repeater>

3.6 BottomSheet — isDismissable defaults to false

Now aligns with Modal behaviour:

Before
<BottomSheet title="Title">Content</BottomSheet>
After
<BottomSheet title="Title" isDismissable>
  Content
</BottomSheet>

3.7 Pictogram — mode prop values renamed

Before
type PictogramMode = 'dark' | 'light' | 'duo';
<Pictogram mode="dark" />;
After
type PictogramMode = 'base' | 'mono' | 'duo';
<Pictogram mode="base" />;

4. Deprecated Components & APIs

Deprecated Replacement / Notes
ButtonDropdown Replaced by Dropdown
Form, FormGroup, FormChitChat, FormSection Removed — use <form> with className="flex flex-col gap-4" and Field components
Pagination pages={[]} Removed — use totalPages={number} instead

5. Form Migration Example

Before
<Form spacing="large" onSubmit={handleSubmit}>
  <FormGroup>
    <InputGroup size="large" label="Given name">
      <Input {...register('givenName')} />
    </InputGroup>
  </FormGroup>
  <FormGroup>
    <Field label="Comments:">
      <Textarea size="large" {...register('comment')} />
    </Field>
  </FormGroup>
</Form>
After
<form className="flex flex-col gap-4" onSubmit={handleSubmit}>
  <Field label="Given name">
    <InputGroup size="large">
      <Input {...register('givenName')} />
    </InputGroup>
  </Field>
  <Field label="Comments:">
    <Textarea size="large" {...register('comment')} />
  </Field>
</form>

6. Removed Symbols & Logos

The following have been removed. Contact the GEL design team if you believe a removal was incorrect.

6.1 Logos

  • BTPanormaMultibrandLargeLogo
  • BTPanormaMultibrandSmallLogo
  • RedAvatarCircleLogo
  • RedAvatarCircleReverseLogo
  • RedAvatarLogo

6.2 Symbols

  • GooglePlusSymbol
  • MastercardAcceptedSymbol
  • MastercardHorizontalSymbol
  • MicrosoftStoreSymbol
  • PayToBlackSymbol
  • PayToDarkGreySymbol
  • PayToLightGreySymbol
  • PayToWhiteSymbol
  • PayToWordmarkBlackSymbol
  • PayToWordmarkDarkGreySymbol
  • PayToWordmarkLightGreySymbol
  • PayToWordmarkWhiteSymbol
  • SlackSymbol
  • TwitterSymbol
  • VisaSymbol
  • XMarkInverseSymbol
  • XMarkSymbol
  • YammerSymbol
Related skills
Installs
6
GitHub Stars
20
First Seen
Apr 6, 2026