pnts.us

On Stage

design, interface, and visualization

Yesterday Eddie announced our partnership with Digitas LBI at the Newfronts conference. It was pretty fun to see the SimpleReach dashboard up on an GINORMOUS screen.

Eddie Kim presenting SimpleReach at Newfronts

Spinning numbers!

Color Mixing Along A Gradient With Sass

design, interface, sass, and visualization

tldr; Here's how I used Sass to mix colors along a gradient and assign them to a value on a scale of 1 - 100.

Color-coded percentiles

Recently I worked on a prototype for a benchmarking app with the intent of helping organizations quickly understand how a given thing performed in a number of contexts across a variety of metrics. A few initial sketches and we ended up with a tabular display coupled with a bar chart visualizing the distance between a given benchmark and the corresponding average.

The initial grid was rather dull and lacked a macro-level visualization that would quickly communicate the overall 'goodness' or 'badness' for a set of benchmarks. After a few uninspiring directions, we hit on using the background color to communicate where a particular metric, displayed as a percentile, fell along a gradient scale. If the scores were largely on the higher end, they'd be one color, and if they were mostly on the lower end, they'd be a different color.

Color-coded percentiles

A grid of benchmark results, displayed as percentiles.

We used Ember, Ember Data (with fixtures), and Sass to build the prototype, which had the advantage of using real data from the get go, and which took care of assigning a percentile class to each table cell in the format of '.percentile-x'. From there, Sass took over, which required a bit of tinkering to get a system in place that would allow us to change the gradient and color encodings on the fly (because we all know how fickle stakeholders can be - amiriteorwhat?).

The two tricks that made this possible were 1) list functions to quickly generate a sequence of numbers given a starting number and an ending number, and 2) four color-stops to ensure percentiles in the mid-range weren't overwhelmingly brown.

List functions aside, this is all the code you need:

For more detail, check out the pen below.

/*------------------------------------------------------------------


Functions - list functions taken directly or inspired by http://hugogiraudel.com/2013/08/08/advanced-sass-list-functions/


-------------------------------------------------------------------*/

@function calcRem($target) {
  @return ($target / 16) + rem;
}

@function calcEm($target, $context: 12) {
  @return ($target / $context) * 1em;
}

// create an arbitrary list of numbers
@function sequence($start, $end) {
  $seq: $start;

  @for $i from $start through $end {
    $x: nth($seq, length($seq)) + 1;
    $seq: append($seq, $x);
  }
 
  @return $seq;
}

// get first item in a list
@function first($list) {
  @return nth($list, 1);
}

// get last item in a list
@function last($list) {
  @return nth($list, length($list));
}


/*------------------------------------------------------------------


Variables


-------------------------------------------------------------------*/

$primary-color: #333;

$mobile: 76em;

$fonts: (junction-bold, junction-light, junction-regular, leaguegothic-condensed-italic, leaguegothic-condensed-regular, leaguegothic-italic, leaguegothic-regular);
@each $font in $fonts {
  @font-face {
    font-family: $font;
      src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/1643/#{$font}.eot');
      src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/1643/#{$font}.eot?#iefix') format('embedded-opentype'), 
           url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/1643/#{$font}.woff') format('woff'), 
           url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/1643/#{$font}.ttf')  format('truetype');
  }
}

$font-sizes: (1, 2, 3, 4, 5, 6, 7 ,8, 9, 10, 11, 12, 13, 14, 18, 20, 24, 30, 36, 48, 64, 72, 96, 144, 288);


/*------------------------------------------------------------------


Placeholders and Mixins


-------------------------------------------------------------------*/

%inlineBlock {
  display: -moz-inline-stack;
  display: inline-block;
  zoom: 1;
  *display: inline;
  vertical-align: top;
}

@each $font-size in $font-sizes {
  %font#{$font-size} {
    font-size: calcEm( $font-size )
  }
}

/*------------------------------------------------------------------


The meat and potatoes - relies on the list functions at the top of this file


-------------------------------------------------------------------*/

// set up the color stops - mess with these to change the gradient
$color-stop-1: hsl(107, 41.0526%, 62.7451%);
$color-stop-2: hsl(177, 42.9752%, 52.5490%);
$color-stop-3: hsl(263, 30.0000%, 60.7843%);
$color-stop-4: hsl(360, 74.8792%, 59.4118%);

// set up percentile ranges 
$top-tier: sequence(67, 99);
$mid-tier: sequence(34, 66);
$bottom-tier: sequence(1, 33);

// mixin to mix the colors 
@mixin tileStyle($color1, $color2, $range) {
  @for $i from first($range) through last($range) {
    $value: index($range, $i) / length($range) * 100;
    $shade: darken(mix($color1, $color2, $value), 15%);
  
    .percentile-#{$i} {
      background: mix($color1, $color2, $value);
      .number {
        text-shadow: .080em .080em $shade;
      }
    }
  }
}

// call the mixin for each percentile grouping
.table {
  @include tileStyle($color-stop-1, $color-stop-2, $top-tier);
  @include tileStyle($color-stop-2, $color-stop-3, $mid-tier);
  @include tileStyle($color-stop-3, $color-stop-4, $bottom-tier);
}


/*------------------------------------------------------------------


Style all the things


-------------------------------------------------------------------*/

* {
   -webkit-box-sizing: border-box;
      -moz-box-sizing: border-box;
           box-sizing: border-box;
}


body {
  background-size: cover;
  font-family: 'junction-light';
  font-size: calcEm( 14 );
  color: $primary-color;
  line-height: 1rem;
}

ul {
  list-style-type: none;
  padding: 0;
}

.search-result {
  color: $primary-color;
  margin: 1em 0 1em 0;
  padding: 0 1em;
}

.table {
  display: table;
  margin-top: 0;
}

.table-header, .table-body {
  display: table-header-group;
}

.table-header {
  @extend %font18;
  text-transform: uppercase;
  .th {
    padding-bottom: .5em;
    text-align: center;
    label {
      @extend %font6;
      display: block;
      margin-top: .7em;
    }
  }
}

.table-body {
  li {
    display: table-row;
    width: 100%;
  }
  .td {
    border: 2px solid #FFF;
    padding: .5em 0;
    line-height: 1em;
    &.metric {
      @extend %font18;
      border-bottom-color: #EFEFEF;
      border-top-color: #EFEFEF;
      label {
        @extend %font6;
        text-transform: uppercase;
        display: block;
      }
      span {
        @extend %font7;
        text-transform: uppercase;
      }
    }
    &:not(.metric) {
      color: #FFF;
      @extend %font24;
      line-height: 1.2em;
      .number {
        @extend %inlineBlock;
        padding-top: .3em;
      }
    }
  }
}

.tr {
  &:first-child {
    .metric {
      border-top: none;
    }
  }
  &:last-child {
    .metric {
      border-bottom: none;
    }
  }
}

.th, .td {
  display: table-cell;
  width: 16%;
  text-align: center;
}

legend {
  position: relative;
  margin: 2em 0 1em 0;
  width: 100%;
  height: 1em;
  background-image: linear-gradient(to left, $color-stop-1, $color-stop-2, $color-stop-3, $color-stop-4);
  &:before {
    position: absolute;
    content: '1st percentile';
    left: 0;
    margin-top: -1.5em;
    @extend %font8;
  }
  &:after {
    position: absolute;
    content: '99th percentile';
    right: 0;
    margin-top: -1.5em;
    @extend %font8;
  }
}

.benchmarks {
  @extend %inlineBlock;
  width: 100%;
}

See the Pen Dynamic background tiles by Andrea Mignolo (@pnts) on CodePen.

Animating Svg

design and gif

Animating SVG

Skeuomorphic Behavior

design and interface

I was in Portland last month for beer, beer, and EmberConf (mostly EmberConf), where I met a great group of people building ambitious applications in Ember. The talks are all available online, and if you're doing Ember development of any sort or interested in getting started, go watch them all now.

One of the more incidental takeaways that keeps popping into my head during idle moments came from Edward Faulkner's talk, "Animations and Transitions in an Ember App". In the first five minutes Edward uses the phrase, 'skeuomorphic behavior' to explain why scrolling has momentum in a medium with no physical limitations - our monkey brains just aren't quick enough to fully process instantanous changes in a system. If we're lucky we know *something* happened, but not exactly what. Ted Neslon said something similar a few years ago at Brooklyn Beta, which I may or not be correctly paraphrasing but remember it as, "animation is orientation in orthogonal space."

What I like about the phrase 'skeuomorphic behavior' is the nuance. For a while, debates about skeuomorphism by and large referred to visual ornamentation to aid in understanding the what of digital objects. Teasing apart the definition and applying it to wider systems of interaction changes skeuomorphism from a passing visual trend into a useful tool for thinking about digital systems. Sound is also a good candidate for skeuomorphic structures that assist in digital wayfinding - apps like Tweetbot and Airmail make nuanced use of sound to communicate the type of action being taken.

For more, check out Edwards Ember talk:

Medians And Messages

data and visualization

We still carry the historical baggage of a Platonic heritage that seeks sharp essences and definite boundaries...This Platonic heritage, with its emphasis in clear distinctions and separated immutable entities, leads us to view statistical measures of central tendency wrongly, indeed opposite to the appropriate interpretation in our actual world of variation, shadings, and continua. In short, we view means and medians as the hard "realities," and the variation that permits their calculation as a set of transient and imperfect measurements of this hidden essence. If the median is the reality and variation around the median just a device for its calculation, the "I will probably be dead in eight months" may pass as a reasonable interpretation.
Variation itself is nature's only irreducible essence. Variation is the hard reality, not a set of imperfect measures for a central tendency. Means and medians are the abstractions.

Stephen Jay Gould: The Median Isn't the Message

pnts.us is fueled by the intersection of mishaps, happenstance, books, curiousity, and beer.

New York, NY