Not Natalya

Style Guides | Design Systems | Pattern Libraries

everybody’s doing it…

(…are you?)

Clarity

Lightning Design System

Motion

Lonely Planet

Mail Chimp

M

NYC Transit Authority Graphics Standards Manual

Graphics Standards Manual: 1970

N

Numbers

Sign Construction and Engineering

Nerd.

How do I Build It?

Lego

Lego Component Library

Lego Truck

Lego Minecraft

Code Patterns Are Design Patterns

Lightning Icon Code

Lightning Code Scribbled

You are not MailForce. You are not InstaFace.

(are you?)

(Team Size)

(Team Structure)

(Internal vs. Consulting)

(Is it 1970?)

Similar Problems

Unique Constraints

Young OddBirds

Small Team, Outside Consulting

Our Product is Good Architecture

patterns for hand-off

“Tiny Bootstraps, for Every Client

— Dave Rupert

CSS Systems

Consistent Systems

Custom Patterns

Web Patterns Start With Code

.grid-span {
  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%
}

Patterns Add Meaning

.grid-span {
  width: span(3);
  margin-right: gutter();
  padding-left: span(1 wide);
}

Susy

Practical Layouts, Past & Future at Refresh Denver, Wednesday, June 14, 2017

Something like “Code is Communication” or Something

- Sarah Drasner

Build Pattern APIs

Lightning Icon Code

Lighting Icons API

CSS Was Designed For Patterns

Classes are Patterns

The Cascade is for Patterns

Specificity is Your Guide

Normal Triangle by Maslow Miriam
Un-ITCSS

ITCSS Layers

👨

masc

femme

rando

Not Just WCAG Accessibility

EMPATHY

for all kinds of not you

Your Team:

rando

No Output from Settings & Tools

(What’s missing? Layouts? State modifiers?)

Organize Partials

Don’t Repeat Yourself

“DRY”

Don’t Stretch For Patterns

“DSfP?”

Not A Pattern:

“These Elements Share a Border Style

Patterns Add Meaning:

“These Elements Share a Purpose

represented by a border style…

Build One-Offs

Home Page?

Patterns Combine Languages

Design » HTML (templates?) » CSS (preprocessor?) » JavaScript

Style Guides == Integration

context & relationships

Integration

Maintenance Must be Integrated

process, toolkit, visibility, simplicity

Naming Conventions

consistent across the entire team

Atomic, ITCSS, SMACSS, OOCSS, BEM…

they’re all great for someone

No Right Answer

but “no answer” is TOTALLY WRONG

Atomic Legos
Put your helmet on

Atomic Legos

Abstract Patterns (in code)

color palettes, sizes, fonts…

Color Palette

CSS Variables?

:root {
  --brand-orange: hsl(24, 100%, 39%);
  --brand-blue: hsl(195, 85%, 35%);
  --brand-pink: hsl(330, 85%, 48%);
}

Sass Variables?

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

Maps Add Meaning

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

The Map Problem

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

The Map Problem

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

[ERROR] Undefined variable: "$colors".

Map Self-Reference

$icons: (
  'brand-pink': hsl(330, 85%, 48%),
  'escher':  'brand-pink',
  'godel': 'escher',
  'bach': 'godel',
  'kevin bacon': 'bach',
);

Define Now

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

OddBird’s Accoutrement-Color

Calculate Later

.usage {
  background: color('gray'); // the function does all the work...
}

OddBird’s Accoutrement-Color

Map Self-Reference

$icons: (
  'brand-pink': hsl(330, 85%, 48%),
  'escher':  'brand-pink',
  'godel': 'escher',
  'bach': 'godel',
  'kevin bacon': 'bach',
);

color('kevin bacon')

but Miriam,

It’s Over-Engineered!

Know The Trade-Offs

Meaningful to Humans & Machines

@each $color in map-keys($colors) {
  [data-color='#{$color}'] {
    background-color: 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};
}

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',
  // ...
);

Exportable

we use sass-json-export

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

@include json-encode($colors);
/* css */
/*! json-encode: '{
  'brand-orange': 'hsl(24, 100%, 39%)',
  'brand-blue': 'hsl(195, 85%, 35%)',
  'brand-pink': 'hsl(330, 85%, 48%)'
}' */

Make Patterns

The Lazy Option

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 color output

Modular Scales?

$ms-base: 1em;
$ms-ratio: $golden; // do you believe in magic?

$large: ms(1);
$larger: ms(2);

Named Ratios

$ratio-options: (
  'octave'            : 2,
  'major-seventh'     : 15/8,
  'major-sixth'       : 5/3,
  'fifth'             : 3/2,
  'augmented-fourth'  : 45/32,
  'fourth'            : 4/3,
  'major-third'       : 5/4,
  'major-second'      : 9/8,
);

Size Maps

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

Accoutrement-Scale

$text-sizes: (
  'root': 16px,
  'rhythm': 'root' ('fifth': 1),
  'gutter': 'rhythm',
);

p {
  margin-bottom: size('gutter');
}

OddBird’s Accoutrement-Scale

Herman font sizes

Herman spacing

Accoutrement-Type

$body-font: (
  'name': 'Fira Sans',
  'source': 'http://www.fontspring.com/',
  'stack': ('Helvetica Neue', 'Helvetica', 'Arial', sans-serif),
  'regular': 'sans/firasans-regular',
  'italic': 'sans/firasans-italic',
  'bold': 'sans/firasans-bold',
);

Automated Webfont Imports

@include import-webfonts($fonts);

:root {
  @include font-family('body');
}

Herman fonts

Breakpoint Maps

$breakpoints: (
  'footer-columns': 25em,
  'toolbar-icons': 34em,
  'form-columns': toolbar-icons,
  'off-canvas': 50em,
  'calendar-tabs': 60em,
);

Named Queries

.sidebar {
  @include below('off-canvas') {
    transform: translate3d(-101%, 0, 0);
  }
}

It’s only over-engineered if it

Gets in Your Way

Part of the Process

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

True Results

Document, Ergo Sum

What Susy Functions?

Concrete Patterns

icons, buttons, etc.

Notification with Icons

User Message

HTML Template Logic is Great

pre-processors for your markup!

{% 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

<Icon name="gear" alt="alternative text" size="16" className="foo" data-other="bar" />

(not clear, not super)

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

Looks like Markup

{% macro icon(name, alt=none, size=none, is_inline=true, class=none) %}
  <svg data-icon="{{ name }}" {{ attr_if(size, 'data-size') }} {{ attr_if(class, 'class') }}>
    <use xlink:href="#icon-{{ name }}" />
  </svg>

  {%- if alt -%}
    <span class="is-hidden">{{ alt }}</span>
  {%- endif -%}
{% endmacro %}

What the ??

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

Lightning Desing: Icon Code

class="slds-icon slds-icon-text-warning slds-icon--x-small"

Related Class Patterns » Data Attributes

icon color and size classes

.slds-icon-text-default | .slds-icon-text-warning | .slds-icon-text-error

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

.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 %}

{{ icon(‘warning’) }}

vs.

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

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 ... */
}

Notification output

Style Guides are Output

Patterns live in Code

Make Code Readable

Make Code Meaningful