diff --git a/.prettierignore b/.prettierignore
index 6ab7f3a36..f6be79b9f 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,2 +1,2 @@
package-lock.json
-src/codemods/__testfixtures__/
\ No newline at end of file
+src/codemods/__testfixtures__/
diff --git a/docs/migrating.storybook.mdx b/docs/migrating.storybook.mdx
index c386d4ff1..7a499b3c4 100644
--- a/docs/migrating.storybook.mdx
+++ b/docs/migrating.storybook.mdx
@@ -53,6 +53,20 @@ This won't change how your icons look in any way since we already exported the d
npx jscodeshift -t node_modules/@freenow/wave/lib/cjs/codemods/deprecated-icons.js path/to/src
```
+### Inverted property deprecation
+
+With the introduction of our new dark theme in Wave we've removed the `inverted` property that several components had. In case you want a specific component to have
+the styling of the opposite theme you need to wrap it with the `InvertedColorScheme` component.
+
+The components that supported the `inverted` prop are: `Input`, `Password`, `Textarea`, `Button`, `Select`, `SelectList`, `PhoneInput`, `Datepicker`, `Tooltip`, `Text`, `Logo` and `Breadcrumbs`.
+
+```bash
+npx jscodeshift -t node_modules/@freenow/wave/lib/cjs/codemods/inverted-to-wrapper.js path/to/src
+```
+
+Disclaimer: This codemod wraps every Wave component that is using the `inverted` property with `InvertedColorScheme`, this could lead to a decline in performance in case the
+DOM gets too deep. Ideally when you have Wave components using the `inverted` prop with a common parent you wrap the parent instead of each individual component.
+
### Text weak property
The `weak` property was the initial way to indicate secondary information in a `Text` component, it has been deprecated for a while in favour of
diff --git a/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-false.input.tsx b/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-false.input.tsx
new file mode 100644
index 000000000..1fa088856
--- /dev/null
+++ b/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-false.input.tsx
@@ -0,0 +1,13 @@
+import { Button } from '@freenow/wave';
+
+interface Props {
+ label: string;
+ disabled?: boolean;
+ onClick: () => void;
+}
+
+export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => (
+
+);
diff --git a/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-false.output.tsx b/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-false.output.tsx
new file mode 100644
index 000000000..bb65d147a
--- /dev/null
+++ b/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-false.output.tsx
@@ -0,0 +1,13 @@
+import { Button } from '@freenow/wave';
+
+interface Props {
+ label: string;
+ disabled?: boolean;
+ onClick: () => void;
+}
+
+export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => (
+
+);
diff --git a/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-true.input.tsx b/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-true.input.tsx
new file mode 100644
index 000000000..2a4efe95a
--- /dev/null
+++ b/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-true.input.tsx
@@ -0,0 +1,13 @@
+import { Button } from '@freenow/wave';
+
+interface Props {
+ label: string;
+ disabled?: boolean;
+ onClick: () => void;
+}
+
+export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => (
+
+);
diff --git a/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-true.output.tsx b/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-true.output.tsx
new file mode 100644
index 000000000..ba0e6cfdb
--- /dev/null
+++ b/src/codemods/__testfixtures__/inverted-to-wrapper/explicit-boolean-true.output.tsx
@@ -0,0 +1,15 @@
+import { Button, InvertedColorScheme } from '@freenow/wave';
+
+interface Props {
+ label: string;
+ disabled?: boolean;
+ onClick: () => void;
+}
+
+export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => (
+
+ ()
+
+);
diff --git a/src/codemods/__testfixtures__/inverted-to-wrapper/individual-wrap-siblings.input.tsx b/src/codemods/__testfixtures__/inverted-to-wrapper/individual-wrap-siblings.input.tsx
new file mode 100644
index 000000000..964ea0988
--- /dev/null
+++ b/src/codemods/__testfixtures__/inverted-to-wrapper/individual-wrap-siblings.input.tsx
@@ -0,0 +1,18 @@
+import { Button, Box } from '@freenow/wave';
+
+interface Props {
+ label: string;
+ disabled?: boolean;
+ onClick: () => void;
+}
+
+export const ActionItem = ({ label, onClick }: Props): JSX.Element => (
+
+
+
+
+);
diff --git a/src/codemods/__testfixtures__/inverted-to-wrapper/individual-wrap-siblings.output.tsx b/src/codemods/__testfixtures__/inverted-to-wrapper/individual-wrap-siblings.output.tsx
new file mode 100644
index 000000000..c1d8a5cb8
--- /dev/null
+++ b/src/codemods/__testfixtures__/inverted-to-wrapper/individual-wrap-siblings.output.tsx
@@ -0,0 +1,22 @@
+import { Button, Box, InvertedColorScheme } from '@freenow/wave';
+
+interface Props {
+ label: string;
+ disabled?: boolean;
+ onClick: () => void;
+}
+
+export const ActionItem = ({ label, onClick }: Props): JSX.Element => (
+
+
+
+
+
+
+
+
+);
diff --git a/src/codemods/__testfixtures__/inverted-to-wrapper/local-rename.input.tsx b/src/codemods/__testfixtures__/inverted-to-wrapper/local-rename.input.tsx
new file mode 100644
index 000000000..d21af799f
--- /dev/null
+++ b/src/codemods/__testfixtures__/inverted-to-wrapper/local-rename.input.tsx
@@ -0,0 +1,13 @@
+import { Button } from '@freenow/wave';
+
+interface Props {
+ label: string;
+ disabled?: boolean;
+ onClick: () => void;
+}
+
+export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => (
+
+);
diff --git a/src/codemods/__testfixtures__/inverted-to-wrapper/local-rename.output.tsx b/src/codemods/__testfixtures__/inverted-to-wrapper/local-rename.output.tsx
new file mode 100644
index 000000000..ba0e6cfdb
--- /dev/null
+++ b/src/codemods/__testfixtures__/inverted-to-wrapper/local-rename.output.tsx
@@ -0,0 +1,15 @@
+import { Button, InvertedColorScheme } from '@freenow/wave';
+
+interface Props {
+ label: string;
+ disabled?: boolean;
+ onClick: () => void;
+}
+
+export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => (
+
+ ()
+
+);
diff --git a/src/codemods/__testfixtures__/inverted-to-wrapper/styled-rename.input.tsx b/src/codemods/__testfixtures__/inverted-to-wrapper/styled-rename.input.tsx
new file mode 100644
index 000000000..019f79ab1
--- /dev/null
+++ b/src/codemods/__testfixtures__/inverted-to-wrapper/styled-rename.input.tsx
@@ -0,0 +1,25 @@
+import { Button, Colors, Spaces } from '@freenow/wave';
+import styled from 'styled-components';
+
+interface Props {
+ label: string;
+ disabled?: boolean;
+ onClick: () => void;
+}
+
+const Action = styled(Button)`
+ justify-content: flex-start;
+
+ color: ${Colors.AUTHENTIC_BLUE_900};
+ font-weight: normal;
+ line-height: 1.43;
+
+ border-radius: 0;
+ padding: ${Spaces[2]};
+`;
+
+export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => (
+
+ {label}
+
+);
diff --git a/src/codemods/__testfixtures__/inverted-to-wrapper/styled-rename.output.tsx b/src/codemods/__testfixtures__/inverted-to-wrapper/styled-rename.output.tsx
new file mode 100644
index 000000000..1067ad773
--- /dev/null
+++ b/src/codemods/__testfixtures__/inverted-to-wrapper/styled-rename.output.tsx
@@ -0,0 +1,27 @@
+import { Button, Colors, Spaces, InvertedColorScheme } from '@freenow/wave';
+import styled from 'styled-components';
+
+interface Props {
+ label: string;
+ disabled?: boolean;
+ onClick: () => void;
+}
+
+const Action = styled(Button)`
+ justify-content: flex-start;
+
+ color: ${Colors.AUTHENTIC_BLUE_900};
+ font-weight: normal;
+ line-height: 1.43;
+
+ border-radius: 0;
+ padding: ${Spaces[2]};
+`;
+
+export const ActionItem = ({ label, onClick, disabled = false }: Props): JSX.Element => (
+
+ (
+ {label}
+ )
+
+);
diff --git a/src/codemods/__tests__/inverted-to-wrapper-test.ts b/src/codemods/__tests__/inverted-to-wrapper-test.ts
new file mode 100644
index 000000000..5a898d59d
--- /dev/null
+++ b/src/codemods/__tests__/inverted-to-wrapper-test.ts
@@ -0,0 +1,18 @@
+jest.autoMockOff();
+const { defineTest } = require('jscodeshift/dist/testUtils');
+
+const tests = [
+ 'local-rename',
+ 'explicit-boolean-false',
+ 'explicit-boolean-true',
+ 'styled-rename',
+ 'individual-wrap-siblings'
+];
+
+describe('inverted-to-wrapper', () => {
+ tests.forEach(test =>
+ defineTest(__dirname, 'inverted-to-wrapper', { quote: 'single' }, `inverted-to-wrapper/${test}`, {
+ parser: 'tsx'
+ })
+ );
+});
diff --git a/src/codemods/inverted-to-wrapper.ts b/src/codemods/inverted-to-wrapper.ts
new file mode 100644
index 000000000..0ca2bd206
--- /dev/null
+++ b/src/codemods/inverted-to-wrapper.ts
@@ -0,0 +1,121 @@
+import { API, FileInfo, ImportDeclaration, JSXAttribute, VariableDeclarator } from 'jscodeshift';
+import { Options } from 'recast';
+
+const ComponentNamesWithInvertedProp = [
+ 'Input',
+ 'Password',
+ 'Textarea',
+ 'Button',
+ 'Select',
+ 'SelectList',
+ 'PhoneInput',
+ 'Datepicker',
+ 'Tooltip',
+ 'Text',
+ 'Breadcrumbs',
+ 'Logo'
+];
+
+const WRAPPER_COMPONENT_NAME = 'InvertedColorScheme';
+
+export default (file: FileInfo, api: API, options: Options) => {
+ const j = api.jscodeshift;
+ const ast = j(file.source);
+ const printOptions = options ?? { quote: 'single' };
+
+ const localComponentNames: string[] = [];
+
+ // Find @freenow/wave imports
+ const waveImports = ast.find(j.ImportDeclaration, {
+ source: {
+ value: '@freenow/wave'
+ }
+ });
+
+ // Find component named imports in @freenow/wave imports that potentially have an inverted prop
+ const componentImports = waveImports
+ .find(j.ImportSpecifier)
+ .filter(path => ComponentNamesWithInvertedProp.includes(path.node.imported.name));
+
+ // Get the local icons import names
+ componentImports.forEach(spec => {
+ if (spec.node.local?.name) localComponentNames.push(spec.node.local.name);
+ });
+
+ // Find declarations of styled components that use a component which has the inverted prop
+ const styledExpressions = ast.find(j.TaggedTemplateExpression, {
+ tag: {
+ arguments: ([argument]) =>
+ argument?.type === 'Identifier' && ComponentNamesWithInvertedProp.includes(argument.name)
+ }
+ });
+
+ styledExpressions.forEach(ex => {
+ if (ex.parent?.node && ex.parent.node.type === 'VariableDeclarator') {
+ const styledDeclaration: VariableDeclarator = ex.parent.node;
+ // Mark the name of the declared styled component as a local component which can have the inverted prop
+ if (styledDeclaration.id.type === 'Identifier') localComponentNames.push(styledDeclaration.id.name);
+ }
+ });
+
+ // Find usages of the components
+ const jsxComponents = ast.find(j.JSXElement, {
+ openingElement: {
+ name: {
+ name: name => localComponentNames.includes(name)
+ }
+ }
+ });
+
+ let shouldAddWrapperImport = false;
+
+ // Iterate over jsx components
+ jsxComponents.forEach(el => {
+ // Find inverted props
+ const invertedProps = j(el).find(j.JSXAttribute, { name: name => name.name === 'inverted' });
+
+ if (invertedProps.size() !== 1) return;
+
+ let shouldWrap = false;
+ const invertedProp: JSXAttribute = invertedProps.get(0).node;
+
+ // console.log(invertedProp)
+ // In case the prop has a value (`inverted={true}` or `inverted={false}`) set shouldWrap based on the value
+ if (
+ invertedProp.value &&
+ invertedProp.value.type === 'JSXExpressionContainer' &&
+ invertedProp.value.expression.type === 'BooleanLiteral'
+ ) {
+ shouldWrap = invertedProp.value.expression.value;
+ } else {
+ // In case the prop has an implicit `true` value set shouldWrap to `true`
+ shouldWrap = true;
+ }
+
+ // Remove the inverted prop
+ invertedProps.at(0).remove();
+
+ // In case `inverted` was `true` wrap the element with the wrapper component
+ if (shouldWrap) {
+ shouldAddWrapperImport = true;
+
+ // Build wrapper component with the current element as it's children
+ const WrapperComponent = j.jsxElement(
+ j.jsxOpeningElement(j.jsxIdentifier(WRAPPER_COMPONENT_NAME)),
+ j.jsxClosingElement(j.jsxIdentifier(WRAPPER_COMPONENT_NAME)),
+ [j.jsxText('\n ', '\n '), el.node, j.jsxText('\n ', '\n ')]
+ );
+
+ // Replace element with wrapper
+ el.replace(WrapperComponent);
+ }
+ });
+
+ // Add wrapper import
+ if (shouldAddWrapperImport && waveImports.size() > 0) {
+ const importDeclaration: ImportDeclaration = waveImports.get(0).node;
+ importDeclaration.specifiers.push(j.importSpecifier(j.identifier(WRAPPER_COMPONENT_NAME)));
+ }
+
+ return ast.toSource(printOptions);
+};