ENGINEERINGΒ + DESIGN

design

systems

@RiaCarmin

πŸ”₯πŸ”₯πŸ”₯

VERY

By understanding recurrent design problems in our environment, readers can identify extant patterns in their own design projects and use these patterns to create a language of their own.

β€œ

@RiaCarmin

trello.com/b/

pIVAuqKc

p - i - v - a - u - q - k - c

THE

why’s

🎨 + πŸ’»

…

greater coherence between design and engineering

#1

Β 

livable code

β€” Sarah Mei @sarahmei

makes it easy to bring people on to your project, makes building your product fun

#2

Β 

πŸ’

HAVE STANDARDS

consistent user interface​

#3

Β 

THE

what's

THE

architecture

fluent / Microsoft

carbon / IBM

lightning / Salesforce

polaris / Shopify

material / Google

THE

code

01


implement the brand guidelines in a way that ensures consistency

02

Β 

build in some of the rules of graphic design

.
.
.
.
└── styles
    β”œβ”€β”€ base // Constants
    β”‚   β”œβ”€β”€ _base.scss
    β”‚   β”œβ”€β”€ _colors.scss
    β”‚   β”œβ”€β”€ _layout.scss
    β”‚   β”œβ”€β”€ _line.scss
    β”‚   β”œβ”€β”€ _motion.scss
    β”‚   β”œβ”€β”€ _plane.scss
    β”‚   β”œβ”€β”€ _spacing.scss
    β”‚   └── _typography.scss
    β”œβ”€β”€ global // Global resets and styles
    β”‚   β”œβ”€β”€ _global.scss
    β”‚   β”œβ”€β”€ _links.scss
    β”‚   β”œβ”€β”€ _lists.scss
    β”‚   β”œβ”€β”€ _normalize.scss
    β”‚   β”œβ”€β”€ …
    β”œβ”€β”€ index.scss // Manifest
    β”œβ”€β”€ modules // Style blocks
    β”‚   β”œβ”€β”€ _buttons.scss
    β”‚   β”œβ”€β”€ _errors.scss
    β”‚   β”œβ”€β”€ _layout.scss
    β”‚   └── _typography.scss
    └── utilities // Functions and Mixins
        β”œβ”€β”€ _font-size.scss
        β”œβ”€β”€ _plane-level.scss
        β”œβ”€β”€ _space.scss
        └── _utilities.scss
// In Sass import order matters

@import "base/base";
@import "utilities/utilities";
@import "global/global";
@import "modules/modules";
styles/index.scss
// =============================================================================
// Colors
// =============================================================================

// Theme Colors
// …

// Grey Colors
// …

// UI Colors
$ui-colors: (
  action: $color-primary,
  success : #42f4a1,
  warning : #ffcc68,
  error : #f44141,
  info : #7caeff,
);

$color-action: map-get($ui-colors, action);
$color-success : map-get($ui-colors, success);
$color-warning : map-get($ui-colors, warning);
$color-error : map-get($ui-colors, error);
$color-info : map-get($ui-colors, info);

// Brand Colors
// …
styles/base/_colors.scss
// =============================================================================
// Typography
// =============================================================================

// Montserrat β€” Bold 700
// Inconsolata β€” Regular 400
// Inconsolata β€” Bold 700

$heading-font-family: "Montserrat", serif;
$base-font-family: "Inconsolata", monospace;

$font-weight-regular: 400;
$font-weight-bold: 700;

// Font Colors
$base-font-color: $color-x-dark-grey;
$action-color: $color-primary;
styles/base/_typography.scss
// =============================================================================
// BASE
// =============================================================================

@import 'colors';
@import 'layout';
@import 'line';
@import 'motion';
@import 'plane';
@import 'spacing';
@import 'typography';
styles/base/base.scss
// Calculate font-size to line-height ratio (1) Typographic rules
 @mixin font-size($factor: 1) {
    $init: 4;
    $size: $factor + $init;
    $rhythm: $step / 2;
    $font-ratio: 0.75;

    font-size: $size * $rhythm * $font-ratio / 10rem;
    line-height: $size * $rhythm / 10rem;
}

