Skip to content

Latest commit

Β 

History

History
1371 lines (944 loc) Β· 82 KB

MIGRATION.md

File metadata and controls

1371 lines (944 loc) Β· 82 KB

Migration

πŸ€– Automated migration

Some of the changes in this guide can be automated using the ESLint and Stylelint plugins. Changes that can be automated are marked with a robot emoji (πŸ€–) and the name of the rule (e.g. no-deprecated-props)

We encourage you to enable and apply the rules incrementally and review the changes before continuing. The rules don't cover all edge cases, so further manual changes might be necessary. For example, the ESLint rules only analyze one file at a time, so if a Circuit UI component is wrapped in a styled component in one file and used in another, ESLint won't be able to update its props.

Prior to v5, codemods were implemented using jscodeshift.

From v8.x to v9

Circuit UI v9 introduces a new typeface, more flexible typography APIs, and stable input components for colors, dates, and phone numbers. For a complete list of changes, refer to the changelog.

Prerequisites

Circuit UI now requires at minimum Node.js v20. Note that Node 18 reached its end-of-life in October 2024.

We strongly recommend upgrading to Foundry v8.3+ which adds support for the Circuit UI ESLint plugin's new package scope and prevents dependency conflicts in the shared @typescript-eslint/* dependencies. If you're unable to upgrade, manually add the Circuit UI ESLint plugin to your ESLint config and enable the relevant rules.

Renamed package scope

The packages have moved from the @sumup to the @sumup-oss scope to avoid conflicts with private packages. To get started, remove the old design system packages, then install the new ones:

npm uninstall @sumup/circuit-ui @sumup/design-tokens @sumup/icons @sumup/intl
npm install @sumup-oss/circuit-ui @sumup-oss/design-tokens @sumup-oss/icons @sumup-oss/intl temporal-polyfill

Note that temporal-polyfill is a new required peer dependency and that the @sumup-oss/intl peer dependency has been upgraded to v3. If your app also depends on @sumup-oss/intl (previously called @sumup/intl), you need to upgrade it as well. Refer to its changelog for migration instructions.

Follow the same process to upgrade any linter plugins your app is using:

# ESLint
npm uninstall @sumup/eslint-plugin-circuit-ui
npm install @sumup-oss/eslint-plugin-circuit-ui
# Stylelint
npm uninstall @sumup/stylelint-plugin-circuit-ui
npm install @sumup-oss/stylelint-plugin-circuit-ui

Update any static and dynamic imports to the new package scope (πŸ€– renamed-package-scope). The ESLint rule might not catch all occurrences of the old package names, so manually search for and fix any left-overs after applying the ESLint rule. For example:

-import { Button, type ButtonProps } from '@sumup/circuit-ui';
+import { Button, type ButtonProps } from '@sumup-oss/circuit-ui';

-jest.mock('@sumup-oss/circuit-ui', () => ({
-  ...jest.requireActual<typeof import('@sumup-oss/circuit-ui')>(
-    '@sumup-oss/circuit-ui',
-  ),
-}));
+jest.mock('@sumup-oss/circuit-ui', () => ({
+  ...jest.requireActual<typeof import('@sumup-oss/circuit-ui')>(
+    '@sumup-oss/circuit-ui',
+  ),
+}));

New typeface

The default typeface has changed from Aktiv Grotesk to Inter, a variable font. Variable fonts combine a continuous range of weights and other "axes" into a single file. This speeds up page load times and enables more creative freedom. Inter is a close match to Aktiv Grotesk, so the change should be seamless.

Circuit UI no longer loads the fonts by default. Instead, import the stylesheet that contains the font face declarations globally in your application, such as in a global layout file:

import '@sumup-oss/design-tokens/fonts.css';

To speed up the loading of the fonts, add preload links to the global <head> element of your application. Choose which subsets to preload based on the languages your app supports. The available subsets are latin, latin-ext, cyrillic, cyrillic-ext, greek, greek-ext, and vietnamese.

<link
  rel="preload"
  href="https://static.sumup.com/fonts/Inter/Inter-normal-latin.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>

Typography APIs

The typography components have been redesigned to improve visual hierarchy, ensure consistency across platforms and provide more flexible APIs for developers. The changes are fully backward compatible and can be adopted gradually.

Consistent names

The Title component has been renamed to Display for consistency with other platforms. The SubHeadline component has been deprecated in favor of the Headline component in size s. The BodyLarge component has been deprecated in favor of the Body component in size l (πŸ€– no-deprecated-components).

Flexible props

The Body component's variant prop has been split into individual color, weight and decoration props. Use the color prop instead of the alert, confirm and subtle variants, use the weight prop instead of the highlight variant, and use custom CSS to replicate the quote variant (πŸ€– no-renamed-props). The new decoration prop makes it easier to apply italic and strikethrough styles.

The sizes of the Display (formerly Title), Headline, and Body components have been consolidated and renamed to enforce greater visual hierarchy. Here's how the old size names map to the new ones (πŸ€– no-renamed-props):

Display

Old New
one l
two m
three m
four s

Headline

Old New
one l
two m
three s
four s

Body

Old New
one m
two s

The typography design tokens have been updated accordingly (πŸ€– no-deprecated-custom-properties (ESLint & Stylelint)):

Old New
typography-title-one-font-size display-l-font-size
typography-title-one-line-height display-l-line-height
typography-title-two-font-size display-m-font-size
typography-title-two-line-height display-m-line-height
typography-title-three-font-size display-m-font-size
typography-title-three-line-height display-m-line-height
typography-title-four-font-size display-s-font-size
typography-title-four-line-height display-s-line-height
typography-headline-one-font-size headline-l-font-size
typography-headline-one-line-height headline-l-line-height
typography-headline-two-font-size headline-m-font-size
typography-headline-two-line-height headline-m-line-height
typography-headline-three-font-size headline-m-font-size
typography-headline-three-line-height headline-m-line-height
typography-headline-four-font-size headline-s-font-size
typography-headline-four-line-height headline-s-line-height
typography-sub-headline-font-size headline-s-font-size
typography-sub-headline-line-height headline-s-line-height
typography-body-large-font-size body-l-font-size
typography-body-large-line-height body-l-line-height
typography-body-one-font-size body-m-font-size
typography-body-one-line-height body-m-line-height
typography-body-two-font-size body-s-font-size
typography-body-two-line-height body-s-line-height

New components

The new Compact component should be used to label information in space-constraint contexts.

The new Numeral component should be used to give emphasis to numerical content such as currency values.

Stable components

All experimental components are now stable.

  • The Calendar and DateInput components have been rebuilt from scratch for better performance and accessibility. They replace the legacy RangePicker, RangePickerController, SingleDayPicker, CalendarTag, and CalendarTagTwoStep components, which have been removed. The DateInput component now requires additional localized label props.
  • The ColorInput and PhoneNumberInput components enable users to enter hex colors and phone numbers respectively.
  • The Tooltip and Toggletip components have been rebuilt from scratch for improved accessibility. They replace the legacy Tooltip component.

Update the related imports (πŸ€– component-lifecycle-imports). For example:

- import { Calendar, type CalendarProps, type PlainDateRange } from '@sumup-oss/circuit-ui/experimental';
+ import { Calendar, type CalendarProps, type PlainDateRange } from '@sumup-oss/circuit-ui';

