The introduction
This article series will walk you through how to build your own component library by creating one.
This is part two of the series. If you haven’t read Part 1 yet, I recommend you read it first. In addition, we will talk about atomic design. If you’re not familiar with the concept, reading about it (here) will help.
In this section we will discuss:
-
Styled – Components make our components more configurable
-
Add a palette to our library
-
Use some polished features
-
Efficient development processes using NPM Link, Babel and ESLint
-
Apply atomic design principles to our component library structure
Atomic Design & Components
Now that we’ve done a lot of initial configuration, we’ll spend some time thinking about how to build a useful component library. To achieve this, we will borrow some concepts from Brad Frost’s atomic design. This book is definitely worth reading and you can buy it if you want to learn more about it. But for the sake of brevity, we’ll look at just a few higher-dimensional abstractions. Brad divides the Web UI into five separate parts: atoms, molecules, organisms, templates, and pages.
Our component library will be made up of atoms and molecules. Component libraries allow developers to quickly build entities, templates, and pages.
Source: bradfrost.com/wp-content/…
Atoms -> Elements
Atoms are the smallest indivisible units that make up the UI. Think buttons, links, input fields, etc. In this tutorial, we refer to them collectively as elements. Remember we created the button component and put it in the /elements directory? 💡
We will start writing our component library by creating elements. Once we have enough elements, we’ll add a molecule. I like to start with elements because it keeps our components from getting too complicated. Each element should be independent, and we should be able to create molecules by combining these elements. They should work like Lego bricks out of the box, without any electrical tools. When we start building molecules, if something doesn’t fit, then we need to go back to our elements and rethink their design.
Molecules -> Components
As mentioned above, molecules are simple and are different combinations of atoms. Next we will call these components. The word “component” comes naturally to the React world, but we’ll use it in a special way. An example is the search area, which consists of a label, an input box, and a button. Another example is a drop-down list, which consists of a button (which triggers the drop-down), a list, and list items.
Okay, but what isn’t a component?
Good question. Thank you for your question. A navigation bar is not a component by definition. A navigation bar is more like an entity. It is built out of many components and can change a lot as the content of its composition changes. A login form is an example of another entity. It might contain a title, a form entity label, input fields and buttons, a link to reset the password, and a div that contains all the elements.
Why not add entities to the component library?
Another good question. 🏆 Adding entities to the component library is no problem. But we should put these delusions aside when they arise. The purpose of a component library is to build components that are extensible and reusable. If we start by building many entities, we limit our users to following our particular schema. We are adding components so that users can quickly build a consistent UI. But if they need something different, or don’t like our particular pattern, they can easily go back to the elements and build their own pattern.
Keep in mind that there are situations where adding entities makes sense. For example, suppose you have three main applications for your business, and you want them all to have consistent login forms. Ok, so put the entity of the login form into your library.
Ok, so now we’ve covered a lot of the theory behind component libraries. Let’s get to the fun part and start building. 🛠
Add an effective development workflow
In the first part, we begin with “… It would be nice to have a local test environment to experiment with before we release to NPM.” The end. As we promised, that is what we will do now.
Configure our local environment
We need a local environment to try out our components. Fortunately, I have a small application for this environment. You’ll need to pull it down from GitHub and install the dependency.
$ git clone [email protected]:alanbsmith/component-lib-playground.git
$ cd component-lib-playground
$ npm install
Copy the code
If you are using Yarn, run Yarn.
If you run NPM run dev or YARN dev, open http://localhost:8080 in your browser and see “Hello, World!” That is great! If you go into the SRC/Components/app.js file and modify part of JSX, you will also notice that it is hot loaded with updates. This is a great point for testing our library. Next we will connect our application to the component library.
Connect to our component library
⚠️ Note: you may want to keep the component library open in one terminal window and the application open in another.
First we add the Build: Watch script to the component library’s package.json file. -w tells Babel to pay attention to any changes in the lib directory and update the contents of the build directory immediately.
"scripts": {
"build": "babel lib -d build",
"build:watch": "babel lib -w -d build",
"lint": "eslint lib/**; exit 0",
...
Copy the code
Now we will create a symlink to our library locally. If you want to know more about NPM Link, here’s some useful documentation. Run NPM Link at the root of our component library. You may have noticed that the console is running PrePublish.
We will now tell our test application which library we will use symlink. Run in the root directory of the application:
⚠️ Note: Remember to replace component-lib with your component library name.
npm link component-lib
Copy the code
That’s it! We are ready to use our component library in our test application!
Add buttons to the test application
In SRC/Components/app.js we’ll import and call our component like any other external library:
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from 'component-lib'
...
const App = ({ name }) => {
return (
<div>
<h1>Hello, {name}!</h1>
<Button>Click Me!</Button>
</div>
);
};
...
Copy the code
Once updated, the test application will hot load the update and the button will appear!
Added our first component to the test application!
💡 notes that our test application has no idea what styled components are. Anyone can use our library without having to install other support libraries. That’s why we put everything in devDependencies. ⚠️ note: An error occurred when running the test application without styled components and polished versions. Please check back and give comments.
Very good! Now let’s make some changes to Button. It would be great if the buttons were a different color. Maybe purple?
const Button = styled.button` background: #7E5BEF; . &:hover { background: #592DEA; } `;Copy the code
You will notice that when our lib directory was updated, the test application did not reflect the change. That’s because we need to run NPM run build again to update the build directory. It’s not the best thing to do. Having to manually rebuild after every file update is a bit silly and painful. Remember the build: Watch script we added earlier? Now we can use it.
$ npm run build:watch
Copy the code
Dong! Our component library is now automatically updated with every change! 🎊 When we do this, we split screen and run NPM run esLint :watch to immediately see if we have any syntax errors. Your terminal might look something like this:
Run the build:watch script and Lint :watch script on terminals.
Now that changes to our library are immediately reflected in the application, we can easily experiment.
Add a palette
The static color of the button limits this element a lot. It would be nice to have a palette where the user can select colors, which can be implemented by passing a property. Let’s add the new colors.js file to /lib/styles in the component library. In it we add the following:
module.exports = {
// light shades
white: '#FFFFFF',
snow: '#F9FAFC',
darkSnow: '#EFF2F7',
extraDarkSnow: '#E5E9F2',
// dark tones
silver: '#8492A6',
slate: '#3C4858',
steel: '#273444',
black: '#1F2D3D',
// dark shades
smoke: '#E0E6ED',
darkSmoke: '#D3DCE6',
extraDarkSmoke: '#C0CCDA',
// blue shades
lightBlue: '#85D7FF',
blue: '#1FB6FF',
darkBlue: '#009EEB',
// purple shades
lightPurple: '#A389F4',
purple: '#7E5BEF',
darkPurple: '#592DEA',
// pink shades
lightPink: '#FF7CE5',
pink: '#FF49DB',
darkPink: '#FF16D1',
// orange shades
lightOrange: '#FF9E7C',
orange: '#FF7849',
darkOrange: '#FF5216',
// green shades
lightGreen: '#29EB7F',
green: '#13CE66',
darkGreen: '#0F9F4F',
// yellow shades
lightYellow: '#FFD55F',
yellow: '#FFC82C',
darkYellow: '#F8B700',
// ui colors
info: '#1FB6FF',
success: '#13CE66',
danger: '#FF4949',
warning: '#FFC82C',
};
Copy the code
These colors are from Marvel’s StyleGuide. You’re welcome to use your own colors, but these are enough for our purposes.
Now we’ll update the button element to use these colors:
. import * as colors from '.. /styles/colors'; const Button = styled.button` background: ${({ bgColor }) => colors[bgColor]}; . color: ${({ fontColor }) => colors[fontColor]}; . &:hover { background: ${({ hoverColor }) => colors[hoverColor]}; } `; Button.defaultProps = { bgColor: 'blue', fontColor: 'white', hoverColor: 'darkBlue', }; export default Button;Copy the code
Very cool. Now our users can use the desired color from the palette in the bgColor, fontColor and hoverColor properties. We also added defaultProps to each color property, so if the user doesn’t pass any properties, things won’t be too bad.
Good! When our test application rerenders, the button should turn blue. Let’s add some properties and declare a new color. I’ll set it to orange here, but you can use any color you want from the palette.
const App = ({ name }) => { return ( <div> <h1>Hello, {name}! </h1> <Button bgColor="orange" hoverColor="darkOrange" > Click Me! </Button> </div> ); };Copy the code
Great! Nice! It’s nice for us users to be able to quickly update button colors without doing any configuration. Having to add a hoverColor is a bit annoying. But if we don’t do that, we’ll see the equally annoying darkBlue. B: Maybe Polished can help?
Add some Polish
Polished is a lightweight tool set that provides some handy Sass features. We already have it installed in the component library, so we just need to import it now. We will add the Darken function to replace the hoverColor attribute.
import styled from 'styled-components'; import { darken } from 'polished'; import * as colors from '.. /styles/colors'; const Button = styled.button` background: ${({ bgColor }) => colors[bgColor]}; . color: ${({ fontColor }) => colors[fontColor]}; . &:hover {background: ${({bgColor}) => darken(0.1, colors[bgColor])}; } `; Button.defaultProps = { bgColor: 'blue', fontColor: 'white', };Copy the code
Note that we have completely removed the hoverColor attribute. Now we can use Darken and bgColor to get our new hover background color. 0.1 is how much we darken the color. We can also remove the hoverColor attribute.
. <Button bgColor="orange"> Click Me! </Button> ...Copy the code
Good! This will make our components much easier to use! Please note again that your test application does not know what is available.
Wrapping Up
⚠️ Note: We’d better save our project and commit.
$ git status
$ git add -A
$ git commit -m 'adds color palette and dynamic styling for Button'
Copy the code
We now have an efficient workflow, a great color palette, and dynamic styling for our button elements. In Part 3 we will continue to add other elements to our library.
I hope this is helpful. If you like it, please let me know! Welcome to share if you think this is helpful to others!