Art » Internet

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

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

Young OddBirds

Systems for System-Building

CSS Systems

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);
}

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

config » layout » components » elements » states & JS-hooks

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

Relationships Between Colors

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?

(only if it gets in your way)

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

avoid opinionated output / stay abstract

Tools Should Fit You

“Sorry, this hammer only builds patio chairs…”

Test Your Logic

Try True

@include test-module('Utilities') {
  @include test('Map Add [function]') {
    $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),
      'Returns the sum of two numeric maps');
  }
}

True Results

Sass-Lint Results

Document, Ergo Sum

What Susy Functions?

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 Gotchas

(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)

Lightning Desing: Icon Code

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 %}">
      {% set icon_name = 'ok' if ('success' in tags) else 'warning' %}
      {{ icon(icon_name) }}
      {# THINGS GO HERE. YOU DON'T CARE. #}
    </div>
  {% endif %}
{% endmacro %}

Herman Documentation

/// @example njk
///   {{ import 'alert.macros.njk' as alert }}
///   {{ alert.notice(
///     message='You did it! You win all the style guides!', 
///     tags=['success']
///   ) }}
[data-notice] {
  /* ... notification styles here ... */
}

StyleGuide!

(we’re still working on this, but…)

Style Guides Are The Output

Code Patterns Are The Foundation

Make Code Readable

Make Code Meaningful

Patterns Live Inside Your Code!