Other changes

  • Changed the PlainDateRange type from an array to an object with start and end properties. This affects the Calendar component's selection prop. Use the new updatePlainDateRange helper function to update a date range when a user selects a date:

    import { useState } from 'react';
    import { Calendar, updatePlainDateRange } from '@sumup-oss/circuit-ui';
    
    function Component() {
      const [selection, setSelection] = useState({});
      return (
        <Calendar
          onSelect={setSelection((prevSelection) =>
            updatePlainDateRange(prevSelection, date)
          )}
        />
      );
    }
  • Deprecated the InputElement interface and narrowed the Input component's element type to HTMLInputElement and the TextArea component's element type to HTMLTextAreaElement. This affects refs and event handlers.

    -import { InputElement } from '@sumup-oss/circuit-ui';
    
    -ChangeHandler<InputElement>
    +ChangeHandler<HTMLInputElement>
    
    -useRef<InputElement>()
    +useRef<HTMLInputElement>()
  • Removed the Table component's deprecated initialSortedRow prop. Use the initialSortedColumn prop instead (πŸ€– no-renamed-props).

From v7.x to v8

Circuit UI v8 introduces a redesigned Button component and switches the brand color from blue to black.

To get started, upgrade @sumup/circuit-ui and its peer dependencies:

npm upgrade @sumup/circuit-ui @sumup/design-tokens @sumup/icons

Upgrade any linter plugins your app is using:

# ESLint
npm upgrade @sumup/eslint-plugin-circuit-ui
# Stylelint
npm upgrade @sumup/stylelint-plugin-circuit-ui

Redesigned buttons

The Button component has been redesigned from the ground up. Corners are now rounded instead of pill-shaped, the tertiary variant now sports an underline to improve accessibility, and the loading spinner has been replaced with three animated dots. Long button labels that would previously wrap are now truncated to a single line with a trailing ellipsis.

The ButtonGroup component now uses a container query (instead of a media query) to switch to a horizontal layout. This makes it possible to use it in narrow spaces such as the SidePanel component.

Custom styles

Check whether any custom styles applied to a Button clash with the new design. Here are some common patterns we've observed and how to migrate them:

  • Tertiary Button with (horizontal) padding removed: A tertiary button should only be used next to a primary or secondary one and never alone. Use an Anchor with variant="highlight" or a secondary Button instead.

Disabled state

To improve accessibility, the Button's disabled attribute has been replaced with the aria-disabled attribute which enables the disabled element to receive focus and be perceived by screenreader users. Interactions with the disabled element are blocked by a dummy click handler. This is an internal markup change; developers should continue to use the disabled prop.

This change might break tests that use @testing-library/jest-dom's toBeDisabled, toBeEnabled and toHaveAttribute matchers. Replace toHaveAttribute('disabled') with toBeDisabled() and not.toHaveAttribute('disabled') with toBeEnabled(), then extend the matchers to account for the aria-disabled attribute:

Custom Jest matcher
// jest.setup.js
import { toBeDisabled, toBeEnabled } from '@testing-library/jest-dom/matchers';

function isAriaDisabled(element) {
  return (
    element.hasAttribute('aria-disabled') &&
    element.getAttribute('aria-disabled') === 'true'
  );
}

expect.extend({
  toBeDisabled(element) {
    const isDisabled =
      toBeDisabled.bind(this)(element).pass || isAriaDisabled(element);
    return {
      pass: isDisabled,
      message: () => {
        const is = isDisabled ? 'is' : 'is not';
        return [
          this.utils.matcherHint(
            `${this.isNot ? '.not' : ''}.toBeDisabled`,
            'element',
            ''
          ),
          '',
          `Received element ${is} disabled:`,
          `  ${this.utils.printReceived(element.cloneNode(false))}`,
        ].join('\n');
      },
    };
  },
  toBeEnabled(element) {
    const isEnabled =
      toBeEnabled.bind(this)(element).pass || !isAriaDisabled(element);

    return {
      pass: isEnabled,
      message: () => {
        const is = isEnabled ? 'is' : 'is not';
        return [
          this.utils.matcherHint(
            `${this.isNot ? '.not' : ''}.toBeEnabled`,
            'element',
            ''
          ),
          '',
          `Received element ${is} enabled:`,
          `  ${this.utils.printReceived(element.cloneNode(false))}`,
        ].join('\n');
      },
    };
  },
});

Black & white theme

SumUp is changing its brand colors from blue to black & white. The color tokens exported by @sumup/design-tokens have been updated accordingly.

The color tokens have been removed from the legacy JavaScript theme object and theme prop type. Use the semantic color tokens instead:

-color: ${theme.colors.p500};
+color: var(--cui-fg-accent);

Removed components and props

  • Removed the legacy navigation components: Header, Sidebar, SidebarContextProvider and SidebarContextConsumer. Use the TopNavigation and SideNavigation components instead (πŸ€– no-deprecated-components)
  • Removed the deprecated variant prop from the ProgressBar component (πŸ€– no-deprecated-props)

Other changes

  • Deprecated the ButtonGroup's actions.[primary|secondary].size prop. Use the top-level size prop instead.
  • Replaced the NotificationBanner's "tertiary" action variant with the "secondary" one and changed the action size to medium. Update the action props if necessary.
  • Changed the NotificationInline's action from the Button to the Anchor component. Update the action props if necessary.
  • Changed the variant of the previous/next buttons in the Pagination component from "tertiary" to "secondary".
  • Changed the default size of the CloseButton from 40px to 48px to match the Button component.

From v6.x to v7

Circuit UI v7 contains two foundational changes β€” the switch to ES Modules and the replacement of Emotion.js with CSS Modules β€” and a number of smaller changes to improve consistency and accessibility. New component lifecycle stages lower the barrier for contributions. The Next.js template has been upgraded to Circuit UI v7 and a new Remix template has been added.

To get started, upgrade @sumup/circuit-ui and its peer dependencies:

npm upgrade @sumup/circuit-ui @sumup/design-tokens @sumup/icons

Upgrade any linter plugins your app is using:

# ESLint
npm upgrade @sumup/eslint-plugin-circuit-ui
# Stylelint
npm upgrade @sumup/stylelint-plugin-circuit-ui

If you are using Emotion.js in your app or are importing legacy components, update all Emotion.js-related dependencies:

npm upgrade @emotion/is-prop-valid @emotion/react @emotion/styled

For a complete list of changes, refer to the changelog.

Prerequisites

Circuit UI now relies on APIs introduced in React 18. Upgrade the react and react-dom peer dependencies to >=18.

Circuit UI now requires at minimum Node.js v18. Note that Node 16 is scheduled to reach its end-of-life in September 2023.

Circuit UI no longer supports a range of older browsers:

Browser Previous New
Chrome 63+ 73+
Firefox 67+ 67+
Edge 79+ 79+
Safari iOS 11.0+ 12.2+
Safari macOS 11.1+ 12.1+
Opera 50+ 60+
Samsung Internet 8.2+ 11.1+

ES Modules

As of ES6 (ES2015), JavaScript supports a native module format called ES Modules, or ECMAScript Modules. This modern module format replaces the non-standard CommonJS.

@sumup/circuit-ui and @sumup/icons are now pure ESM. Please read this.

  • If you use TypeScript, you need to use TypeScript 4.7 or later (ref).
  • If you use a bundler, make sure it supports ESM and that you have correctly configured it for ESM. (Next.js supports ESM packages out of the box since v12).
  • The "exports" field is now used to configure the package entry points. Files that are not explicitly defined in "exports" can no longer be imported.

CSS Modules

Emotion.js, the CSS-in-JS library that Circuit UI had used until now to style components, has been replaced with CSS Modules. This will significantly improve the performance of SumUp’s web applications, future-proof the component library against ecosystem changes and start to decouple it from React. Read more about the reasoning in the RFC.

Global styles

Remove the BaseStyles component and import the CSS files containing the light theme and component styles instead:

// _app.tsx
import { ThemeProvider } from '@emotion/react';
import { light } from '@sumup/design-tokens';
-import { BaseStyles } from '@sumup/circuit-ui';
+import '@sumup/design-tokens/light.css';
+import '@sumup/circuit-ui/styles.css';

function App({ Component, pageProps }) {
	return (
		<ThemeProvider theme={light}>
-			<BaseStyles />
			<Component {...pageProps} />
		</ThemeProvider>
	);
}

The application code must be processed by a bundler that can handle CSS files. Next.js, Create React App, Remix, Vite, Parcel and others support importing CSS files out of the box.

If you are only importing stable components and aren't using Emotion.js in your app, you can remove all Emotion.js-related dependencies.

Custom component styles

You can continue to pass the className and styles props to Circuit UI components. If your application uses Emotion.js, you can continue to use the css prop since it is transpiled to the className prop by Emotion.js’ Babel plugin.

Design tokens

The design tokens have been ported to CSS custom properties (aka CSS variables) similar to the existing semantic color tokens:

-${theme.borderRadius.circle}
+var(--cui-border-radius.circle)

The JavaScript theme object from @sumup/design-tokens has been deprecated. Use the πŸ€– prefer-custom-properties ESLint rule to flag and automatically rewrite uses of the JS theme to CSS custom properties.

Note that some theme properties haven't been migrated to CSS custom properties and are considered legacy:

  • mq and breakpoints: Unfortunately, CSS custom properties aren't supported inside media queries. In the future, we might be able to use CSS environment variables but they are a long way off. Until then, you can keep using the JS theme properties or hard-code the media queries.
  • grid: These properties were only intended to be used by the Grid components. Remove any usage of them.

Utility classes

Circuit UI exports style mixins such as spacing, hideVisually, or shadow. These functions return an Emotion.js style object that can be passed to the css prop but not the className prop. The legacy style mixins will be kept for backward compatibility.

For applications that don’t use Emotion.js, Circuit UI exports a new, smaller collection of string utility classes that can be passed to the className prop and conditionally joined using the new clsx helper.

Example
import { clsx, utilClasses } from '@sumup/circuit-ui';

function Component() {
  return <div className={clsx(utilClasses.center, utilClasses.hideVisually)} />;
}

Component lifecycle

Circuit UI v7 introduces the concept of lifecycle stages for components. Within each stage, components meet different requirements and receive different levels of support. Experimental and legacy components are exported separately from stable components. Use the πŸ€– component-lifecycle-imports ESLint rule to flag components that have moved to a different stage and automatically update their imports.

The following components have been moved to the legacy stage:

  • Layout components: Grid, Row, Col, and InlineElements
  • Calendar components: CalendarTag, CalendarTagTwoStep, RangePicker, RangePickerController, SingleDayPicker, and CalendarConstants
  • Legacy navigation components: Header, Sidebar, SidebarContextProvider, and SidebarContextConsumer
  • Tooltip component
  • Style mixins: cx, center, clearfix, disableVisually, focusOutline, focusVisible, hideScrollbar, hideVisually, inputOutline, shadow, spacing, and typography
  • The uniqueId utility function

Removed @sumup/collector

@sumup/collector has been deprecated and the integration with Circuit UI has been removed. Replace the tracking prop with event handlers to dispatch user interaction events instead (πŸ€– no-deprecated-props).

import { Button } from '@sumup/circuit-ui';
+import { useClickTrigger } from '@sumup/collector';

function Component() {
+  const dispatch = useClickTrigger();
+
+  const handleClick = () => {
+    dispatch({
+      component: 'button',
+      label: 'track-button',
+      customParameters: { key: 'value' },
+    });
+    // ...other logic
+  }

  return (
    <Button
+      onClick={handleClick}
-      tracking={{
-        label: 'track-button',
-        customParameters: { key: 'value' },
-      }}
    >
      Buy now
    </Button>
  );
}

Other changes

  • Removed the public export of the RadioButton component. Use the RadioButtonGroup component instead Reverted in v7.1 and postponed until v9 (πŸ€– no-deprecated-components)
  • Removed the public export of the Selector component. Use the SelectorGroup component instead Reverted in v7.1 and postponed until v9 (πŸ€– no-deprecated-components)
  • Removed the deprecated children property from the SelectorGroup's options prop. Use the label and description properties instead.
  • Removed the deprecated children prop from the Checkbox component. Use the label prop instead.
  • Removed the deprecated explanation prop from the Toggle component. Use the description prop instead (πŸ€– no-renamed-props)
  • Removed the deprecated confirm, notify, and alert variants from the Badge, NotificationInline, and NotificationToast components. Use the success, warning, and danger variants instead (πŸ€– no-renamed-props)
  • Changed the signature of the ImageInput's component prop. The component should now accept aria-hidden instead of alt.
  • Migrated the Calendar components to TypeScript. Some props are now required. The CalendarTagTwoStep's clearText and confirmText props have been renamed to clearButtonLabel and confirmButtonLabel respectively.
  • Migrated the Carousel components to TypeScript. Added the required playButtonLabel, pauseButtonLabel, prevButtonLabel, and nextButtonLabel props.
  • Migrated the Tabs and Sidebar components to TypeScript.
  • Simplified the function signature of the style mixins that no longer require the theme parameter (shadow, focusOutline, focusVisible, and inputOutline).
  • Removed the sharedPropTypes export. Type the props using TypeScript instead.

Known issues

  • Circuit UI v7 is incompatible with next-plugin-preact because the plugin breaks support for ES Modules.

From v6.x to v6.3

Although not a major version, Circuit UI v6.3 comes with a few important visual changes related to color that we wanted to mention here for visibility.

To get started, upgrade @sumup/circuit-ui and @sumup/design-tokens:

yarn upgrade @sumup/circuit-ui @sumup/design-tokens --latest

New semantic color tokens

In v6.1, we released new semantic color tokens, which are meant to replace the existing color tokens from @sumup/design-tokens to enable use cases like theming, as well as more robust customizations. The new tokens were declared as CSS custom properties in the Circuit UI BaseStyles.

Starting in v6.3, all Circuit UI components (except the legacy Sidebar component) use these semantic color tokens under the hood. Legacy color tokens from @sumup/design-tokens have been deprecated and will be removed in a future major version.

Visual component changes

A number of components were adjusted to the new color system. Most notably:

  • The Avatar component's default placeholder (visible when no src is passed) was changed to match the new colors and to use SVG icons from @sumup/icons.
  • All form components validationHints, in their warning state, are now orange instead of gray.
  • Warning icons are now orange on white, instead of yellow with a black exclamation mark !.
  • The warning color changed from yellow (2.01:1 contrast ratio) to orange (3.01:1 contrast ratio).
  • The Button component's disabled state styles were visually aligned with iOS and Android styles. Contrast is now effectively lower.

