Building Design Systems With React

@emmawedekind

Hello World

class Emma extends Component {
    state = {
        name: 'Emma Wedekind',
        location:'Karlsruhe, Germany',
        job:'UX Engineer @ LogMeIn',
        hobbies: [
            'Coding Coach Founder',
            'JS Party Panelist',
            'Egghead.io Instructor'
        ]
    }
}

Breathing is hard

Picture your  dream home

But this is a talk about React... why do we care about architecture?

🤔

We don't

We all think about the world in different ways...

which leads to inconsistent user interfaces.

Design Systems

Design Systems

Provide the building blocks from which we construct our applications.

Users should not have to wonder whether different words, situations, or actions mean the same thing.

Jakob Nielsen's Heuristics

DESIGN SYSTEMS

Give designers & engineers the ability to build consistent user interfaces without wasting a ton of time and energy.

Okay... but you still haven't told us what a Design System is.

🙄

DESIGN LANGUAGE

A set of standards which guide the creation of a suite of products underneath a brand.

COMPONENT LIBRARY

A repository of standalone, coded components which turn the design language into the building blocks of an application.

STYLE GUIDE

The documentation for the design language and the component library.

Component Libraries

WHAT MAKES A COMPONENT LIBRARY GREAT?

Accessibile

Easy to install & use

Uses modern, but adopted technologies

Responsiveness

Customization

Flexibility

DETERMINE

DESIGN

DOCUMENT

DEVELOP

DEPLOY

5-D PRIORITIZATION METHOD

UI AUDIT

Take an inventory of all components, in all products, in all states.

CHOOSE YOUR STACK

You must meet developers where they're at.

Don't start by building for scale.

Let's build a button

PROPERTIES

TYPE

SIZE

ICON

THEME

LABEL

CLICK HANDLER

export const ButtonTypes = Object.freeze({
  PRIMARY: "primary",
  SECONDARY: "secondary",
  TERTIARY: "tertiary"
});

export const ButtonSizes = Object.freeze({
  SMALL: "small",
  MEDIUM: "medium",
  LARGE: "large"
});

export const ButtonThemes = Object.freeze({
  LIGHT: "light",
  DARK: "dark"
});
buttonTypes.js
// Button.jsx

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { ButtonTypes, ButtonSizes, ButtonThemes } from './buttonTypes';
import './button.css'

export default class Button extends Component {

    ...

    render() {
        const { disabled, onClickHandler, label } = this.props;
        return (
            <button
                className={this.getButtonClasses()}
                onClick={event => onClickHandler(event)}
                disabled={disabled}
            >
            {label}
            </button>
        );
    }
}
// Button.jsx

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { ButtonTypes, ButtonSizes, ButtonThemes } from './buttonTypes';
import './button.css'

export default class Button extends Component {
    getButtonClasses() {
        const { icon, size, theme, type } = this.props;
        const buttonClasses = [
            'button',
            `button--${size}`,
            `button--${theme}`,
            `button--${type}`
        ];

        return buttonClasses.join(' ');
    }


    render() {

        ...

        );
    }
}
// Icons.jsx

import React, { Component } from "react";
import PropTypes from "prop-types";
import { IconTypes } from "./iconTypes";

const SaveIcon = () => {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      width="24"
      height="24"
      viewBox="0 0 24 24"
      className='button__icon'
    >
      <title>Save</title>
      ...
    </svg>
  );
};

export default class Icon extends Component {
  render() {
    ...
  }
}
// Icons.jsx
import React, { Component } from "react";
import PropTypes from "prop-types";
import { IconTypes } from "./iconTypes";

const SaveIcon = () => {
  ...
};

export default class Icon extends Component {
  render() {
    const { icon } = this.props;
    switch (icon) {
      case IconTypes.SAVE:
        return <SaveIcon />;

      default:
        return null;
    }
  }
}

Icon.propTypes = {
  icon: PropTypes.oneOf(Object.values(IconTypes))
};

Icon.defaultProps = {
  icon: IconTypes.NONE
};
// Button.jsx

...
import { IconTypes } from "./iconTypes";
import Icon from "./Icons";


export default class Button extends Component {
  getButtonClasses() {
    const { icon, size, theme, type } = this.props;
    const buttonClasses = [
      "button",
      `button--${size}`,
      `button--${theme}`,
      `button--${type}`
    ];

    icon && icon !== IconTypes.NONE && buttonClasses.push("button--icon");

    return buttonClasses.join(" ");
  }

  ...

}
// Button.jsx

...

export default class Button extends Component {
  getButtonClasses() {
    const { icon, size, theme, type } = this.props;
    ...
  }

  render() {
    const { disabled, onClickHandler, label, icon } = this.props;
    return (
      <button
        className={this.getButtonClasses()}
        onClick={event => onClickHandler(event.target)}
        disabled={disabled}
      >
        {icon && <Icon icon={icon} />}
        {label}
      </button>
    );
  }

}
// Button.jsx

...

Button.propTypes = {
  type: PropTypes.oneOf(Object.values(ButtonTypes)),
  disabled: PropTypes.bool,
  onClickHandler: PropTypes.func.isRequired,
  label: PropTypes.string.isRequired,
  size: PropTypes.oneOf(Object.values(ButtonSizes)),
  theme: PropTypes.oneOf(Object.values(ButtonThemes)),
  icon: PropTypes.oneOf(Object.values(IconTypes))
};

Button.defaultProps = {
  type: ButtonTypes.PRIMARY,
  onClickHandler: () => console.log("No click handler specified"),
  label: "",
  disabled: false,
  size: ButtonSizes.MEDIUM,
  theme: ButtonThemes.LIGHT,
  icon: IconTypes.NONE
};
<Button
  size={ButtonSizes.MEDIUM}
  label="Button"
  onClickHandler={() => alert("you clicked!")}
  type={ButtonTypes.PRIMARY}
/>

<Button
  size={ButtonSizes.SMALL}
  label="Button"
  onClickHandler={() => alert("you clicked!")}
  type={ButtonTypes.PRIMARY}
/>

<Button
  size={ButtonSizes.LARGE}
  label="Button"
  theme={ButtonThemes.DARK}
  onClickHandler={() => alert("you clicked!")}
  type={ButtonTypes.PRIMARY}
/>

Documentation

GOOD

DOCUMENTATION

Installation & setup instructions

GOOD

DOCUMENTATION

Installation & setup instructions

Property schema

GOOD

DOCUMENTATION

Installation & setup instructions

Property schema

Type definition

GOOD

DOCUMENTATION

Installation & setup instructions

Property schema

Type definition

Technology choices & rationale

GOOD

DOCUMENTATION

Installation & setup instructions

Property schema

Type definition

Technology choices & rationale

Examples with code snippets

Takeaways

NATHAN CURTIS

"A design system isn't a project; it's a product serving products."

NATHAN CURTIS

"The system's promise isn't a delivered library...

its promise is to enable consistent experience spread across products."

So even though we all think about the world in different ways...

Design systems allow us to create consistent and accessible interfaces, cross-product.

Thank you

@emmawedekind

Grab my code =>

Building Design Systems With React

By Emma Wedekind

Building Design Systems With React

  • 6,702