Style Guides » Maintainable Patterns

Consistency, Clarity, Efficiency

document, ergo sum

My First Docs (2009)

// Susy Example:
#page
  +grid-container
  #left-nav
    +grid-col(3)
  #main-content
    +grid-prefix(1)
    +grid-col(4, 10, true)

WTF?!

#page
#left-nav
#main-content

No Programming Skillz

I make artsy bullshit

No Original Ideas

Thanks Natalie Downe!

No Plan

Just scratching an itch

No Stinkin’ Badges

seriously, badges weren’t a thing in 2009

35 Twitter Followers

no GitHub account

Context Matters

Team Size & Structure?

Internal vs. Consulting?

Similar Problems, Unique Constraints

your mileage will vary

Systems for System-Building

“Small Bootstrap something something every website [or something]”

Not Brad Frost

(I’m great at taking notes)

"Tiny Bootstraps, for Every Client"

— Dave Rupert

(nailed it)

Our Product is the Architecture

a solid foundation for hand-off

Quality Architecture == Patterns in Code

documentation isn’t enough

Patterns Combine Languages

Design » HTML » CSS » JavaScript

Style Guides Represent Integration

show context & relationships

Maintenance Must be Integrated

process, toolkit, visibility, simplicity

“No Silos

— Donna Chan & Isaak Hayes [from memory]

Pattern API

(Brad Frost said it, so I added a slide)

Basics of CSS Web Architecture

Atomic, ITCSS, SMACSS, OOCSS, BEM…

they’re all great for someone

Separation of Concerns

data » logic » structure » presentation

Specificity is Your Guide

prefer the shallow end…

CSS Was Designed For Patterns

what did you think “classes” are for?

Don’t Repeat Yourself

but also:

Don’t Stretch For Patterns

"These Elements Share a Border Style"

too fragile

"These Elements Share a Purpose"

that purpose is represented by a border style

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

— Natalie Wiezenbaum [from memory]

Build One-Offs

— Nathan Curtis

Patterns Should Add Meaning

semantic naming helps

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

Naming Conventions

consistent across the entire team

What Is This?

layout region » component » element » state » JS-hook

  • layout region » data-region="banner"

  • component » .calendar or data-calendar="weekly"?

  • element » .calendar-day ?

  • state » data-state="active" or .is-active

  • JS-hook » .js-calendar

No Right Answer

but “no answer” is TOTALLY WRONG

Abstract Patterns

color palettes, sizes, fonts…

How Do We Represent Abstract Patterns?

Color Palette

Color Variables

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

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

easy to access, forced grouping, not machine readable

Color Maps

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

.usage {
  background: map-get($colors, 'brand-pink');
}

naturally grouped, accessible through functions, machine readable

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

Semantic Color Assignments

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

What Are The TradeOffs?

Make Documentation 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%),
);

Export Sass Maps to JSON

we use sass-json-export

@include json-encode($neutral-colors);


Herman Colors

Size Maps

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

Accoutrement-Scale

$ratios: (
  'herman': 1.4
);

$text-sizes: (
  'root': 22px,
  'h1': 'root' ('herman': 3),
  'h2': 'root' ('herman': 2),
  'h3': 'root' ('herman': 1),
  'small': 18px,
  'smaller': 16px,
);

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


Herman Fonts

Toolkits Are A Byproduct

Systems > Solutions

provide tools, not pre-built structures

Tools Should Fit You

“Sorry, this hammer only builds patio chairs…”

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

Test Your Tools

Sass True

@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),
    'Returns the sum of two numeric maps');
}

True Results

Lint Your Code

let the robots do the work

Sass-Lint Results

Concrete Patterns

icons, buttons, etc.

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

Templates Should Look Like Templates

I want to see the expected structure at a glance

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

Sass Icon Defaults

.slds-icon_container {
  border-radius: size('border-radius');
  display: inline-block;
}

.slds-icon {
  @include square('medium');
  border-radius: size('border-radius');
  fill: color('white');
}

Sass Icon Colors

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

Sass Icon Sizes

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

Herman Icons

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

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


Herman Icons

Something More Complex?

Here’s A Macro

it creates variations on a user-message

{% macro user_message(message, tags=[]) %}
  {% if message %}
    <div data-message="{% for tag in tags %}{{ tag }} {% endfor %}">
      {# THINGS GO HERE. YOU DON'T CARE. #}
    </div>
  {% endif %}
{% endmacro %}

So… Now What?

Maybe?..

{% set user_message_doc={
  'name': 'User Message',
  'description': 'Generate pop-up user notification messages for info, success, errors, or warnings.',
  'examples': [
    {
      'content': 'Yay, that worked great!',
      'tags': ['success']
    },
    {
      'content': 'Oh no, something went wrong!',
      'tags': ['error']
    }
  ]
} %}
{% macro user_message(message, tags=[]) %}
  {# THINGS GO HERE. YOU DON'T CARE. #}
{% endmacro %}

And/Or This?

/// Generate pop-up user notification messages for info, success, errors, or warnings.
/// @macro message.macros.js.j2:user_message
/// @macro-data
///   - 'content': 'Yay, that worked great!'
///   - 'tags': ['success']
/// @macro-data
///   - 'content': 'Oh no, something went wrong!',
///   - 'tags': ['error']
[data-message] {
  content: "it's late, and I don't want to make a real example here";
}

StyleGuide!

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