// Add visual hierarchy level styles. (3) Visual hierarchy
@mixin plane-level($n: 1) {
    @if $n == 1 { box-shadow: $box-shadow-0; }
    @if $n == 2 { box-shadow: $box-shadow-2; }
    @if $n == 3 { box-shadow: $box-shadow-4; }
}

// Calculate step-based spacing in rem. (2) Layout grid and alignment
@function space($n) {
    @return $n * $step + 0rem; // πŸ‘ˆ Uses the Base step.
}

// Add truncate styles to an inline element. (4) Common style helpers
@mixin truncate($truncation-boundary) {
  max-width: $truncation-boundary;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
styles/utilities/[...].scss
// =============================================================================
// GLOBAL
// =============================================================================

@import 'normalize';

* {
  box-sizing: border-box;
}

html {
  height: 100%;
  width: 100%;
  font-size: 62.5%;
  background-color: $base-background-color;
}

body {
…
}

@import 'buttons';
@import 'code';
@import 'forms';
@import 'links';
@import 'lists';
@import 'spacing';
@import 'typography';
styles/global/global.scss
// =============================================================================
// Links
// =============================================================================

a {
    color: $color-action;
    transition: all $base-duration $base-timing;
}

a:hover {
    color: $color-action;
}

// =============================================================================
// Lists
// =============================================================================

ol, ul {
    margin: spacing(2) 0 spacing(2) spacing(3);
    font-size: font-size(1);
}
li {
    margin-bottom: spacing(2);
}
styles/global/_[…].scss
// =============================================================================
// Links
// =============================================================================

a {
    color: $color-action; // πŸ‘ˆ (1) Brand constant
    transition: all $base-duration $base-timing; // πŸ‘ˆ (1) Brand constant
}

a:hover {
    color: $color-action; // πŸ‘ˆ (1) Brand constant
}

// =============================================================================
// Lists
// =============================================================================

ol, ul {
    margin: spacing(2) 0 spacing(2) spacing(3); // πŸ‘ˆ (2) Gr. Design mixin
    font-size: font-size(1); // πŸ‘ˆ (2) Gr. Design mixin
}
li {
    margin-bottom: spacing(2); // πŸ‘ˆ (2) Gr. Design mixin
}
styles/global/_[…].scss
// =============================================================================
// Blocks: Buttons
// =============================================================================

%button {
    display: inline-block;
    height: spacing(4);
    padding: 0 30px;
    text-align: center;
    font-size: font-size(1);
    font-weight: $font-weight-bold;
    line-height: spacing(4);
    letter-spacing: 0.2rem;
    text-transform: uppercase;
    text-decoration: none;
    white-space: nowrap;
    background-color: transparent;
    border-radius: $base-border-radius;
    border: $base-border;
    cursor: pointer;
    box-sizing: border-box;
    transition: all $base-duration $base-timing;
}
styles/modules/_buttons.scss

// =============================================================================
// Blocks: Layout
// =============================================================================

%base-view {
    min-height: 100%;
    width: 100%;
    padding-top: $header-height; // πŸ‘ˆ (1) Brand constant
}

%base-padding {
    padding-left: spacing(2);
    padding-right: spacing(2);
}

%desktop-view {
    @media screen and (min-width: $breakpoint-mobile) { // πŸ‘ˆ (1) Brand constant
        @include plane-level(1); // πŸ‘ˆ (2) Gr. Design mixin
        max-width: $breakpoint-mobile; // πŸ‘ˆ (1) Brand constant
        border-radius: $base-border-radius; // πŸ‘ˆ (1) Brand constant
        background: $color-white; // πŸ‘ˆ (1) Brand constant
    }
}

%card {
    @include plane-level(2); // πŸ‘ˆ (2) Gr. Design mixin
    border-radius: $base-border-radius; // πŸ‘ˆ (1) Brand constant
}
styles/modules/_layout.scss
// =============================================================================
// Blocks: Typography
// =============================================================================

%sub-heading {
    font-weight: $font-weight-bold; // πŸ‘ˆ (1) Brand constant
    text-transform: uppercase;
    letter-spacing: 0.2rem;
    @include font-size(2); // πŸ‘ˆ (2) Gr. Design mixin
    @each $theme, $color in $grey-colors { // πŸ‘ˆ (3) Generating modifiers from maps
        &--#{$theme} {
            color: $color;
        }
    }
}

