Pro Yearly is on sale from $80 to $50! »

Engineering a Design System

Engineering a Design System

While plenty is said about design system theory, organizational adoption, and visual design, you won’t get very far without solid engineering practices, sound architecture, and the right technologies.

We’ll take a tour through the engineering of TELUS digital’s React-based design system developed over the past year, examining technologies such as CSS Modules; testing practices; architectural decision points; and strategies for scaling to dozens of teams and hundreds of users.

Whether you are at a small or large scale, just starting or far along with a design system, you can immediately apply the lessons and technologies from this talk to your own work.

Fd8e3ace64d471302758efb64e1eb0aa?s=128

Ryan Oglesby

April 24, 2018
Tweet

Transcript

  1. Engineering a Design System

  2. Design systems Style guide?
 Component library?
 UI toolkit? }close enough

  3. Make designers and developers more produc<ve
 
 Consistent end user

    experience Why?
  4. h"ps://www.telus.com | Design System

  5. | Design System React Sketch

  6. Engineering for stability by crea(ng inten(onal APIs and
 tes(ng first

    and o5en. Engineering for scale ↔ by automa(ng documenta(on and op(mizing package distribu(on.
  7. 
 // Button.scss .button { padding: 1rem; border-radius: 4px; color:

    white; } .button--primary { background-color: $color-primary; } .button--secondary { background-color: $color-secondary; } 
 <Button className="button--primary"> Shop now </Button> <Button className="button--secondary"> Learn more </Button> class Button extends React.Component { render() { // a branded HTML button } } Design system Applica8on Output
  8. 
 // Button.scss .button { padding: 1rem; border-radius: 4px; color:

    white; } .button--primary { background-color: $color-primary; } .button--secondary { background-color: $color-secondary; } 
 // MySquareButton.scss 
 .button { border-radius: 0; color: black; } .my-square-button { background-color: hotpink; } 
 <Button className=“my-square-button"> Find a store </Button> class Button extends React.Component { render() { // a branded HTML button } } Design system Applica8on 
 // Button.scss .button { padding: 1rem; border-radius: 4px; color: white; } .button--primary { background-color: $color-primary; } .button--secondary { background-color: $color-secondary; } Output
  9. h"ps://github.com/css-modules/css-modules

  10. // Button.scss .button { // core button styles } .primary

    { composes: button; background-color: $color-primary; } .secondary { composes: button; background-color: $color-secondary; } import styles from './Button.scss' class Button extends React.Component { render() { return ( <button className={styles[this.props.variant]}> {this.props.children} </button> ) } }
  11. // Button.scss .button { // core button styles } .primary

    { composes: button; background-color: $color-primary; } .secondary { composes: button; background-color: $color-secondary; } import styles from './Button.scss' class Button extends React.Component { render() { return ( <button className={styles[this.props.variant]}> {this.props.children} </button> ) } } // compiled Button.css .TDS_Button__button___2kyCB { ... } 
 .TDS_Button__primary___3Xb72 { ... } .TDS_Button__secondary___2qhCP { ... }
  12. 
 <Button variant="primary"> Shop now </Button> <Button variant="secondary"> Learn more

    </Button>
  13. Accessibility Tes8ng

  14. 
 $ yarn test:e2e @tds/core-button yarn run v1.5.1 $ node

    scripts/e2e.js Started chromedriver [Components Spec] Test Suite ================================ Running: @tds/core-button ✔ Element <#button> was visible after 31 milliseconds. ✔ Passed [ok]: 9 aXe Tests Passed ✖ Failed [fail]: (Elements must have sufficient color contrast [<div class="TDS_Flexbox-modules__row___DL6tZ TDS_BaseButton-modules__centered___3Xkio TDS_Borders-modules__rounded___1Qg-s">Submit</div>]) - expected "https:// dequeuniversity.com/rules/axe/2.6/color-contrast?application=axeAPI" but got: "policy violation" at endReadableNT (_stream_readable.js:1101:12) ✖ #app passes accessibility scan - expected "true" but got: "false" at Object.checkAccessibility (/Users/ryanoglesby/Projects/telus-digital/tds/ e2e/commands/checkAccessibility.js:4:35) FAILED: 2 assertions failed and 2 passed (1.145s)

  15. h"ps://dequeuniversity.com/rules/axe/2.6/color-contrast

  16. Accessibility Tes8ng h"p://nightwatchjs.org h"ps://github.com/ahmadnassri/nightwatch-accessibility

  17. Visual Regression Tes8ng

  18. $ yarn test:e2e @tds/core-notification yarn run v1.5.1 $ node scripts/e2e.js

    Started chromedriver [Components Spec] Test Suite ================================ Running: @tds/core-notification ✖ Screenshots Match Failed for chrome_headless.png with a tolerance of 0%, actual was 1.07%. - expected "0" but got: "1.07" at Object.takeScreenshot (/Users/ryanoglesby/Projects/telus-digital/tds/e2e/ commands/compareScreenshot.js:55:17) at Object.browser.saveScreenshot (/Users/ryanoglesby/Projects/telus-digital/ tds/e2e/commands/compareScreenshot.js:38:16) at FSReqWrap.oncomplete (fs.js:153:20) FAILED: 1 assertions failed and 3 passed (3.314s) _________________________________________________ TEST FAILURE: 1 assertions failed, 3 passed. (3.378s)
  19. Visual Regression Tes8ng

  20. Visual Regression Tes8ng

  21. Visual Regression Tes8ng

  22. Visual Regression Tes8ng h"ps://github.com/HuddleEng/Resemble.js

  23. Engineering for stability by crea(ng inten(onal APIs and
 tes(ng first

    and o5en. Engineering for scale ↔ by automa(ng documenta(on and op(mizing package distribu(on.
  24. • Correct and trustworthy • Comprehensive • Component APIs (props,

    data types, allowed values) • Live examples Documenta8on must-haves React Styleguidist Storybook
  25. /** * A standard, branded clickable element for use in

    forms mainly. * @version 1.0.2 */ class Button extends React.Component { render() { // ... } } Button.propTypes = { /** * The HTML button type. * @see See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/ Element/button) for more info. */ type: PropTypes.oneOf(['button', 'submit', 'reset']), /** * The style. */ variant: PropTypes.oneOf(['primary', 'secondary', 'inverted']), /** * The label. */ children: PropTypes.string.isRequired, } Button.defaultProps = { type: 'button', variant: 'primary', }
  26. Provide a function as the `onClick` prop to perform an

    action when clicked. **Avoid using a button if navigation is the primary action, as a [Link](#link) is more appropriate.** ### Best practices * Aim to use only one primary button per page * Keep the label short and able to fit on a single line * The label should clearly describe the action By default, Buttons will be displayed in the `primary` variant. Use primary buttons for the main action on a page or in a form. ``` <Button>Submit</Button> ``` Specify the `variant` to create a button for secondary actions. ``` <Button variant=“secondary”>Find out more</Button> ```
  27. h"ps://tds.telus.com/components/index.html

  28. h"ps://www.npmjs.com/package/@telusdigital/tds

  29. import { Button } from '@telusdigital/tds' import '@telusdigital/tds/dist/index.css' class HelloWorldApp

    extends React.Component { render() { return ( <Button onClick={this.props.sayHello}>Hello world</Button> ); } } export default HelloWorldApp 
 { "name": "@telusdigital/hello-world-app", "version": “0.1.0", "description": "An app that says hello", "dependencies": { "@telusdigital/tds": "^1.0.0" } }
  30. import { Button } from '@telusdigital/tds' import '@telusdigital/tds/dist/index.css' class HelloWorldApp

    extends React.Component { render() { return ( <Button onClick={this.props.sayHello}>Hello world</Button> ); } } export default HelloWorldApp 
 { "name": "@telusdigital/hello-world-app", "version": “0.1.0", "description": "An app that says hello", "dependencies": { "@telusdigital/tds": "^1.0.0" } } “^2.0.0" .TDS_Button__primary___3Xb72 { /* ... */ } .TDS_Button__secondary___2qhCP { /* ... */ } .TDS_Text__base___2nt0g { /* ... */ } .TDS_Box__spacing2___3Z6ro { /* ... */ }
  31. @tds/box @tds/buDon @tds/buDon-link @tds/card @tds/checkbox @tds/chevron-link @tds/colours @tds/css-reset @tds/decora8ve-icon h"ps://lernajs.io

  32. h"ps://www.npmjs.com/org/tds

  33. • Consolidate the lint, build, test, and release process •

    Easier to coordinate changes across mul<ple components • Single place to report issues • Easier to generate documenta<on Why a monorepo?
  34. import Box from '@tds/core-box' import Button from '@tds/core-button' class HelloWorldApp

    extends React.Component { render() { return ( <Box inset={3}> <Button onClick={this.props.sayHello}>Hello world</Button> </Box> ); } } export default HelloWorldApp 
 { "name": "@telusdigital/hello-world-app", "version": “0.1.0", "description": "An app that says hello", "dependencies": { “@tds/core-box": “^1.0.2”, “@tds/core-button": "^1.1.0" } }
  35. h"ps://tds.telus.com h"ps://github.com/telus/tds-core It’s open source

  36. React CSS Modules ✅ jest + enzyme + enzyme-matchers nightwatch

    + nightwatch-accessibility (axe-core) resemble-js react-styleguidist lerna Key technology
  37. <ThankYou /> Ryan Oglesby @ryanoglesby08 hOps:/ /ryanogles.by Engineering a Design

    System