-
Notifications
You must be signed in to change notification settings - Fork 22
Migrating to V4
We have moved away from Styled Components (CSS-in-JS) to Linaria (build-time CSS extraction). This improves compatibility with projects that rely on SSR or strict CSP.
If your project uses V3, follow these steps to install V4.
Recommended step:
Upgrade to the latest V3 release and ensure your build and unit tests are passing first. This will help us with troubleshooting V4-specific issues.
npm i @lifesg/react-design-system@3
1. Upgrade to the V4 Design System
npm i @lifesg/react-design-system@4
2. Import the theme stylesheet
In your application's JavaScript entrypoint (e.g. pages/_app.tsx or app/layout.tsx in a NextJS project):
import "@lifesg/react-design-system/theme/styles/lifesg.css";Or in an existing .css file:
@import "@lifesg/react-design-system/theme/styles/lifesg.css";3. Set up the new ThemeProvider
Replace the Styled Components ThemeProvider or DSThemeProvider with the new ThemeProvider from @lifesg/react-design-system/theme:
- import { DSThemeProvider, LifeSGTheme } from "@lifesg/react-design-system/theme";
- import { ThemeProvider } from "styled-components";
+ import { ThemeProvider } from "@lifesg/react-design-system/theme";
- <ThemeProvider theme={LifeSGTheme}>{children}</ThemeProvider>
- <DSThemeProvider theme={LifeSGTheme}>{children}</DSThemeProvider>
+ <ThemeProvider theme="lifesg">{children}</ThemeProvider>Important
If DSThemeProvider was not previously used, add mode="light" to avoid issues with the default dark mode in V4.
<ThemeProvider theme="lifesg" mode="light">
{children}
</ThemeProvider>4. Migrate design token usages
In V3, design tokens were called as functions. In V4, they are CSS variables. Refer to the Design tokens section for migration details.
General
- Design tokens
- Accessing design token values at runtime
- Breakpoint tokens
- Dark mode
- ThemeProvider
- ThemeSpec and customisation
Components
In V3, design tokens could be called as functions that accepted props.
V3 usage
import { Colour } from "@lifesg/react-design-system/theme";
const ExampleComponent = styled.div`
background: ${(props) => Colour["bg"](props)};
`;V4 usage
In V4, design tokens are CSS variable strings. Update usages depending on the context:
-
In Styled Components:
import { Colour } from "@lifesg/react-design-system/theme"; const ExampleComponent = styled.div` background: ${Colour["bg"]}; `;
-
In inline styles:
<div style={{ color: Colour["text"] }} />
If you need the resolved CSS value at runtime (e.g. to pass to a third-party library or perform calculations), use the useDesignToken hook:
import { Colour, useDesignToken } from "@lifesg/react-design-system/theme";
const ExampleComponent = () => {
const color = useDesignToken(Colour["text"]);
// color is a resolved CSS value e.g. "#1d2939"
return <ThirdPartyChart color={color} />;
};For tokens that resolve to a numeric length unit (e.g. radius, spacing or breakpoints), use parsePxOrRemValue to get a number:
import { Spacing, useDesignToken } from "@lifesg/react-design-system/theme";
import { parsePxOrRemValue } from "@lifesg/react-design-system/theme";
const ExampleComponent = () => {
const gap = parsePxOrRemValue(useDesignToken(Spacing["spacing-16"]));
// gap is a number e.g. 16
return <ThirdPartyChart gap={gap} />;
};In V3, you had to extract the breakpoint value at runtime by invoking the design token.
V3 usage
import { Breakpoint } from "@lifesg/react-design-system/theme";
import { useTheme } from "styled-components";
const ExampleComponent = () => {
const theme = useTheme();
const tablet = Breakpoint["lg-max"]({ theme });
const isTablet = useMediaQuery({ maxWidth: tablet });
};V4 usage
In V4, media query hooks are provided out of the box:
import {
Breakpoint,
useSafeMaxWidthMediaQuery,
} from "@lifesg/react-design-system/theme";
const ExampleComponent = () => {
const isTablet = useSafeMaxWidthMediaQuery(Breakpoint["lg-max"]);
};If the actual pixel value is required at runtime:
import { Breakpoint, useDesignToken } from "@lifesg/react-design-system/theme";
import { parsePxOrRemValue } from "@lifesg/react-design-system/theme";
const ExampleComponent = () => {
const tablet = parsePxOrRemValue(useDesignToken(Breakpoint["lg-max"]));
const handleResize = () => {
if (window.innerWidth <= tablet) {
// do something
}
};
};In V4, dark mode is enabled by default. ThemeProvider follows the user's system preference unless a mode is explicitly set.
If your project does not support dark mode, set mode="light" to opt out:
import { ThemeProvider } from "@lifesg/react-design-system/theme";
<ThemeProvider theme="lifesg" mode="light">
{children}
</ThemeProvider>;The Styled Components ThemeProvider should be replaced with the new ThemeProvider from @lifesg/react-design-system/theme.
V3 usage
import { LifeSGTheme } from "@lifesg/react-design-system/theme";
import { ThemeProvider } from "styled-components";
<ThemeProvider theme={LifeSGTheme}>
<Component />
</ThemeProvider>;V4 usage
import { ThemeProvider } from "@lifesg/react-design-system/theme";
<ThemeProvider theme="lifesg">
<Component />
</ThemeProvider>;Import the corresponding theme stylesheet in your entrypoint:
import "@lifesg/react-design-system/theme/styles/lifesg.css";The ThemeSpec object and the overrides API have been removed in V4. There is no longer mixing and matching of colour schemes via props.
V3 usage
import { LifeSGTheme, ThemeSpec } from "@lifesg/react-design-system/theme";
import { ThemeProvider } from "styled-components";
const customTheme: ThemeSpec = {
...LifeSGTheme,
overrides: {
primitiveColour: {
"primary-10": "#F3C85C",
},
font: {
"heading-size-xxl": "4rem",
"heading-lh-xxl": "4.5rem",
"heading-ls-xxl": "0.056rem",
},
},
};
const App = () => {
return (
<ThemeProvider theme={customTheme}>
<Component />
</ThemeProvider>
);
};V4 usage
For ad hoc customisation, override CSS variables directly. Choose an approach based on your project's constraints:
-
Option 1: CSS stylesheet — override entire theme
Override the theme globally by targeting the
data-themeattribute set byThemeProvider.[data-theme="lifesg"] { --fds-colour-bg-primary: #0043ce; --fds-colour-text-inverse: #ffffff; }
-
Option 2: CSS stylesheet — override for a specific context
Suitable for static overrides scoped to a class or selector.
.custom-theme { --fds-colour-bg-primary: #0043ce; --fds-colour-text-inverse: #ffffff; }
<div className="custom-theme"> <Component /> </div>
-
Option 3: Inline style
Suitable for dynamic overrides driven by runtime values.
const App = ({ customTheme }) => { return ( <div style={ { "--fds-colour-bg-primary": customTheme.primaryColour, "--fds-colour-text-inverse": "#ffffff", } as React.CSSProperties } > <Component /> </div> ); };
-
Option 4:
useApplyStyleUse this if your project enforces a strict Content Security Policy that disallows inline styles.
import { useRef } from "react"; import { useApplyStyle } from "@lifesg/react-design-system/theme/utils"; const App = ({ customTheme }) => { const rootRef = useRef<HTMLDivElement>(null); useApplyStyle(rootRef, { "--fds-colour-bg-primary": customTheme.primaryColour, "--fds-colour-text-inverse": "#ffffff", }); return ( <div ref={rootRef}> <Component /> </div> ); };
Border.Util has been removed. For dashed border styling, use the DashedBorder utility component instead.
V3 usage
Border.Util["dashed-default"] could be applied directly in Styled Components declarations.
import { Border } from "@lifesg/react-design-system/theme";
const ExampleComponent = styled.div`
${Border.Util["dashed-default"]({
radius: Radius["sm"],
thickness: Border["width-040"],
colour: Colour["border"],
})}
`;V4 usage
A generic utility component is provided.
import { DashedBorder } from "@lifesg/react-design-system/dashed-border";
<DashedBorder
radius={Radius["sm"]}
thickness={Border["width-040"]}
colour={Colour["border"]}
>
{/* original children here */}
</DashedBorder>;Button.Default, Button.Small and Button.Large have been deprecated. Use Button with the sizeType prop instead.
- <Button.Large>Click me</Button.Large>
- <Button.Small>Click me</Button.Small>
- <Button.Default>Click me</Button.Default>
+ <Button sizeType="large">Click me</Button>
+ <Button sizeType="small">Click me</Button>
+ <Button>Click me</Button>| Deprecated |
sizeType value |
|---|---|
Button.Large |
large |
Button.Small |
small |
Button.Default |
default (or omit) |
ButtonWithIcon has been merged into Button. Update the import and replace the component.
- import { ButtonWithIcon } from "@lifesg/react-design-system/button-with-icon";
+ import { Button } from "@lifesg/react-design-system/button";
- <ButtonWithIcon icon={<Icon />}>Label</ButtonWithIcon>
+ <Button icon={<Icon />}>Label</Button>DSThemeProvider has been removed. Replace with the new ThemeProvider. See ThemeProvider.
ErrorDisplay.ImagePathAttributes has been renamed to ErrorDisplayImagePathAttributes.
- import { ErrorDisplay } from "@lifesg/react-design-system/error-display";
- type MyProps = ErrorDisplay.ImagePathAttributes;
+ import { ErrorDisplayImagePathAttributes } from "@lifesg/react-design-system/error-display";
+ type MyProps = ErrorDisplayImagePathAttributes;IconButton has been merged into Button. Update the import and replace the component.
- import { IconButton } from "@lifesg/react-design-system/icon-button";
+ import { Button } from "@lifesg/react-design-system/button";
- <IconButton icon={<Icon />} />
+ <Button icon={<Icon />} />The underlying baseTextSize prop type has been moved from the internal TypographySizeType to FontSize.
- import type { TypographySizeType } from "@lifesg/react-design-system/theme/font/types";
+ import type { FontSize } from "@lifesg/react-design-system/theme";The accepted values are unchanged:
"heading-xxl" | "heading-xl" | "heading-lg" | "heading-md" | "heading-sm" | "heading-xs"
| "body-baseline" | "body-md" | "body-sm" | "body-xs"
The underlying size prop type has been moved from the internal TypographySizeType to TextListSize.
- import type { TypographySizeType } from "@lifesg/react-design-system/theme/font/types";
+ import type { TextListSize } from "@lifesg/react-design-system/text-list";The accepted values are unchanged:
"heading-xxl" | "heading-xl" | "heading-lg" | "heading-md" | "heading-sm" | "heading-xs"
| "body-baseline" | "body-md" | "body-sm" | "body-xs"