.example-heading {
    @extend %sub-heading;
    margin: space(2) 0;
}

// .example-heading .example-heading--white
// .example-heading .example-heading--light
// .example-heading .example-heading--medium
// …
styles/modules/_typography.scss
.
.
.
.
└── styles
    β”œβ”€β”€ base // Constants
    β”‚   β”œβ”€β”€ _base.scss
    β”‚   β”œβ”€β”€ _colors.scss
    β”‚   β”œβ”€β”€ _layout.scss
    β”‚   β”œβ”€β”€ _line.scss
    β”‚   β”œβ”€β”€ _motion.scss
    β”‚   β”œβ”€β”€ _plane.scss
    β”‚   β”œβ”€β”€ _spacing.scss
    β”‚   └── _typography.scss
    β”œβ”€β”€ global // Global resets and styles
    β”‚   β”œβ”€β”€ _global.scss
    β”‚   β”œβ”€β”€ _links.scss
    β”‚   β”œβ”€β”€ _lists.scss
    β”‚   β”œβ”€β”€ _normalize.scss
    β”‚   β”œβ”€β”€ …
    β”œβ”€β”€ index.scss // Manifest
    β”œβ”€β”€ modules // Style blocks
    β”‚   β”œβ”€β”€ _buttons.scss
    β”‚   β”œβ”€β”€ _errors.scss
    β”‚   β”œβ”€β”€ _layout.scss
    β”‚   └── _typography.scss
    └── utilities // Functions and Mixins
        β”œβ”€β”€ _font-size.scss
        β”œβ”€β”€ _plane-level.scss
        β”œβ”€β”€ _space.scss
        └── _utilities.scss
.
.
.
.
β”œβ”€β”€ components
β”‚   β”œβ”€β”€ avatar
β”‚   β”‚   β”œβ”€β”€ index.js
β”‚   β”‚   └── style.scss
β”‚   β”œβ”€β”€ button
β”‚   β”‚   β”œβ”€β”€ index.js
β”‚   β”‚   └── style.scss
β”‚   └── message
β”‚       β”œβ”€β”€ index.js
β”‚       └── style.scss
@import 'Base'; // (1) Resolve style constants and placeholder classes
@import 'Utilities'; // (1) Resolve utilities

// (2) Block styles
.avatar {
  border-radius: 50%;
  height: spacing(5); // πŸ‘ˆ (2) Gr. Design mixin
  width: spacing(5); // πŸ‘ˆ (2) Gr. Design mixin
  vertical-align: -64%;

  // (3) Modifier styles
  // .avatar .isOnline
  & .isOnline { // https://smacss.com/book/type-state
    border: 0.2rem solid $color-success; // πŸ‘ˆ (1) Brand constant
  }
}
components/avatar/style.scss
import { h } from 'preact';
import PropTypes from 'prop-types';
import { CLOUDINARY } from '../../config';

import style from './style'; // (1) Import styles
import { block } from 'bem-cn'; // (2) BEM class names generator

function Avatar({online, size = 40, user}) {
  
  const avatarClassNames = block(style.avatar); // (3) Create block
  avatarClassNames.is({[style.isOnline]: online}) // (4) Add Modifier

  const imgSrc = `${CLOUDINARY}/image/fetch/w_${size},h_${size},c_fill/${ user.avatar}`;
  return <img src={imgSrc} class={avatarClassNames)} />;
}

Avatar.defaultProps = {
  online: false
};

Avatar.propTypes = {
  user: PropTypes.shape({
    username: PropTypes.string.isRequired,
    avatar: PropTypes.string,
    size: PropTypes.number
  })
};

export default Avatar;
components/avatar/index.js
@import 'Base'; // (1) Resolve style constants and placeholder classes

// (2) Block styles
.message {
  padding: 1rem;
  border-bottom: $base-border;
  width: 100%;
  text-decoration: none;
  display: block;
  figure {
    display: inline-block;
  }

  // (3) Modifier classes
  // .message .message--unread
  &--unread { // BEM
      background-color: $color-accent;
  }
}
components/message/style.scss
import { h } from 'preact';
import PropTypes from 'prop-types';

import Link from 'react-router-dom/Link';
import UserChip from '../userchip';

import style from './style'; // (1) Import styles
import { block } from 'bem-cn'; // (2) BEM class names generator
const block = setup({
    mod: '--', // (optional) Traditional BEM syntax
});

function Message({ id, user, count, unread }) {

    const messageClassNames = block(style.message); // (3) Create block
    messageClassNames({ unread }) // (4) Add modifier

    return (
      <Link class={messageClassNames} to={`/messages/${id}`}>
        <UserChip user={user} />
        &ensp;|&ensp;
        <span class="font__accent">{count}</span>
        &ensp;messages
      </Link>
    );
}
Message.propTypes = {
    …
};

export default Message;
components/message/index.js
@import 'Base'; // (1) Resolve style constants
                // and placeholder classes

.button {
    // (2) Extend button styles
    @extend %button; // πŸ‘ˆ

    // (3) Generate modifier styles
    @each $theme, $color in $ui-colors { // πŸ‘ˆ
        &--#{$theme} {
            background-color: $color;
            color: transparentize($white, .2);
        }
        &--#{$theme}:hover,
        &--#{$theme}:focus {
            background-color: shade($color, 20%);
            color: $white;
        }
    }
}
styles/base/_colors.scss
// UI Colors
$ui-colors: (
  action: $color-primary,
  success : #42f4a1,
  warning : #ffcc68,
  error : #f44141,
  info : #7caeff,
);
components/button/style.scss
import { h } from 'preact';
import PropTypes from 'prop-types';

import style from './style'; // (1) Import styles
import { block } from 'bem-cn'; // (2) BEM class names generator
const block = setup({
    mod: '--', // (optional) Traditional BEM syntax
});

function Button({ children, ...rest }) {
    const buttonClassNames = block(style.message); // (3) Create block
    buttonClassNames(rest) // (4) Add modifiers

    return (
      <button className={buttonClassNames}>
        {children}
      </button>
    );
}

Button.propTypes = {
    action: PropTypes.bool,
    success: PropTypes.bool,
    warning: PropTypes.bool,
    error: PropTypes.bool,
    info: PropTypes.bool,
};

export default Button;
components/button/index.js





const MyComponent = () => (
    <div>
        <Button action>Action</Button>
        <Button success>Success</Button>
        <Button warning>Warning</Button>
        <Button error>Error</Button>
        <Button info>Info</Button>
    </div>
);
components/my-component/index.js

css({
  color: 'darkorchid',
  backgroundColor: 'lightgray'
})

styled.div({
  color: 'darkorchid',
  backgroundColor: 'lightgray'
})

css`
  color: 'darkorchid';
  background-color: 'hotpink';
`

styled(MyComponent)`
  color: 'darkorchid';
  background-color: 'hotpink';
`
.
.
β”œβ”€β”€ src
β”‚   β”œβ”€β”€ components
β”‚   β”‚   β”œβ”€β”€ …
β”‚   β”œβ”€β”€ elements  // (4) β€œAtoms”
β”‚   β”‚   β”œβ”€β”€ Box.js
β”‚   β”‚   β”œβ”€β”€ Box.md
β”‚   β”‚   β”œβ”€β”€ Box.story.js
β”‚   β”‚   β”œβ”€β”€ Button.js
β”‚   β”‚   β”œβ”€β”€ Button.md
β”‚   β”‚   β”œβ”€β”€ Button.story.js
β”‚   β”‚   β”œβ”€β”€ …
β”‚   β”œβ”€β”€ global // (3) Global styles
β”‚   β”‚   └── index.js
β”‚   β”œβ”€β”€ styles // (1) Constants, functions and style blocks
β”‚   β”‚   β”œβ”€β”€ Colors.js
β”‚   β”‚   β”œβ”€β”€ Colors.story.js
β”‚   β”‚   β”œβ”€β”€ Layout.js
β”‚   β”‚   β”œβ”€β”€ Line.js
β”‚   β”‚   β”œβ”€β”€ Motion.js
β”‚   β”‚   β”œβ”€β”€ Plane.js
β”‚   β”‚   β”œβ”€β”€ Scale.js
β”‚   β”‚   β”œβ”€β”€ Typography.js
β”‚   β”‚   └── Typography.story.js
β”‚   └── utilities // (2) Helpers
β”‚       └── styleguide.js
β”‚       └── …
styles/index.scss
// =============================================================================
// Colors
// =============================================================================

// 1. Functions // =============================================================

export const calcGrey = value => `hsl(230, 20%, ${value}%)`

// 2. Constants // =============================================================

export const ui = {
    alert: '#f19066',
    default: '#2e86de',
    error: '#e66767',
    info: '#54a0ff',
    success: '#1dd1a1'
}

export const grey = {
    black: '#000000',
    dark: calcGrey(30),
    medium: calcGrey(60),
    light: calcGrey(98),
    white: '#FFFFFF'
}

// …

// 3. Style Blocks // ==========================================================
styles/Colors.js
import React from 'react'
import { storiesOf } from '@storybook/react'

import Colors from 'Styles/Colors'

storiesOf('Styles/Colors', module)
    .add('UI Colors', () => (
        <Styleguide>
            <Styleguide.Title>UI Colors</Styleguide.Title>
            <Styleguide.Grid>
                <Swatches scheme={Colors.ui} />
            </Styleguide.Grid>
        </Styleguide>
    ))
styles/Colors.story.js
// Typography
// =============================================================================

// Constants // ================================================================
…

// Functions // ================================================================
…

// Style Blocks // =============================================================
export const paragraph = css(
    {
        fontFamily: TEXT_FONT
    },
    calcFontSize(2)
)

export const title = css(
    {
        fontFamily: HEADING_FONT,
        fontWeight: FONT_WEIGHT_REGULAR
    },
    calcFontSize(8)
)

export const caption = css(
    {
        fontFamily: TEXT_FONT,
        letterSpacing: '0.025rem'
    },
    calcFontSize(1)
)
styles/Typography.js






storiesOf('Styles/Typography', module)    
    .add('Body', () => {
        const Paragraph = styled.p(Typography.paragraph) // πŸ‘ˆ
        const Caption = styled.p(Typography.caption)  // πŸ‘ˆ

        return (
            <Styleguide>
                <Styleguide.Title>body typography</Styleguide.Title>
                <Paragraph>
                    Lorem ipsum dolor sit amet, ea nominati salutatus ius, vix
                    …
                </Paragraph>
                <Caption>
                    Lorem ipsum dolor sit amet, ea nominati salutatus ius, vix
                    …
                </Caption>
            </Styleguide>
        )
    })
styles/Typography.story.js






        const Factor = styled(
            ({ className, factor }) => (
                <p className={className}>Size factor of {factor}.</p>
            ))
            ((props) => calcFontSize(props.factor), {
                margin: `0 0 ${calcSpace(2)} 0`
            })


        <Factor factor={factor} />
styles/Typography.story.js
import { injectGlobal } from 'emotion'
import Colors from 'Styles/Colors'
import { BASE_SCALE } from 'Styles/Scale'
import Typography, { TEXT_FONT, calcFontSize } from 'Styles/Typography'
import { calcSpace } from 'Styles/Layout'

// πŸ‘‡
injectGlobal` 
  * {
    box-sizing: border-box;
  }

  html {
    line-height: 1.15;
    -webkit-text-size-adjust: 100%;
    font-size: ${BASE_SCALE};
    font-family: ${TEXT_FONT};
    color: ${Colors.grey.dark};
  }
  
  html,
  body {
    height: 100%;
    position: relative;
    background-color: ${Colors.grey.light};
    margin: 0;
    padding: 0;
  }

…

`
styles/global/index.js

fin.

@RiaCarmin

@RiaCarmin

trello.com/b/

pIVAuqKc

p - i - v - a - u - q - k - c

Made with Slides.com