Type checking improvements in V8

Reduce boilerplate and increase confidence with Base Web V8
Chase Starr - 24 June 2019

Changes in Base Web@v8

If you do not use flow, you can safely update to v8 from v7 with no issues. There has been no breaking change in functionality or javascript APIs.

Improved flow type API in base web styled function

If you are migrating from v7 be sure to read the last section here for directions on how to update smoothly.

See here for a recap of what the styled function does and view the v7 migration docs for previous context. We've heard your feedback on the v7 styled function and have improved its usability when working with custom themes. Previously, developers using custom theme types must import the type in each file using the function. This introduced additional boilerplate and increased the barrier of entry to statically typing your code; doing so should be as simple as possible. A new function called createThemedStyled is now exported from the baseui package. This function will provide a custom type that you supply in the $theme field in styled's callback. Take a look at the example below.

// @flow
import {styled, createThemedStyled} from 'baseui';
type PropsT = {};
// Uses baseui's default theme.
function StyledA = styled<PropsT>('div', props => {
return {
backgroundColor: props.$theme.colors.primary400,
};
});
// Supplying a custom theme.
type CustomTheme = {customColor: string};
const themedStyled = createThemedStyled<CustomTheme>();
function StyledB = themedStyled<PropsT>('div', props => {
return {
backgroundColor: props.$theme.customColor,
};
});

Themed withStyle function

A common use-case is to extend a baseui styled component and maintain type-safety with your custom theme. Previously, writing the code below would error on accessing $theme in StyledB. Now we no longer need to perform additional type annotation simply to appease flow, and can focus on the task at hand with all of the added benefits to type checking.

// @flow
import {styled, withStyle} from 'baseui';
type PropsT = {};
function StyledA = styled<PropsT>('div', props => {
return {
backgroundColor: props.$theme.colors.primary400,
};
});
function StyledB = withStyle<typeof StyledA, PropsT>(StyledA, props => {
return {
color: props.$theme.colors.mono400,
};
});

Similar to how styled has a related createThemedStyled function, withStyle has createThemedWithStyle.

// @flow
import {createThemedStyled, createThemedWithStyle} from 'baseui';
type CustomTheme = {customColor: string};
const themedStyled = createThemedStyled<CustomTheme>();
const themedWithStyle = createThemedWithStyle<CustomTheme>();
type PropsT = {};
function StyledA = themedStyled<PropsT>('div', props => {
return {
backgroundColor: props.$theme.customColor,
};
});
function StyledB = themedWithStyle<typeof StyledA, PropsT>(StyledA, props => {
return {
color: props.$theme.customColor,
};
});

One helpful tip when working with themed styled functions, is to isolate the 'create...' calls to a single file in your code base. From there, you can relatively import your themed styled functions to avoid duplicated import statements from baseui.

themed-styled.js

// @flow
import {createThemedStyled, createThemedWithStyle} from 'baseui';
type CustomTheme = {customColor: string};
export const styled = createThemedStyled<CustomTheme>();
export const withStyle = createThemedWithStyle<CustomTheme>();

styled-components.js

// @flow
import {styled, withStyle} from './themed-styled.js';
type PropsT = {};
function StyledA = styled<PropsT>('div', props => {
return {
backgroundColor: props.$theme.customColor,
};
});
function StyledB = withStyle<typeof StyledA, PropsT>(StyledA, props => {
return {
color: props.$theme.customColor,
};
});

Themed useStyletron hook

Brand new to Base Web is a themed wrapper of styletron's useStyletron hook. In addition to what the styletron-react version gives you, importing useStyletron from baseui will provide the theme. Doing so removes the need to import ThemeContext around and calling React.useContext to read values from the theme. See the example below for a consice method to style your elements.

// @flow
import {useStyletron} from 'baseui';
function ComponentA() {
const [css, theme] = useStyletron();
return <div className={css({color: theme.colors.primary400})}>test</div>;
}

As we've seen before, useStyletron also has a related createThemedUseStyletron function.

// @flow
import {createThemedUseStyletron} from 'baseui';
type CustomTheme = {customColor: string};
const themedUseStyletron = createThemedUseStyletron<CustomTheme>();
function ComponentA() {
const [css, theme] = themedUseStyletron();
return <div className={css({color: theme.customColor})}>test</div>;
}

Migrating themed styled calls to v8

See below for an example of how v7 patterns map to v8.

A simple scenario:

-import {styled} from 'baseui';
+import {createThemedStyled} from 'baseui';
type CustomTheme = {customColor: string};
type PropsT = {};
+const themedStyled = createThemedStyled<CustomTheme>();
-const StyledA = styled<PropsT, CustomTheme>('div', props => {
+const StyledA = themedStyled<PropsT>('div', props => {
return {color: props.$theme.customColor};
});

A more complex scenario:

-import {styled} from 'baseui';
+import {createThemedStyled} from 'baseui';
type CustomTheme = {customColor: string};
type PropsT = {};
+const themedStyled = createThemedStyled<CustomTheme>();
function ComponentA(props: {className: string}) {
return <div className={props.className}>test</div>;
}
-const StyledB = styled<typeof ComponentA, PropsT, CustomTheme>(ComponentA, props => {
+const StyledB = themedStyled<typeof ComponentA, PropsT>(ComponentA, props => {
return {color: props.$theme.customColor};
});

To make the migration process even simpler, you can run a codemod to shift from v7's generic API to this extracted pattern. The codemod operates somewhat naively, but will output running code. There may be some follow-up after running. Follow the instructions below to run the codemod locally.

npm install -g jscodeshift
jscodeshift -t node_modules/baseui/codemods/styled-v8-themedStyled.js <transform-path>

Use the -d option for a dry-run and use -p to print the output for comparison.