Other changes

  • We've received a report that the upgrade surfaced a long-standing JSDOM bug where a <fieldset> element is considered :disabled if it contains a <legend>. This might cause issues with unit tests that use user-event, since it will throw when trying to interact with a :disabled element. This is seemingly unrelated to Circuit UI, but if you run into the issue, you can work around it by turning off user-event's pointerEventsCheck: userEvent.click(element, { pointerEventsCheck: 0 }.

From v5.x to v6

Circuit UI v6 contains two major changes: the removal of default component margins, and changes to form components to improve consistency and accessibility.

To get started, upgrade @sumup/circuit-ui and its peer dependencies:

yarn upgrade @sumup/circuit-ui @sumup/design-tokens @sumup/icons --latest

For a complete list of changes, refer to the changelog.

⚠️ Note: this major version doesn't include codemods. The changes cannot easily be automated and should be migrated manually. Detailed migration steps for each change are listed below.

No default component margins

Default component margins have been deprecated since v2, and the use of the noMargin prop was encouraged to ensure that UIs don't rely on the default margin. Omitting the prop throws errors since v5.

In v6, default margins have been completely removed from components. The noMargin prop, now redundant, has been removed as well.

If you've already addressed all the errors thrown in v5, migration is straightforward: now that the noMargin prop isn't necessary anymore, you can simply remove it from your codebase. If you haven't, make sure to address them before migrating to v6. Failure to do so can result in unintended UI bugs.

Removing the prop from your codebase is easiest done using search and replace in your IDE.

Form component consistency

In v6, form components follow a more consistent and accessible pattern.

Markup changes

Markup was adapted to improve consistency and accessibility across form components.

  • Form components are now all wrapped in a div that receives any styles passed through the style or className attributes, including via the Emotion.js css prop. This might break custom styles.
  • Form <label>s don't wrap inputs anymore. This might break styles previously passed to labelStyles (see also The labelStyles prop was removed).
  • An empty live region is now rendered below form components. If a validationHint is passed along with either the invalid, hasWarning or showValid props (indicating status messages), the message will be rendered inside the live region to be announced by screen readers.
  • The validationHint is now programmatically associated to a form control via aria-describedby. A unique id for it is generated internally. If a custom id is passed to a component with aria-describedby, the descriptions will be combined.

Verify your forms visually. Look out for form components with custom styles and adapt them as needed.

The Label component was removed

In most cases, the Label component shouldn't be used: form components have a built-in label prop that ensures that controls are appropriately and accessibly labelled.

In implementations where the Label doesn't label a form component, use a <Body size="two" /> instead.

Where the Label describes the purpose of a form component, use the built-in label prop instead. In edge cases and custom form component implementations, replace the Label by a <label css={typography("two")} />.

The inline prop was removed

The prop was removed from the Input, TextArea and Select components.

It went again the principles of atomic design and was rarely used in implementations.

Replace it with a custom CSS wrapper, for example using display: flex;.

Alternatively, to recreate the exact same functionality, pass display: inline-block; and a margin to the components:

const inlineStyles = (theme) => css`
  display: inline-block;
  margin-right: ${theme.spacings.mega};
`;

function Address() {
  return (
    <>
      <Input label="Postcode" css={inlineStyles} />
      <Input label="City" css={inlineStyles} />
    </>
  );
}

The labelStyles prop was removed

The prop was removed from the Input and TextArea components.

In v5, labelStyles is predominantly used to apply styles to a form component's wrapper, the label element. Wrapper styles can't be passed via the style or className prop, because these are forwarded to the underlying form element.

In v6, form components are wrapped in a div that receives any style or className props, including via the Emotion.js css prop.

Therefore, to migrate:

  • labelStyles should be replaced by the Emotion.js css prop
  • css (previously passed to the underlying form element) should be replaced by inputStyles

For example:

<Input
  label="Name"
  /* wrapper styles */
-  labelStyles={spacing({ bottom: "giga" })}
+  css={spacing({ bottom: "giga" })}
  /* input styles */
-  css={customInputStyles}
+  inputStyles={customInputStyles}
/>

Note that if you were using the labelStyles in non-standard ways (for example using selectors such as > div to style component internals), you may need to refactor your implementation.

The label prop only accepts a string

In v5, some form components (such as the Input) accepted any ReactNode as a label.

This can be an accessibility issue, because the label prop value is exposed to assistive technology as the control's accessible name. Accessible names don't have semantics or structure, so rendering e.g. an anchor or a tooltip in the label is bad practice.

In v6, the label prop only accepts a string. The change is TypeScript-only (i.e. a ReactNode would still be rendered) but migration is encouraged.

We've observed two common patterns affected by this change:

If a label is used to render an optional label, it can be replaced with the optionalLabel prop:

- const optionalStyles = (theme) => css`
-   color: ${theme.colors.n700};
- `;

<Input
- label={<>Address line 2 <span css={optionalStyles}>(Optional)</span></>}
+ label="Address line 2"
+ optionalLabel="Optional"
/>;

If a label is used to render a tooltip next to the label, replace the tooltip with a validationHint.

If you need time to migrate, you can temporarily ignore the change (with @ts-expect-error if using TypeScript). Bear in mind that passing a ReactNode may stop working in a future minor.

Improved validationHint for the Checkbox component

In v5, a validationHint passed to the Checkbox component (either as a description or as a status message) would render in a tooltip.

This caused accessibility and UI issues (with the tooltip overlapping other controls) and wasn't consistent with other form components.

In v6, a validationHint is rendered below the Checkbox.

Minor fixes

This version also includes a number of accessibility and visual fixes. While these aren't technically breaking changes, you may notice them in snapshots, so they are listed here for your convenience:

  • the RadioButtonGroup's role was changed from the implicit group (from the fieldset element) to the explicit radiogroup. It also has orientation="vertical"
  • the chevron icon in the Select element was hidden from assistive technology using aria-hidden
  • invalid radio buttons are now programmatically marked as invalid using aria-invalid
  • the CurrencyInput's currency code/symbol is now exposed to assistive technology as part of the input's description
  • the font size of any text rendered in an Input's prefix or suffix was increased from 14px to 16px to match designs. This also affects the CurrencyInput's currency code/symbol.
  • unintended spacing was removed from below the TextArea using vertical-align: top;

Other changes

  • react-number-format was upgraded to v5. This could be a breaking change if you were relying on internal react-number-format props. Refer to the react-number-format migration guide for details. Any explicit typings will also need to be updated.
  • The ButtonGroup component now switches between a secondary button (on viewports of at least mq.kilo) and a tertiary button (on viewports narrower than mq.kilo) using CSS media queries, instead of rendering three buttons and conditionally hiding one. Tests (e.g. using @testing-library) should now query the secondary button without using *AllBy queries. Snapshots might also be affected.
  • Typography design tokens now use the rem unit. Ensure that your global styles do not override the root font-size. See The Surprising Truth About Pixels and Accessibility.
  • The Popover component was migrated from Popper (deprecated) to Floating UI. If your app uses Popper directly, we recommend migrating to Floating UI to avoid duplicating dependencies. See Migrating from Popper 2 to Floating UI
  • Circuit UI's browser support policy was updated. The library now supports browsers with support for dynamic module imports. See the Browser Support documentation for details.
  • If your app uses TypeScript, an upgrade of the @types/react package in Circuit UI may clash with the version installed in your app. If your app is on React 18, upgrade @types/react to ^18.0.25 to fix the issue. If your app is on React 17, upgrade @types/react to ^17.0.52 and extend the React namespace. More details on the DefinitelyTyped PR that introduced the issue.

πŸ€– Codemods (jscodeshift)

Some of the changes up to v5 can be automated with codemods, small scripts that modify your app's source code automatically. Changes that can be codemodded are marked with a robot emoji (πŸ€–) and the name of the transform (e.g. button-variant-enum). The codemods are built with jscodeshift and can be run through the CLI that ships with Circuit UI. Here is an overview of all available options (you can view this help menu by running yarn circuit-ui migrate --help):

yarn circuit-ui migrate

Automatically transforms your source code to Circuit UI's latest APIs

Options:
  --language, -l   The programming language(s) of the files to be transformed
                [array] [required] [choices: "TypeScript", "JavaScript", "Flow"]
  --path, -p       A path to the folder that contains the files to be
                   transformed                           [string] [default: "."]
  --transform, -t  The transform to be applied to the source code
                       [string] [required] [choices: "button-variant-enum", ...]

You can only run one codemod at a time and we encourage you to apply the transforms incrementally and review the changes before continuing. The codemods don't cover all edge cases, so further manual changes might be necessary. For example, jscodeshift is only able to look at one file at a time, so if a Circuit UI component is wrapped in a styled component in one file and used in another, the codemod won't be able to update its props.

Tip: Provide the --transform/-t argument at the end of the command, so that as you run further codemods you can easily replace the last argument and reuse the command to run the next codemod.

⚠️ If you run into 'node\r': No such file or directory when running the codemods with yarn, run them with Node directly instead (this is a known issue).

./node_modules/.bin/circuit-ui migrate -l JavaScript -l TypeScript -t codemod-name

From v4.x to v5

Circuit UI v5 is a maintenance release, primarily removing deprecated components and props.

⚠️ In order to make the migration from v4 to v5 easier, we recommend that you address the changes listed below before upgrading the Circuit UI dependencies. Deprecation warnings can also help identify code that needs to be migrated.

Explicit browser support

Starting in v5, Circuit UI is explicit about which browsers it supports. Refer to the browser support documentation for details.

Here's what you need to be mindful of when migrating:

  • You no longer need to transpile Circuit UI when bundling your application (using e.g. next-transpile-module in Next.js). Circuit UI now supports all target browsers out-of-the-box.
  • Previously recommended polyfills for Internet Explorer support are no longer necessary, and can be removed from your application:

New semantic color names

...in design tokens

New semantic color names were introduced in @sumup/[email protected]. The legacy names were deprecated and have been removed in v5.

Legacy name New name
success confirm
warning notify
danger alert

The new naming brings cross-platform consistency, as well as alignment with some existing components such as the notification icons.

πŸ€– semantic-color-names

...in component variants

Some Body, BodyLarge and Badge component variants were also renamed for consistency with the new design tokens:

Legacy variant New variant
<Body variant="success" /> <Body variant="confirm" />
<Body variant="error" /> <Body variant="alert" />
<Badge variant="success" /> <Badge variant="confirm" />
<Badge variant="warning" /> <Badge variant="notify" />
<Badge variant="danger" /> <Badge variant="alert" />

Note: the Body variants mapping above also apply to the BodyLarge component.

The legacy variant names were deprecated in v4.14.0 and will be removed in v5.

πŸ€– semantic-variant-names

New notification components

The legacy Circuit UI notification components (Notification, NotificationList, NotificationCard and InlineMessage) were general purpose and lacking clear guidelines on when to use which, leading to inconsistent usage in our apps.

They were deprecated in v4.14.0 and replaced by semantic, accessible components that make it clear when each should be used and are flexible enough to cover all use cases (NotificationBanner, NotificationFullscreen, NotificationModal, NotificationToast and NotificationInline). The legacy components have been removed in v5.

To migrate to the new notifications, you'll need to:

  • Replace the InlineMessage by the NotificationInline in your app
  • Replace uses of the NotificationCard, NotificationList and Notification (often used together) by either the NotificationInline or NotificationBanner, depending on the use cases.

Furthermore, some patterns that were previously not supported by Circuit UI are now available:

  • The new NotificationToast provider and hook should replace any custom implementation of a toast notifications system for improved accessibility.
  • The new NotificationModal can be used for building notification dialogs that are essential to user flows.
  • The new NotificationFullscreen can replace custom full-screen messages such as error pages or empty states, for more consistency across pages.

Read more about the new notification components in the Notification section in Storybook.

Required accessible labels

In Circuit UI v3, components requiring accessible labels started throwing an error when the labels weren't being passed. This behavior could be worked around for migration purposes by setting the UNSAFE_DISABLE_ACCESSIBILITY_ERRORS environment variable.

In v5, the workaround was removed, meaning that all components that require accessible labels expect to receive them.

Before migrating, make sure that you add appropriate and localized labels for all occurrences flagged by the error mechanism. After that, stop setting the UNSAFE_DISABLE_ACCESSIBILITY_ERRORS environment variable.

Runtime errors for missing noMargin props

Several components used to have built-in bottom margin by default, and a noMargin prop to reset it. This non-atomic behavior was deprecated several major versions ago, but migration has proved difficult.

All components with built-in margin should be passed noMargin, so that we can remove the prop using codemods in a future major while avoiding UI regressions.

Instead of removing the noMargin prop in v5, components that should receive it now throw a runtime error in development if the prop is missing. Production and testing builds are not affected. The noMargin prop was also marked as required in TypeScript types.

While migrating, you can use an escape hatch to continue running your app in development without throwing.

In your app, expose the UNSAFE_DISABLE_NO_MARGIN_ERRORS environment variable. You can use the Webpack DefinePlugin (see the Circuit UI Storybook Webpack config as an example) or, if your app uses Next.js, you can declare the variable in your next.config.js (Next.js documentation).

Once your app is set up to accept the environment variable, set it to true in development to prevent components from throwing:

UNSAFE_DISABLE_NO_MARGIN_ERRORS=true yarn dev # or yarn start

Keep in mind that this escape hatch is not meant as a way to permanently bypass the errors, but as a temporary workaround to help migrate without regressions. The noMargin prop will be entirely removed in v6.

The ListItemGroup replaces the CardList

Two new components, ListItem and ListItemGroup, were added to Circuit UI v4.4.0 to render lists of contextually similar items.

The ListItem component should generally be used implicitly by passing items to the ListItemGroup. The ListItemGroup differs from the List component because while the latter applies light styling to an HTML ul or ol, the former allows for structured data and interactivity. Refer to the component's documentation for usage guidelines and examples.

Circuit UI v5 comes with two related breaking changes:

  • the legacy CardList component has been deprecated in favor of the ListItemGroup. The component APIs are very different and migration can unfortunately not be automated: please migrate manually. The new component also comes with different styles: verify the changes visually with a designer.
  • (If you were already using the ListItem in a Circuit UI v4.x release:) The ListItem component's prefix and suffix props were renamed to leadingComponent and trailingComponent. The suffixLabel and suffixDetails props were renamed to trailingLabel and trailingDetails. The codemod will not transform uses of the ListItem as ListItemGroup items. (πŸ€– listitem-prop-names)

Combined LoadingButton and Button

The LoadingButton is an extension of the Button component and adds a loading state. This is a common use case, so the loading functionality has been added to the Button component itself. The LoadingButton was deprecated in v4.1 and was removed in v5.

The API hasn't changed: uses of the LoadingButton can be replaced by the Button component

πŸ€– component-names-v5

Other changes

  • The deprecated zIndex.sidebar design token was removed from @sumup/design-tokens. Use sIndex.navigation instead. There is no codemod for this change: search and replace the old value for the new one in your codebase.
  • The deprecated shadowSingle, shadowDouble and shadowTriple style mixins were removed. Use the shadow() style mixin instead. There is no codemod for this change: migrate manually by searching for the deprecated style mixins in your codebase, and verify the changes visually.
  • The RadioButton component's deprecated children prop was removed. Use the label prop, now typed as required, instead. There is no codemod for this change: search your codebase for uses of the RadioButton or RadioButtonGroup component and migrate them manually.
  • The ButtonGroup component's deprecated children prop was removed. Use the actions prop instead. If the new ButtonGroup component API doesn't fit your use case, consider aligning your use case with the design system, or writing a custom layout wrapper for your buttons.
  • The deprecated showValid option was removed from the inputOutline style mixin.

From v3.x to v4

Circuit v4 is a small maintenance release, featuring the upgrade to Emotion 11 and new brand icons.

To get started, upgrade @sumup/circuit-ui and its peer dependencies:

yarn upgrade @sumup/circuit-ui @sumup/design-tokens @sumup/icons --latest

For a complete list of changes, refer to the changelog.

Emotion 11

Circuit was upgraded to Emotion 11, so apps using Circuit will also have to upgrade. The main changes are Emotion's new package names and better TypeScript support.

New package names

The main change in Emotion 11 is a renaming of most packages under the @emotion organization.

It can be automated by using an ESLint plugin provided by Emotion: refer to the migration guide for upgrade instructions.

Then, after running the codemod:

  • Remove the legacy dependencies and add the new ones:
    # example with yarn
    yarn remove @emotion/core jest-emotion babel-plugin-emotion # remove legacy dependencies
    yarn add @emotion/react # add new dependencies
    yarn add -D @emotion/babel-plugin @emotion/jest # add new dev dependencies
    yarn upgrade @emotion/styled --latest # upgrade existing packages
  • Fix any duplicate imports of @emotion/react in cases where multiple modules (e.g. css from v10's @emotion/core and ThemeProvider from v10's emotion-theming) are all under @emotion/react in v11.

Better TypeScript support

Emotion's TypeScript types have been improved in v11. Refer to the migration guide for details of the changes.

Typing the theme

One of the most useful changes is support for typing the theme.

Previously, many apps would use CreateStyled:

// utils/styled.ts
import styled, { CreateStyled } from '@emotion/styled';
import { Theme } from '@sumup/design-tokens';

export default styled as CreateStyled<Theme>;

...and import the custom styled in their components:

// components/RedCard.tsx
import { css } from '@emotion/core';

import styled from 'util/styled';

const RedCard = styled(Card)(
  ({ theme }) => css`
    background-color: red;
  `
);

Now, you can type the theme by adding it to @emotion/react's declaration:

// types/emotion.d.ts (don't forget to include this file in tsconfig.json under `typeRoots`)
import { Theme as CircuitTheme } from '@sumup/design-tokens';
import {} from '@emotion/react/types/css-prop'; // See https://github.com/emotion-js/emotion/pull/1941

declare module '@emotion/react' {
  export interface Theme extends CircuitTheme {}
}

...and use styled from @emotion/styled directly:

// components/RedCard.tsx
import { css } from '@emotion/core';
import styled from '@emotion/styled';

const RedCard = styled(Card)(
  ({ theme }) => css`
    background-color: red;
  `
);

Compatibility with Storybook

In case you're using Storybook in your application, you'll need to tweak the Storybook Webpack config to make it compatible with Emotion 11 (Storybook is still on Emotion 10).

Make sure that references to Emotion packages inside Storybook are pointing to the v11 packages:

// .storybook/main.js

const path = require('path');
const toPath = (_path) => path.join(process.cwd(), _path);

module.exports = {
  webpackFinal: async (config) => {
    // Add compatibility with Emotion 11
    config.resolve.alias = {
      ...config.resolve.alias,
      '@emotion/core': toPath('node_modules/@emotion/react'),
      '@emotion/styled': toPath('node_modules/@emotion/styled'),
      'emotion-theming': toPath('node_modules/@emotion/react'),
    };
    return config;
  },
};

New brand icons

Circuit v4 ships with the new brand icons from @sumup/icons v2.

Overview of @sumup/icons v2

  • There used to be "filled" and "empty" versions of some of the v1 icons. The distinction has been removed and all icons are now using a "filled" design. The icon versions were primarily used in the legacy Sidebar component, which is being replaced by the new SideNavgation.
  • The icons' size names changed from small and large to 16 and 24 (their size in pixels).
  • The default icon size was small in v1 but is now 24 in v2 (this change follows Circuit v3's new component heights).
  • Many icons were renamed.
  • Some icons or icon sizes were removed.

Migrating to @sumup/icons v2

Most of the changes can be automated using the πŸ€– icons-v2 codemod.

The codemod will print warnings and errors to your console when a manual migration is required:

  • Some icons were renamed to match the names of SumUp products. They should not be used outside of the product's context. If you have doubts about your use of the icon, file an issue or contact the Design System team.
  • If an icon you were using was removed in v2 and can't be replaced by another icon, file an issue or contact the Design System team.
  • If you were using a small (16px) icon that is only available in v2 in 24 (24px), see if the 24px size works for your use case. If it doesn't, file an issue or contact the Design System team.

Finally, remember to visually verify your application after the upgrade!

From v2.x to v3

Circuit v3 is a large major release, including long-awaited changes from the full year that passed since v2. This guide will help you upgrade your application. Don't hesitate to contact the maintainers if you have any further questions.

Dependencies

Start by upgrading @sumup/circuit-ui and its peer dependencies:

yarn upgrade @sumup/circuit-ui @sumup/design-tokens @sumup/icons --latest

Accessibility

Any accessible label props that were previously optional are now required and enforced in all components.

To help identify places where accessible labels are missing, components throw runtime errors in development at missing labels. Production and testing builds are not affected.

During the migration and while the missing labels are still being added, you can use an escape hatch to continue running the app in development without throwing accessibility errors.

In your app, expose the UNSAFE_DISABLE_ACCESSIBILITY_ERRORS environment variable. You can use the Webpack DefinePlugin (here's an example in the Circuit UI Storybook config) or, if your app uses Next.js, you can declare the variable in your next.config.js (Next.js documentation).

Now, if you want to turn off the accessibility errors temporarily, run the development app with the environment variable set to true:

UNSAFE_DISABLE_ACCESSIBILITY_ERRORS=true yarn dev # or yarn start

Keep in mind that this escape hatch is not meant as a way to permanently avoid the errors, but as a temporary workaround while the missing labels are being written, localized and added to the relevant components.

Reminder: For the Input and Select components, use the built-in label prop instead of using the Label component separately.

Other accessibility improvements include:

New JSX transform

Circuit v3 improves compatibility with the new JSX transforms (introduced in React v17).

We recommend the following Babel config for applications on Next.js and Emotion 10 (Emotion 11 is not yet supported by Circuit UI). It also includes support for Emotion's css prop.

{
  "presets": [
    [
      "next/babel",
      {
        "preset-react": {
          "runtime": "automatic",
          "importSource": "@emotion/core"
        }
      }
    ]
  ],
  "plugins": [["babel-plugin-emotion", { "cssPropOptimization": true }]]
}

Typography

Before v3, typography components were general purpose and flexible. There were no guidelines on when to use a certain typography style. This has led to inconsistent usage in our apps.

The new version introduces new, semantic typography components that make it clear when each should be used, are flexible enough to cover all use cases, and for the first time include semantic colors as well.

Typography component names

The core typography components were renamed:

v2 v3
Text Body
Heading Headline
SubHeading SubHeadline
Blockquote See the next section

(πŸ€– component-names-v3.)

Note that the codemod will also transform other renamed components (see Other Changes.

Typography component variants

The Body (formerly Text) component's variants were changed:

v2 v3
<Text bold> <Body variant="highlight">
<Text italic> Use custom styles
<Text strike> Use custom styles
<Blockquote> <Body variant="quote">

⚠️ These changes also apply to the Anchor component, which extends Body.

Use πŸ€– body-variant-highlight to migrate bold to variant="highlight". Note that this will transform your <p> elements into inline <strong> elements: you might also need to pass as="p" if you need a block-level element.

Bonus: the new Body component also supports the success, error and subtle semantic variants. Have a look at the story to get started.

New sizes

The number of available sizes was reduced, and size names were changed from the metric prefix scale (kilo, mega etc.) to numbers (one, two etc.).

Typography components size prop

The number of sizes was reduced in v3, here's the desired mapping from v2:

v2 v3
<Heading size="kilo"> <Headline size="four">
<Heading size="mega"> <Headline size="four">
<Heading size="giga"> <Headline size="three">
<Heading size="tera"> <Headline size="two">
<Heading size="peta"> <Headline size="one">
<Heading size="exa"> <Headline size="one">
<Heading size="zetta"> Migrate manually to size="one" or use custom styles (2.625rem)
<SubHeading size="kilo"> <SubHeadline> (the sizes were removed)
<SubHeading size="mega"> <SubHeadline> (the sizes were removed)
<Text size="kilo"> <Body size="two">
<Text size="mega"> <Body size="one">
<Text size="giga"> Migrate manually to size="one" or use custom styles (1.125rem)

⚠️ The Body size changes also apply to the Anchor and List components.

Most of these changes can be automated using the πŸ€– typography-sizes codemod.

The codemod will also warn about occurrences of <Heading size="zetta"> and <Text size="giga">. These should be manually migrated to size="one" if possible, or alternatively to custom size styles (recommendation from design: 2.625rem for the Headline and 1.125rem for the Body).

Typography sizes mixins

The deprecated text[Kilo|Mega|Giga] style mixins were replaced by a single typography mixin, and the deprecated heading[Kilo|Mega|Giga|Tera|Peta|Exa|Zetta] and subHeading[Kilo|Mega] style mixins were removed.

Generally, avoid using typography style mixins. Instead, use typography components directly.

Heading as props

The as prop is now required in both the Headline and the SubHeadline components. Intentionally setting the heading level ensures a consistent and accessible page structure. The as prop values were also restricted to HTML heading elements to ensure that heading components render semantic heading elements.

Component Allowed as prop values
Headline h1, h2, h3, h4, h5, h6
SubHeadline h2, h3, h4, h5, h6

Note that the Headline and SubHeadline will still fall back to h2 and h3, respectively, but omitting the as prop will start throwing errors in the next major version.

Typography design tokens

The typography size values in @sumup/design-tokens were also updated to reflect the changes in @sumup/circuit-ui.

There is no codemod for these changes, migrate manually through search and replace (e.g. typography.text.mega πŸ‘‰ typography.body.one). Refer to the tables in Typography component names and Typography components size prop for the correct mappings.

Generally, avoid using typography size tokens. Instead, use typography components directly.

New modal and popover APIs

The Modal and Popover components were refactored to consolidate their APIs and to improve their accessibility.

Modal

The Modal, ModalWrapper, ModalHeader, ModalFooter, ModalContext, and ModalConsumer components are no longer exported. Instead, use the useModal hook to render modals instead.

Modals in Circuit v3 are accessible by default, have a streamlined UI and behavior (dimensions, click outside, etc.) and handle edge cases like modal and popover stacking.

Refer to the Modal stories for usage examples.

⚠️ You might notice browser UI obscuring parts of your application on mobile viewports. Your application needs to ensure its content stays within CSS safe areas using CSS env. See Getting started for help with configuring the viewport.

Popover

The Popover component was rebuilt in Circuit v3. It now uses Popper v2 under the hood and comes with a refreshed component API.

More importantly, the Circuit v3 Popover is opinionated when it comes to its usage pattern. It no longer accepts custom children, unlike v2, but rather a list of actions (links and/or buttons).

To migrate, separate the popovers in your application into two types.

Start by migrating any popover resembling a dropdown menu to the new Circuit v3 Popover. Refer to the Popover story for a usage example.

Most of the remaining popovers with custom children should gradually be migrated to use different UI patterns (for example modals). In the meantime, we recommend creating a local copy of the Circuit v3 Popover in your application that accepts custom children instead of an array of actions. You'll find an example in the SumUp merchant dashboard (private repository).

Component heights

The heights of all form components were aligned for consistency. The new size values are:

Size name Value Usage
giga 48px Default for web + mobile
kilo 32px Dense layout for web + mobile
byte 24px Extreme dense layout for web + mobile

Here's an overview of how the component heights have changed:

Component Old default height New default height
Button and derived components 40px 48px
Input and derived components 40px 48px
Select 40px 48px
Tabs 80px 48px
Tag 34px 32px

We recommend verifying these changes visually at the end of the migration.

In addition to its increased height, the Button's default size was renamed from mega to giga to align it with the new size values (see the table above). (πŸ€– button-default-size)

Other changes

  • The design tokens borderRadius scale was changed. (πŸ€– theme-border-radius)
    value v2 name v3 name
    1px kilo β€” (remove the radius or hardcode)
    4px mega bit
    6px giga β€” (migrate to byte)
    8px tera byte
    12px peta kilo
    16px β€” mega (new value)
  • The NotificationBanner component has been renamed to NotificationCard. (πŸ€– component-names-v3)
  • Label prop names across components were harmonized to follow the Label pattern. (πŸ€– label-prop-names)
    • CardHeader: labelCloseButton πŸ‘‰ closeButtonLabel
    • Hamburger: labelActive πŸ‘‰ activeLabel, labelInActive πŸ‘‰ inactiveLabel
    • Tag: labelRemoveButton πŸ‘‰ removeButtonLabel
    • Toggle: labelChecked πŸ‘‰ checkedLabel, labelUnchecked πŸ‘‰ uncheckedLabel
  • The TableRow, TableHeader and TableCell components are no longer exported. Use the Table component instead.
  • The Table's custom onSortBy method signature has been changed. The nextDirection argument moved to the third position ((index, nextDirection, rows) πŸ‘‰ (index, rows, nextDirection)) and is now optional (i.e. it can be undefined instead of null in the previous implementation).
  • The SelectorGroup's label is now visible by default, pass hideLabel to hide it visually. Its children are now rendered horizontally by default.
  • Default data-testids are no longer built into the Table and CardHeader components. We recommend querying by role in tests instead to imitate how users interact with our applications.

Cleaning up

Finally, Circuit v3 removes previously deprecated and/or unused features and props. These breaking changes may not affect your application if you've already addressed the deprecation warnings in Circuit v2 minors, but we still recommend going through the list of changes below.

  • The deprecated Spacing component has been removed. Use the spacing style mixin instead.
  • The ProgressBar's deprecated children prop has been removed. Use the label prop instead.
  • The Card's deprecated shadow prop has been removed. Shadows have been replaced by a single outline in an earlier minor version.
  • The deprecated styleHelpers aggregate is no longer exported. Import each style mixin directly instead.
  • The themePropType is no longer exported from @sumup/circuit-ui. Import it from @sumup/design-tokens instead.
  • The deprecated withComponents HOC has been removed. Use the useComponents hook instead.
  • The Badge's deprecated onClick prop has been removed. Badges are not meant to be interactive and should only communicate the status of an element. Use the Tag component for interactive elements instead.
  • The Badge's deprecated primary variant has been removed. Use the neutral variant instead.
  • The experimental static styles extraction feature has been removed.

From v1.x to v2

Library format

Circuit UI is now compiled with TypeScript instead of Babel (#563, #597). ES and CJS versions are available as before, though this is not something you need to worry about since most bundlers pick the best format that they support automatically based on the main and module entries in package.json.

Note, however, that some modern JavaScript syntax and language features are no longer pretranspiled. Tools such as react-scripts v2+ and Next.js include node_modules in their transpilation process so Circuit UI works with them out of the box. If you have a custom build process, you should make sure that Circuit UI is transpiled like the rest of your source code.

We recommend testing your application in your oldest supported browsers to verify that it still works.

Peer dependencies

Circuit UI v1 included React and Emotion dependencies as direct dependencies. This could cause these dependencies to be bundled twice if your application specified a different version of React or Emotion.

Circuit UI v2 moves these dependencies to peer dependencies, so Circuit UI will use whatever version your application specifies (#485). The minimum version of React has been bumped to v16.8+ to support hooks.

If you haven't installed them already, you can do so by running the following command in your terminal:

# With yarn
yarn add react react-dom @emotion/core @emotion/styled emotion-theming
# With npm
npm install --save react-dom @emotion/core @emotion/styled emotion-theming

In Circuit UI v2 some functionality has been extracted into separate packages for easier maintenance. The themes have been moved to @sumup/design-tokens, the icons are available from @sumup/icons, and the number & currency utils have been completely rewritten in @sumup/intl. The new @sumup/collector package is used for event tracking. These packages are marked as required peer dependencies. To install them, run the following command in your terminal:

# With yarn
yarn add @sumup/collector @sumup/design-tokens @sumup/icons @sumup/intl
# With npm
npm install --save @sumup/collector @sumup/design-tokens @sumup/icons @sumup/intl

Refer to the individual packages for documentation on how to use them.

Font loading

Circuit UI now downloads the Aktiv Grotesk font family in the 400 (regular) and 700 (bold) weights. It uses @font-face with the font-display set to swap, which means a fallback font is shown until the custom fonts have loaded.

You should remove any code in your application that was previously used to load these fonts.

Forward custom props and refs

A big theme of this release is consistency.

Any additional props that are passed to a component are now spread on their outermost child element (#553). This is useful for test ids, data attributes, and custom styles using Emotion's styled function or css prop.

React refs allow you to access the underlying DOM node of a component. All Circuit UI components now forward refs to the underlying DOM node (for single node components such as a Button) or to the main interactive DOM node (for composite components such as an Input) (#592).

⚠️ The ability to pass custom styles and refs is meant as an escape hatch. We strongly recommend avoiding using them as we cannot guarantee that they will be compatible with future changes. Please consider opening an issue or PR to suggest the improvement in Circuit UI instead.

Component static properties

Many components expose their configuration values as static properties. The Text component, for example, exposes its size options as Text.KILO, Text.MEGA, and Text.GIGA. The purpose of these static properties was to autocomplete the options and prevent typos.

Since Circuit UI is being migrated to TypeScript, the static properties are no longer necessary. VS Code can suggest and autocomplete the options based on the TypeScript types. TypeScript will warn you at build time if you use an unsupported or mistyped value. Furthermore, removing the static properties reduces the bundle size slightly.

Thus, the static properties have been removed from all components. Here's how you can pass a value to a component now:

import { Text } from '@sumup/circuit-ui';

const Hello = () => (
-  <Text size={Text.KILO}>Hello</Text>
+  <Text size="kilo">Hello</Text>
);

The affected components are: Badge, Blockquote, Button, ButtonGroup, Card, CardFooter, CardList.Item, Heading, InlineMessage, Input, List, MessageIcon, ModalFooter, NotificationIcon, Popover, ProgressBar, SubHeading, TableHeader, TableCell, Text, TextArea, and Tooltip.

(πŸ€– component-static-properties)

Removed components

  • The SideNav component has been removed. Use the Sidebar component instead.
  • The CreditCardDetails, CardNumberInput, NameOnCardInput, SecurityCodeInput, ExpiryDateInput, and the credit card utils have been removed. Use SumUp's card widget instead.
  • The CardSchemes and PaymentMethodIcon components have been removed. Use @sumup/icons instead.
  • The AutoCompleteInput and AutoCompleteTags components have been removed. You can build them yourself using the SearchInput, Card, and Tag components.
  • The MaskedInput and RestrictedInput components have been removed. Use react-text-mask or a similar package directly instead.
  • The MessageIcon and MessageButton components have been removed. Use the Notification component's icon and children props instead.
  • The Markdown component has been removed. Use markdown-to-jsx or a similar package instead.
  • The State component has been removed. Use React's useState hook instead.
  • The Picture component has been removed. Use the native HTML picture element instead.

Renamed components

  • The ListView component has been renamed to CardList (πŸ€– component-names-v2)
  • The SvgButton component has been renamed to IconButton (πŸ€– component-names-v2)
  • The Message component has been renamed to Notification (πŸ€– component-names-v2)
  • The InlineNotification component has been renamed to InlineMessage (πŸ€– component-names-v2)
  • The GlobalStyles component has been renamed to BaseStyles (πŸ€– component-names-v2)

Changed components

  • The GlobalStyles component no longer accepts a custom prop. Use Emotion's Global component instead.
  • The Heading, SubHeading, Text, and Input components no longer accept the element prop. Emotion 10 introduced the ability to change the HTML element. Use the as prop instead (πŸ€– as-prop)
  • The List component's ordered prop has been replaced by the variant enum prop (πŸ€– list-variant-enum)
  • The List component's default size is now mega to match the Text component.
  • The Badge component's color prop has been renamed to variant (πŸ€– badge-variant-enum)
  • The primary and secondary Button boolean props have been removed. Use the variant enum prop instead (πŸ€– button-variant-enum)
  • The plain Button prop has been removed. Use the new Anchor component or the tertiary Button variant instead.
  • The flat Button variant has been removed (πŸ€– button-variant-enum)
  • The giga Button size has been removed. Use the mega size (default) instead (πŸ€– button-size-giga)
  • The LoadingButton's exit animations have been removed. An action's success or error result should be communicated outside the button (πŸ€– exit-animations)
  • The Input, TextArea, and Select components have the label built in now. Use the label prop to pass in the label content and remove the Label component from your code. The label prop will become required in the next major version of Circuit UI.
  • The Input and Textarea components no longer accept *ClassName props. Emotion 10 uses style objects instead of class names. Use the *Styles props instead. The wrapperStyles prop has been renamed to labelStyles (πŸ€– input-styles-prop).
  • The Input and Textarea components' deepRef prop has been renamed to ref (πŸ€– input-deepref-prop)
  • The Input and Textarea components no longer have an optional state. Add "(optional)" to the label text instead.
  • The Selector component's onClick and selected props have been renamed to onChange and checked (πŸ€– selector-props). The value and name have been added as required props.
  • The RadioButton, Toggle, and Switch component's onToggle prop has been renamed to onChange (πŸ€– onchange-prop)
  • The Toggle component's on, labelOn, and labelOff props have been renamed to checked, labelChecked, and labelUnchecked (πŸ€– toggle-checked-prop).
  • The IconButton component's dimensions and style have changed. It is now consistent with the Button component.
  • The Hamburger component's default size has been increased to match the IconButton component.
  • The Hamburger component's light prop has been removed. Set the color through CSS instead.
  • The Spinner component's dark prop has been removed. Set the color through CSS instead.
  • The InlineMessage component's type prop has been renamed to variant (πŸ€– inline-message-variant-enum)
  • The Pagination component's footer, justify, align, perPage, and pagesToShow props have been removed. The page prop has been renamed to currentPage. The total prop has been replaced by the totalPages prop which represents the total number of pages as opposed to the total number of items (totalPages = Math.ceil(total / perPage)).

Utilities

  • The currencyAmountUtils have been removed. There is no replacement, we suggest you copy the old implementation to your application.
  • The currencyUtils have been removed. Use @sumup/intl instead (πŸ€– currency-utils)
  • The textTera style helper has been removed. Use the textGiga style helper instead.
  • The shadowGround and shadowBorder style helpers have been removed. Use the box-shadow CSS property instead.
  • The unit style helpers (addUnit, subtractUnit, multiplyUnit, divideUnit ) have been removed. Use the CSS calc function instead.

Theme changes

  • The themes have been moved to @sumup/design-tokens. Import them from there instead (πŸ€– theme-to-design-tokens)
  • The iconSizes.byte theme value has been removed. Use iconSizes.kilo instead (πŸ€– theme-icon-sizes)
  • The grid.afterTera theme value has been renamed to grid.tera (πŸ€– theme-grid-tera)