Themes Migration
For any questions or help with this, you have friends at the #dev-frontend Slack channel
1. Update the design library
TODO: Add info about older versions.
Update the design library to the latest version by running
yarn add @bulb/design
At this point you'll notice your linter is gonna go nuts, ignore it for now and follow this guide, it is the easiest and fastest way to migrate (trust me, I've done this for a couple of repos now).
2. Adding the theme provider
You should wrap your application with a theme provider this will make the theme
object available for styled components and also within any React components through a React Context.
There are two theme providers available, one for client-side rendered applications and another one for server-side rendered applications (e.g. Gatsby or Next.js).
Client-side rendered applications
Go to the file where all of your providers are and add ThemeProvider
to the mix
import { ThemeProvider } from '@bulb/design/modules/ThemeProvider';...export const App: React.FC = () => (<SomeProvider><ThemeProvider><GlobalStyles /><MainComponent /></ThemeProvider></SomeProvider>);
Make sure that <GlobalStyles />
is a child of theme provider!
Server-side rendered applications
For SSR apps, you should do the same as above, but instead, import SSRThemeProvider
!
import { SSRThemeProvider } from '@bulb/design/modules/ThemeProvider';
The SSR theme provider will make sure that:
- The app is server-side rendered with CSS variables
- The app is rehydrated correctly and fallbacks are applied for browsers that do not support CSS variables
3. Updating the theme type for styled-components
In order to make the theme type available for every usage of styled
, you should change styled-components
's DefaultTheme
to be the one exported by the design library.
For that you should create this file:
src/@types/styled-components/index.d.ts
This should be the content:
import { Theme } from '@bulb/design/theme';declare module 'styled-components' {// eslint-disable-next-line @typescript-eslint/no-empty-interfaceinterface DefaultTheme extends Theme {}}
4. Removing all imports that have been deprecated
Okay, now we're gonna start some real code change.
A lot of imports have been deprecated, the first thing we'll do is to get rid of those imports.
Use the global search tool (⌘+⇧+F
on VS Code) to find each of these lines, and replace them with an empty string.
Attention: For all of the next steps, change the "files to include" option to .ts, .tsx
Pro tip: Add a newline (⇧+Return
) to the end of each these, that way you'll end up remove the entire line
import { responsive } from '@bulb/design/utils/responsive';
import { bulbTokens } from '@bulb/design/utils/scaleProperties';
import { palette } from '@bulb/design/styles/palette';
import { media } from '@bulb/design/utils/styleUtils';
import { type } from '@bulb/design/utils/modularScale';
This will remove the majority of the deprecated imports, the rest will be removed ad-hoc in the next steps
5. Getting rid of responsive
This one will blow your mind if you haven't used regex search and replace before.
First perform a global search with this query (click the .*
button to activate regex search)
\$\{responsive`([\n\S\s]*?)`\}
Then simply replace all with $1;
.
For now, ignore all of other errors you see. e.g. Cannot find name 'bulbTokens'.
Something like this:
${responsive`font-size: ${bulbTokens.type.mouse};margin-bottom: ${bulbTokens.spacing.moon};`}
Should now look like this:
font-size: ${bulbTokens.type.mouse};margin-bottom: ${bulbTokens.spacing.moon};
6. Convert all uses of bulbTokens
to use the theme instead
This one is easy. Just two easy steps.
- Perform a global search for
${bulbTokens.spacing
and replace them for${({ theme }) => theme.space
. - perform a global search for
${bulbTokens.type
and replace them for${({ theme }) => theme.fontSizes
.
7. Convert all uses of type
to use the theme instead
Some repos don't use type
, which is great, in that case you can skip this step
Another easy one with regex search.
Perform a global search for \$\{type\('(.+?)'\)\}
(tick the regex option ".*") and replace it with ${({ theme }) => theme.fontSizes.$1}
Cool, eh?
11. Replace media
with themes
This task requires attention
Here's a list. The first line is the global search query, the second line is what you should replace them with:
media.mobileOnly`({ theme }) => theme.media.small.andBelow} {
media.tabletAndDown`({ theme }) => theme.media.medium.andBelow} {
media.tabletAndUp`({ theme }) => theme.media.medium.andAbove} {
media.desktopAndUp`({ theme }) => theme.media.large.andAbove} {
media.singleColumn`({ theme }) => theme.media.extended.below1024} {
media.multiColumn`({ theme }) => theme.media.extended.above1024} {
media.smallAndDown`({ theme }) => theme.media.extended.below1200} {
media.mediumAndUp`({ theme }) => theme.media.extended.above1200} {
You might have noticed that this has broken everything that use these media queries, so now we're gonna fix it with regex search again.
Perform a global search query for:
theme\.media([\n\S\s]*?)`\}
Replace the results with:
theme.media$1};
Most (if not all) of the problems should be fixed now.
11. Replace palette
with theme in styled components
This another quick and easy one. Simple perform this search:
${palette
and replace it with:
${({ theme }) => theme.colours
7.5 - Fixing leftovers responsive and media
TODO
Also theme.dolphin[0]
media.something containing ({ theme })
8. Fixing negative values
Since the new theme.space
and theme.fontSizes
values are based on CSS variables, they don't place nicely with negative values. It's an easy fix, though.
Perform a search for -${({ theme
.
If you find any results fix them to this format:
margin-left: calc(-1 * ${({ theme }) => theme.space.earth});
9. Fixing ugly code
The find and replace above can cause some ugly convertions, though. Let's fix those:
Perform a global search for } ${({ theme
.
If you find anything that looks like this:
margin: ${({ theme }) => theme.space.earth} ${({ theme }) => theme.space.mars};
Replace it for something a bit more elegant, like this:
margin: ${({ theme }) => `${theme.space.earth} ${theme.space.mars}`};
12. Replacing palette
within components that are not styled
For regular React components, you should be able to get the theme from the context by:
import { useTheme } from 'styled-components';const MyComponent: React.FC = () => {const { colours } = useTheme();return <Section backgroundColor={colours.brand.blue} />;};
This one is a bit tedious because you'll need to search for the remaining uses of palette
and manually add useTheme
to the components
13. Replacing palette
in tests
If you have tests that use palette
, you can import and use testTheme
instead.
import { testTheme } from '@bulb/design/utils/testUtils/testTheme';describe('some test', () => {it('renders a pink text', () => {const PinkText = <Text colour={testTheme.colours.brand.pink}>Hello</Text>;});});
⚠️ ATTENTION: Do not use testTheme
for client facing code, as we need to the colours to come from the theme (e.g. Imagine some day there is a dark theme)
14. Replacing palette
in places that are not inside components
This one is tricky. Some times we have constants that contain colours. e.g.
const colourMap = {new: palette.ui.orange,open: palette.ui.red,pending: palette.extended.blue.tintish,hold: palette.extended.blue.shady,solved: palette.greyScale.darkGrey,closed: palette.greyScale.grey,};
The best option is to turn these into functions that take the theme
as argument and then call it inside of the component that's gonna use those colours:
import { DefaultTheme, useTheme } from 'styled-components';const getColourMap = (theme: DefaultTheme) => {new: theme.colours.ui.orange,open: theme.colours.ui.red,pending: theme.colours.extended.blue.tintish,hold: theme.colours.extended.blue.shady,solved: theme.colours.greyScale.darkGrey,closed: theme.colours.greyScale.grey,};const MyComponent: React.FC = (props) => {const theme = useTheme();const colourMap = getColourMap(theme);return <Icon backgroundColor={colourMap[props.status]} />;}
15. Last changes
By now, if you run yarn lint
there shouldn't be many errors in your console.
As a rule of thumb, any import that came from @bulb/design/styles
is now available from themes.
Other changes will be described in the changelog. You can fix each of them by taking a look at the design library changelog.
16. Tests
For unit tests, if you were using @testing-library/react
, there is a new util function that replaces render
:
import { renderWithThemeProvider } from '@bulb/design/utils/testUtils/renderWithThemeProvider';
For end-to-end tests, make sure that you wrap you page component with <ThemeProvider>
17. Checking everything works
Run your application and check if everything is working fine!
Also, ask your QA to do a thorough regression test!