Getting Started with Base Web

Let's build a password generator!
Jhey Tompkins - 2 April 2019
Base Web by Uber Design

In this post, you'll learn how to create a basic password generator using Base Web components. If you are unfamiliar with Base Web, please go over the Getting started section before continuing with this article.

Setup

For the sake of simplicity, we'll use create-react-app to bootstrap a React application.

create-react-app password-generator
cd password-generator

Next, we need to pull in the packages for Base Web.

yarn add baseui styletron-engine-atomic styletron-react

You might be asking "What's Styletron?". Styletron is a CSS-in-JS solution for component-oriented styling.

As per Base Web's set up instructions, we need to wrap our app in both the Styletron Provider and the Base Provider before we can begin:

import {Provider as StyletronProvider} from 'styletron-react';
import {Client as Styletron} from 'styletron-engine-atomic';
import {LightTheme, BaseProvider} from 'baseui';
const engine = new Styletron();
ReactDOM.render(
<StyletronProvider value={engine}>
<BaseProvider theme={LightTheme}>
<App />
</BaseProvider>
</StyletronProvider>,
document.getElementById('root'),
);

Do you want to learn more about Styletron? Check out the Styletron docs.

What are we building?

In this post, we'll implement the following mini-app, that can be used to generate passwords. It will look something like this:

We can create the majority of our UI using the following components

This puts us in a good spot without needing to make any changes.

At this stage, we can hook up some logic and we are almost there. It's not pretty though. We need to add some of our own styling tweaks.

Overrides

If you've ever consumed component libraries, you've likely hit some hurdles. You may want to pass extra props or tweak the rendering. The common scenario is wanting to adjust styles. Some libraries cater for this by exposing extra verbose props.

<MyAwesomeComponent
callToActionsStyle={...}
onActionClick={...}
containerStyle={...}
style={...}
/>

Base Web tackles this with an "Overrides" pattern. It provides a consistent API to override a components characteristics. You are able to override the styles, props and render logic of a component. And this is all made possible through one prop.

To learn more, check out the Overrides guide.

Don't throw everything into overrides though. Adjusting your theme values is better if you override the same properties over and over (Read about themes here).

To customize the Card component to work better for our use-case, we can do something like this:

<Card
overrides={{
Root: {
style: {
left: '50%',
maxWidth: '420px',
position: 'absolute',
top: '20px',
transform: 'translate(-50%, 0)',
width: '95vw',
},
},
}}
>
...
</Card>

As a next step, let's move the button inside our input that generates a new password every time a user clicks it. We could use the Button component and tweak the positioning. But there is no need. Base Web already caters for scenarios like this:

<Input
overrides={{
After: () => (
<Button kind={KIND.minimal}>
<Icon />
</Button>
),
}}
/>

Using overrides, we can leverage an After option and pass a Button to it.

Adjusting the layout

Our app looks almost there but the layout for those options doesn't look right. We need some labels and layout constraints. Base Web provides a component that aids with creating these less reusable layout pieces. We can use a Block whenever there's a need to layout UI.

<Block marginBottom="scale800">
<FormControl label="Length">
<Slider {...sliderProps} />
</FormControl>
</Block>

Alternatively, you can use the useStyletron hook to achieve the same result:

const [css, theme] = useStyletron();
<div className={css({marginBottom: theme.sizing.scale800})}>
<FormControl label="Length">
<Slider {...sliderProps} />
</FormControl>
</div>;

Hooking up the password generation

Let's hook up the logic for our password generator. We will use zxcvbn and generate-password to handle validation and generation.

Base Web exposes the props we need for various change and click handlers. Except for copying our password to clipboard, interactions will generate a new password.

const setNewPassword = p => {
const newPassword = p
? p
: generatePassword({length, numbers, uppercase, symbols});
const {score} = zxcvbn(newPassword);
setStrength(score);
setCopied(false);
setPassword(newPassword);
};

We generate a new password inside an effect whenever one of our options changes. We also generate a new password when our Input action gets clicked.

Communicating password strength

One thing we aren't doing yet is communicating how strong a generated password is. We have a strength score but we aren't using it. Let's communicate password strength with a thick colored border on our input.

We can pass our strength score into a function that returns a color ID from our theme.

const getStrengthColor = strength => {
switch (strength) {
case 0:
return 'negative400';
case 1:
return 'warning400';
case 2:
return 'rating400';
case 3:
return 'positive200';
case 4:
return 'positive400';
default:
return 'primary50';
}
};

Tie that into our Input:

<Input
inputRef={passwordRef}
value={password}
onChange={event => setNewPassword(event.target.value)}
overrides={{
InputContainer: {
style: ({ $theme }) => ({
borderLeftColor: $theme.colors[getStrengthColor(strength)],
borderRightColor: $theme.colors[getStrengthColor(strength)],
borderTopColor: $theme.colors[getStrengthColor(strength)],
borderBottomColor: $theme.colors[getStrengthColor(strength)],
borderLeftWidth: $theme.sizing.scale200,
borderRightWidth: $theme.sizing.scale200,
borderTopWidth: $theme.sizing.scale200,
borderBottomWidth: $theme.sizing.scale200,
})
},
After: () => (...)
}}
/>

And this will give us

Conclusion

With little effort we've put together an example app that uses the Base design language.

By default all the components have the Uber look and feel, but the powerful customization makes for a component library that feels less opinionated.

You can see all the code for this post in the following demo: