Everyone Loves Style Guides

Lightning Design System

Mail Chimp

Lonely Planet

M

NYC Transit Authority Graphics Standards Manual

Graphics Standards Manual: 1970

N

Numbers

Sign Construction and Engineering

Typical Sign Details:

Construction & Engineering

Type “D” Hanging Sign

I See How You Designed It

Now, How do I Build It?

Consistent Design

Requires

Consistent Implementation

Context Matters

One Size Fits No One

Team Size & Structure?

Internal vs. Consulting?

Is it 1970?

Similar Problems, Unique Constraints

your mileage will vary

Systems for System-Building

Consistent Systems

Custom Patterns

“Tiny Bootstraps, for Every Client

— Dave Rupert

Our Product is the Architecture

a solid foundation for hand-off

Quality Architecture == Patterns in Code

documentation isn’t enough

Pattern API

Patterns Combine Languages

Design » HTML » CSS » JavaScript

Style Guides Represent Integration

show context & relationships

Maintenance Must be Integrated

process, toolkit, visibility, simplicity

Don’t Repeat Yourself

“DRY”

Don’t Stretch For Patterns

“DSfP?”

Do Build One-Offs

Not A Pattern:

“These Elements Share a Border Style

Patterns Add Meaning:

“These Elements Share a Purpose

represented by this border style…

Sass Should Add Meaning

// before
aside {
  width: ((3*4em) + (2*1em)) / ((12*4em) + (11*1em)) * 100%; // 23.7288136%
  margin-right: 1em / ((12*4em) + (11*1em)) * 100%; // 01.6949153%
  padding-left: ((1*4em) + (1*1em)) / ((12*4em) + (11*1em)); // 08.4745763%
}

//after
aside {
  width: span(3);
  margin-right: gutter();
  padding-left: span(1 wide);
}

(you probably don’t need grids)

Sass Hint:

“Extends work well when used semantically to represent is-a relationships”

— Natalie Wiezenbaum [from memory]

CSS Was Designed For Patterns

what did you think “classes” were for?

Specificity is Your Guide

prefer the shallow end…

Naming Conventions

consistent across the entire team

Pattern Layers

layout region » component » element » state / JS-hook (?)

Get Organized

with Sass partials!

Atomic, ITCSS, SMACSS, OOCSS, BEM…

they’re all great for someone

No Right Answer

but “no answer” is TOTALLY WRONG

Separation of Concerns

data » logic » structure » presentation

Abstract Patterns (in code)

color palettes, sizes, fonts…

Color Palette

Color Variables

// individual definitions
$color--brand-orange: hsl(24, 100%, 39%);
$color--brand-blue: hsl(195, 85%, 35%);
$color--brand-pink: hsl(330, 85%, 48%);

// direct access
.usage {
  background: $color--brand-pink;
}

Color Maps

// grouped definitions
$colors: (
  'brand-orange': hsl(24, 100%, 39%),
  'brand-blue': hsl(195, 85%, 35%),
  'brand-pink': hsl(330, 85%, 48%),
);

// access through a function
.usage {
  background: map-get($colors, 'brand-pink');
}

Maps Provide Meaningful Structure

The Map Problem

$colors: (
  'brand-blue': hsl(195, 85%, 35%),
  'gray': desaturate(map-get($colors, 'brand-blue'), 80%),
);

[ERROR] Undefined variable: "$colors".

Define Now, Calculate Later

$colors: (
  'brand-blue': hsl(195, 85%, 35%),
  'gray': 'brand-blue' ('desaturate': 80%),
);

// Calculate Later:
.usage {
  background: color('gray');
}

OddBird’s Accoutrement-Color

Layers of Abstraction

$brand-colors: (
  'brand-orange': hsl(24, 100%, 39%),
  'brand-blue': hsl(195, 85%, 35%),
  'brand-pink': hsl(330, 85%, 48%),
);

$neutral-colors: (
  'light-gray': 'brand-blue' ('tint': 80%, 'desaturate': 80%),
  'gray': 'brand-blue' ('desaturate': 80%),
  'black': 'brand-blue' ('shade': 65%),
);

$theme-colors: (
  'text': 'black',
  'border': 'gray',
  // ...
);

Is It Over-Engineered?

Readable by Humans and Machines

@each $color in map-keys($colors) {
  [data-color='#{$color}'] {
    background: color($color);
  }
}

Accessed Dynamically

// this works:
@mixin background($color-name) {
  background: color($color-name);
}

// this doesn't work:
@mixin background($color-name) {
  background: $#{$color-name};
}

Make Patterns

The Lazy Option

Enter Herman

A SassDoc Style-Guide Generator


SassDoc

SassDoc + Herman (alpha)

// Brand Colors
// ------------
/// We use bright primary colors for the main brand,
/// everything else is based on these colors.
/// @preview color-palette
/// @group colors
$brand-colors: (
  'brand-orange': hsl(24, 100%, 39%),
  'brand-blue': hsl(195, 85%, 35%),
  'brand-pink': hsl(330, 85%, 48%),
);


Herman Colors

Size Maps

$text-sizes: (
  'root': 22px,
  'small': 18px,
  'smaller': 16px,
);

Accoutrement-Scale

$text-sizes: (
  'root': 16px,
  'rhythm': 'root' ('fifth': 1),
  'extra-small': 'root' ('multiple': 0.75),
  // ...
);

Accoutrement-Type

$body-font: (
  'name': 'CenturyOldStyle',
  'source': 'http://www.fontspring.com/fonts/fontsite/century-old-style-fs',
  'stack': ('Baskerville', 'Palatino', 'Cambria', 'Georgia', serif),
  'regular': 'serif/CenturyOldStyle-regular',
  'italic': 'serif/CenturyOldStyle-italic',
  'bold': 'serif/CenturyOldStyle-bold',
);

Export Sass Maps to JSON

we use sass-json-export

@include json-encode(
  (
    'brand-colors': $brand-colors,
    'body-font': $body-font,
  )
);


Herman Fonts

So… Is It Over-Engineered?

Over-Engineering is

Part of the Process

(you have to start somewhere)

CSS-Only Tabs

Responsive CSS Tabs in Sass, with help from the float isolation method.
  • Container wraps all tabs dynamically
  • Container remains equal-height
  • No JS Height calculations
  • The entire thing is responsive
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Illum, deserunt, nihil excepturi eius quisquam aspernatur aperiam error ut reiciendis numquam quasi accusantium rerum nesciunt tempore expedita velit laborum architecto quod?
@mixin tabs(
  $slugs,
  $nested: $default-nested-selectors,
  $checked: $default-checked-selector
) {
  $nested-tabs: $nested;
  $nested-content: $nested;
  @if length($nested) > 1 {
    $nested-tabs: nth($nested, 1);
    $nested-content: nth($nested, 2);
  }

  @each $slug in $slugs {
    $toggle   : '[#{$toggle-attribute}*="#{$slug}"]';
    $title    : '[#{$title-attribute}*="#{$slug}"]';
    $content  : '[#{$content-attribute}*="#{$slug}"]';

    #{$content} { @extend %hide-tab-content; }
    #{$toggle} {
      @extend %hide-tab-toggle;
      &#{$checked} {
        & ~ #{$nested-tabs} #{$title} {
          @extend %active-tab-title !optional;
        }
        & ~ #{$nested-content} #{$content} {
          @extend %show-tab-content;
        }
      }
    }
  }
}

What’s Essential?

@mixin input-toggle {
  @include is-hidden;

  &:checked { 
    @content; 
  }
}

Toolkits Are A Byproduct

Toolkits Provide TradeOffs

Systems > Solutions

all our toolkits are abstract

Tools Should Fit You

“Sorry, this hammer only builds patio chairs…”

Document, Ergo Sum

What Susy Functions?

Test Your Tools

@include test('Merges two maps, adding their values where appropriate') {
  $base: (one: 1, two: 1, three: 1);
  $add: (one: 1, two: 2, three: -1);

  @include assert-equal(
    map-add($base, $add),
    (one: 2, two: 3, three: 0),
  );
}

Sass True

True Results

Sass-Lint Results

Concrete Patterns

icons, buttons, etc.

Notification with Icons

User Message

HTML Templates (Jinja/Nunjucks)

pre-processors for your markup!

Patterns » Macros

<h1>{{ icon('wave') }} Hello World!</h1>

Template Logic is Great

presentation layer should have opinions


{% set icon = 'ok' if ('success' in tags) else 'warning' %}

Template Language Gotcha’s

(lessons from Sass language design)

1. Provide a Super-Set

2. Clearly Distinguish New Syntax

<!-- YES -->
<p>{{ icon('gear', alt='alternative text', class='foo') }}</p>

<!-- NO -->
<Icon name="gear" alt="alternative text" size="16" className="foo" data-other="bar" />
/* Really, NO */

import React from 'react';

const Icon = React.createClass({
    /* Render an SVG icon with or without text.
    * 
    * Sample use from another component's `render` method:
    * return <Icon name="gear" alt="Could mean anything" size="16" className="foo" data-other="bar" />
    */

    render () {
        let {name, alt, size, is_inline, className, attrs} = this.props;
        if (is_inline === undefined) {
            is_inline = true;
        }

        return (
            <span>
                <svg data-icon={name} data-is-inline={is_inline} data-size={size}
                        className={className} {...attrs}><use xlinkHref={'#icon-' + name} /></svg>
                {alt ? <span className="js-icon-text is-hidden">{alt}</span> : ''}
            </span>
        );
    }
});

export default Icon;

JavaScript is Not a Universal Solution

other languages exist for a reason

JavaScript is Not a Universal Solution (2)

(in case you forget)

JavaScript is Not a Universal Solution (3)

(I made several slides)

JavaScript is Not a Universal Solution (4)

(to remind you)

JavaScript is Not a Universal Solution (5)

(and let it sink in)

Lightning Desing: Icons

Lightning Desing: Icon Code

Lightning Design: Warning Icon

<span class="slds-icon_container">
  <svg aria-hidden="true" class="slds-icon slds-icon-text-warning slds-icon--x-small">
    <use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#warning"></use>
  </svg>
  <span class="slds-assistive-text">Warning Icon</span>
</span>

Related Class Patterns » Data Attributes

icon color and size classes

[data-slds-icon-color]

.slds-icon-text-default | .slds-icon-text-warning | .slds-icon-text-error
data-slds-icon-color="text-default | text-warning | text-error"

[data-slds-icon-size]

.slds-icon--x-small | .slds-icon--small | .slds-icon--medium | .slds-icon--large
data-slds-icon-size="extra-small | small | medium | large"

Jinja2: Icon Macros

{{ icon(name, alt, color, size) }}

Jinja2: Icon Macros

{% macro icon(name, alt=none, color='text-default', size='medium') %}
<span class="slds-icon_container">
  <svg aria-hidden="true" class="slds-icon" data-slds-icon-color="{{ color }}" data-slds-icon-size="{{ size }}">
    <use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#{{ name }}"></use>
  </svg>
  <span class="slds-assistive-text">{{ alt or name }}</span>
</span>
{% endmacro %}

<span class="slds-icon_container"><svg aria-hidden="true" class="slds-icon slds-icon-text-warning slds-icon--x-small"><use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#warning"></use></svg><span class="slds-assistive-text">Warning Icon</span></span>

{{ icon(‘warning’) }}

Dynamic Colors & Sizes

@each $icon-color in ('text-default', 'text-warning', 'text-error') {
  [data-slds-icon-color='#{$icon-color}'] {
    fill: color($icon-color);
  }
}

@each $icon-size in ('extra-small', 'small', 'medium', 'large') {
  [data-slds-icon-size='#{$icon-size}'] {
    @include square($icon-size);
  }
}

Herman Icons

/// @icons icons/ utility.macros.js.j2:icon
.slds-icon {
  @include square('medium');
  border-radius: size('border-radius');
  fill: color('white');
}

/// @icons <icon-folder> <icon-macro-file>:<macro>


Herman Icons

Notification with Icons

User Message

Notification Macro

with arguments, like a Sass mixin

{% macro notice(message, tags=[]) %}
  {% if message %}
    <div data-notice="{% for tag in tags %}{{ tag }} {% endfor %}">
      {# THINGS GO HERE. YOU DON'T CARE. #}
    </div>
  {% endif %}
{% endmacro %}
/// @example njk
///   {{ import 'alert.macros.njk' as alert }}
///   {{ alert.notice(
///     message='You did it! You will all the style guides!', 
///     tags=['success']
///   ) }}
[data-notice] {
  /* ... notification styles here ... */
}

StyleGuide!

(we’re still working on this)

Failing Our Way To Success

(maybe)

There’s Still Work To Do, BUT…

Code Patterns

are central to a pattern-driven design

Patterns Make Code Readable

Patterns Make Code Meaningful

Patterns Live Inside Your Code!