<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
<channel>
  <title>Tyler Gaw on design and engineering</title>
  <link>https://tylergaw.com/</link>
  <managingEditor>me@tylergaw.com.com (Tyler Gaw)</managingEditor>
  <webMaster>me@tylergaw.com.com (Tyler Gaw)</webMaster>
  <description>An account of the work I do as a designer and engineer</description>
  <lastBuildDate>Thu, 27 Feb 2020 15:30:00 EST</lastBuildDate>
  <language>en</language>
  <generator>Elbow grease</generator>
  <image>
    <url>https://tylergaw.com/images/rss-graphic.png</url>
    <title>Tyler Gaw on design and engineering</title>
    <link>https://tylergaw.com/</link>
    <width>144</width>
    <height>180</height>
  </image>
  <atom:link href="https://tylergaw.com/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Realign 2020</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/realign-2020</link>
      <guid>https://tylergaw.com/articles/realign-2020</guid>
      <pubDate>Thu, 27 Feb 2020 15:30:00 EST</pubDate>
      <description>
        <![CDATA[
<p class="entry-intro">
  Like other designers, I enjoy updating my personal site every so often. In my
  case I do a <a href="https://tylergaw.com/about#versions">major iteration</a> every couple years with minor tweaks between.
  This current incarnation is almost two years old and I’m ready to make major
  changes.
</p>

<p>
  When I launched this design in 2018 I remember thinking this would
  be my last site. I wasn’t under any illusion that I wouldn’t update this site
  again in the future. Only that I had a solid foundation that I could
  build on for a long time. Even if the changes where major, they’d still be
  iterations of existing content and aesthetics.
</p>

<p>
  I have two main motivations for this year’s realign. First, I want to present
  my work from the last few years in a more complete way. The 2018 verison of
  this site listed projects, but didn’t go into much detail. There’s a lot there
  I want to surface. I’m looking for my next role so I need to present my work
  in a way that show’s what I'm capable of.
</p>
<p>
  Second, I just want it to look cooler and weirder. Heavy emphasis on weird.
  With every iteration of this site since I launched it in 2006 I’ve made
  concerted efforts to make it look much different than the standard web
  designer portfolio/blog website. I don’t have anything against other folks’ sites,
  I just want mine to look different and strange.
</p>

<h2>Writing about it</h2>
<p>
  Plenty of other designers are iterating on their personal sites and writing
  about their process. I enjoy reading those. So, I’m going to follow their
  lead and write about my process.
</p>
<p>
  I’ve already started the process in a rough way. For the last few weeks I’ve
  let myself noodle on different areas of the design. I spent a lot of hours
  pushing points around in Illustrator for an updated logo and some fun
  typography squigglevision. I spent, I don’t know how long just fiddling with
  CSS in browser. I wanted time to play a while before I set out to really get
  updated content and design together.
</p>

<p>
  With the initial play I have a rough idea of what I’ll write about. I’ll
  update this post as I go to keep a reference. Some of the topics
  will change and I’ll add more as I go, but below is a rough outline.
</p>

<h3>Realign 2020 Posts</h3>
<ul>
  <li><a>Realign 2020: Logo</a></li>
  <li><a>Realign 2020: Type</a></li>
  <li><a>Realign 2020: Color</a></li>
  <li><a>Realign 2020: Content</a></li>
  <li><a>Realign 2020: Netlify</a></li>
  <li><a>Realign 2020: Tooling Updates</a></li>
</ul>

<p>
  After the the illustration work and in browser playing, I jumped back into
  Sketch to try to get at a more concrete plan and to play more. I won’t continue
  in Sketch to get a “complete” design. I prefer to do a lot in CSS. But here's a zoomed
  out screenshot of my current mess to get an idea of what I’m up to.
</p>

<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-realign-2020-initial-sketch.png" alt="A screenshot of a messy Sketch document showing the current status of this site’s design realign" />
  <figcaption>
    WIP design realign
  </figcaption>
</figure>

<p>
  You can see shell pages started for projects. I have six main buckets of work
  that I want to present in detail. There’s a lot more, but those are the ones that I’ll have the time and energy to pull off right now.
</p>

<p>
  Here we go again.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>Using a Custom OAuth Provider with NetlifyCMS</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/netlify-cms-custom-oath-provider</link>
      <guid>https://tylergaw.com/articles/netlify-cms-custom-oath-provider</guid>
      <pubDate>Sun, 18 Aug 2019 22:00:00 EST</pubDate>
      <description>
        <![CDATA[
<p class="entry-intro">
  At <a href="https://www.streetcred.co">work</a>, we’re in the middle of a site
  realign and rebuild. As part of that effort, we’re moving to
  <a href="https://www.netlifycms.org/">NetlifyCMS</a>. Implementing NetlifyCMS
  was eerily easy for me. But, I did hit one snag when it came to OAuthing
  with GitHub. In this post I explain the problem and what I did to get things
  working.
</p>

<p>
  First, the problem. I didn’t do anything fancy with
  the NetlifyCMS installation. I used the standard approach described
  in <a href="https://www.netlifycms.org/docs/add-to-your-site/">their docs</a>. One
  requirement we had was to not use Netlify. We wanted to keep things on our
  AWS infrastructure, because that’s where all our stuff is. We didn’t want
  another thing to manage.
</p>

<p>
  Before diving in, here are links to examples described in this post. These
  might be all you need to get going if you’re building a similar thing:
</p>
<ul>
  <li><a href="https://tylergaw.github.io/netlify-cms-github-oauth-provider-client-example">Live client example</a></li>
  <li><a href="https://github.com/tylergaw/netlify-cms-github-oauth-provider-client-example">Client example repo</a></li>
  <li><a href="https://glitch.com/edit/#!/netlify-cms-github-oauth-provider-example">Server example on Glitch</a></li>
  <li><a href="https://github.com/tylergaw/netlify-cms-github-oauth-provider-server-example">Server example repo</a></li>
</ul>

<p>
  For NetlifyCMS authenticaton I went with <a href="https://www.netlifycms.org/docs/authentication-backends/#github-backend">GitHub backend</a>.
  I got lost there. The docs say:
</p>

<blockquote>
  <p>To enable basic GitHub authentication:</p>

  <ol>
    <li>Follow the authentication provider setup steps in the Netlify docs.</li>
    <li>Add the following lines to your Netlify CMS config.yml file:</li>
  </ol>
  <cite>
    <a href="https://www.netlifycms.org/docs/authentication-backends/#github-backend">NetlifyCMS docs, "Authentication & Backends - GitHub Backend”</a>
  </cite>
</blockquote>

<p>
  I didn’t want to use Netlify. But I also went ahead and followed the docs they
  referenced in step one. Even with that, I couldn’t quite figure out what
  needed to happen. Step two above was fine. But, I could tell something was
  missing.
</p>

<h2>The Problem</h2>
<p>
  When I tried to log in to the NetlifyCMS on our
  deployed site, I would hit a 404. So I knew I was missing some piece, but
  was unclear what that was. I’d spent a while banging my head against it so
  I asked <a href="https://gitter.im/netlify/NetlifyCMS?at=5d4da39b9507182477ac29cf">a question</a>
  in the NetlifyCMS Gitter room, but didn’t find help there.
</p>

<h2>The Solution</h2>
<p>
  <strong>To use a GitHub backend with NetlifyCMS, you have to have your own server
    to handle OAuth</strong>. This is a requirement of GitHub’s authentication
    flow. The good news about that, is that it’s a standard OAuth flow. The
    bad news about that, is that it’s a standard OAuth flow.
</p>

<h3>Prior Art</h3>

<p>
  <a href="https://github.com/vencax/netlify-cms-github-oauth-provider">vencax/netlify-cms-github-oauth-provider</a>
  is an example of what’s needed. It’s mostly an example of a standard OAuth flow with
  a couple—very—helpful NetlifyCMS-specific bits. That got me almost there, but I still
  couldn’t get everything working. I was still having trouble understanding
  how all the pieces fit together.
</p>

<p>
  What I needed to do was build my own server to handle the OAuth flow.
  This is a thing I’ve done and <a href="https://tylergaw.com/articles/jribbble-three-and-oauth/">written about before</a>.
  OAuth is like that for me. I set it up. Deploy it. Forget it. Then have to
  give myself a refresher to do again. That’s what the server example in this
  post is.
</p>

<h3>The Server</h3>

<p>
  I went with <a href="https://glitch.com">Glitch</a> for this example. For me
  it’s quickest way to get a server spun up. And the easiest way for me to share
  it with you.
</p>

<p>
  I put steps in the <a href="https://github.com/tylergaw/netlify-cms-github-oauth-provider-server-example#what-you-need-to-do">README</a>
  to explain what you need to do to get this up and running for your own use.
</p>

<p>
  The user-facing part of the <a href="https://glitch.com/edit/#!/netlify-cms-github-oauth-provider-example">server example</a>
  exists, but it’s not meant for direct use. If you try to use it directly it’ll
  look like it’s not doing anything. The idea is for you to copy the
  example—either on Glitch or your own server—and point your NetlifyCMS client
  at it using the <code>base_url</code> key described below and in the client
  example.
</p>

<p>
  The code is 99% standard OAuth handshake handling.
  A <code>GET</code> followed by a <code>POST</code> to GitHub’s OAuth resources
  to retrieve the OAuth access token. I left comments in the code to point out what’s
  happening and what’s NetlifyCMS-specific. The biggest chunk of code that
  NetlifyCMS needs is this;
</p>

<pre><code class="language-javascript">const postMsgContent = {
  token: body.access_token,
  provider: "github"
};

const script = `
  &ltscript&gt;
    (function() {
      function recieveMessage(e) {
        console.log("recieveMessage %o", e);

        // send message to main window with the app
        window.opener.postMessage(
          'authorization:github:success:${JSON.stringify(postMsgContent)}',
          e.origin
        );
      }

      window.addEventListener("message", recieveMessage, false);
      window.opener.postMessage("authorizing:github", "*");
  })()
&lt;/script&gt;`;</code></pre>

<p>
  A couple bits of formatting aside, I took this from <a href="https://github.com/vencax/netlify-cms-github-oauth-provider/blob/master/index.js#L74">vencax/netlify-cms-github-oauth-provider</a>.
  This chunk of code is a little tough to follow. The gist of it is that your
  NetlifyCMS client JavaScript is gonna get your GitHub access token via
  <code>postMessage</code>. I tried to find the postMessage handling code in
  the NetlifyCMS code, but haven’t been able to track it down yet. If anyone
  knows where that is, <a href="mailto:me@tylergaw.com">I’d love to know</a>.
</p>
<p>
  We put this <code>script</code> in a string because we have the
  server respond with it. You could also put this in HTML and return the
  access token a different way, but this a way to make short work of it.
</p>

<h3>The Client</h3>

<p>
  There’s a single customization needed to make this work with the standard
  GitHub backend NetlifyCMS configuration. The <code>base_url</code> key.
</p>

<code><pre class="language-yml">backend:
  name: github
  branch: master
  repo: tylergaw/netlify-cms-github-oauth-provider-client-example # change this to your repo
  base_url: https://netlify-cms-github-oauth-provider-example.glitch.me # change this to your OAuth server
</pre></code>

<p>
  The other items in <code>config.yml</code> are standard NetlifyCMS config
  items. <code>base_url</code> needs to be a public URL to the server that
  handles OAuth for your GitHub application. Like
  the example in this post.
</p>

<p>
  You can see in the <a href="https://github.com/tylergaw/netlify-cms-github-oauth-provider-client-example">client code</a>
  that this is a no frills NetlifyCMS setup.
</p>

<h2>That’s All</h2>

<p>
  We’re a similar setup at <a href="https://streetcred.co">StreetCred</a> on
  our soon-to-launch new site and so far, so good. NetlifyCMS is an excellent
  project. One that I’ve been wanting for years. It checks all the boxes that
  other CMSs don’t and I look forward to using it more.
</p>
<p>
  If this post and/or the examples helped you, or if you try to use it and get
  stuck, send me an email at <a href="mailto:me@tylergaw.com">me@tylergaw.com</a>
  and let me know.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>Good Things</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/good-things</link>
      <guid>https://tylergaw.com/articles/good-things</guid>
      <pubDate>Sun, 04 Aug 2019 15:00:00 EST</pubDate>
      <description>
        <![CDATA[
<p class="entry-intro">
  Folks in design and technology have a tendency towards negativity. Any day
  of the week you can read Twitter, blog posts, and have conversations with co-workers
  and hear people declare, with self-appointed authority, that anything they don’t
  find value in is bad.
</p>
<p>
  Here’s a non-exhaustive, unordered list of things about my work—maybe yours
  too—that are good.
</p>

<ul>
  <li>HTML is good</li>
  <li>The WHATWG is good</li>
  <li>CSS is good</li>
  <li>The CSSWG is good</li>
  <li>Sass is good</li>
  <li>PostCSS is good</li>
  <li>CSS-in-JS is good</li>
  <li>CSS Modules is good</li>
  <li>Sketch app is good</li>
  <li>Photoshop is good</li>
  <li>Illustrator is good</li>
  <li>Affinity Designer is good</li>
  <li>Paying for fonts is good</li>
  <li>JavaScript is good</li>
  <li>jQuery is good</li>
  <li>React is good</li>
  <li>React Native is good</li>
  <li>Redux is good</li>
  <li>Redux Thunk is good</li>
  <li>Redux Saga is good</li>
  <li>node is good</li>
  <li>Babel is good</li>
  <li>webpack is good</li>
  <li>rollup.js is good</li>
  <li>gulp is good</li>
  <li>Metalsmith is good</li>
  <li>Handlebars is good</li>
  <li>Prettier is good</li>
  <li>npm is good</li>
  <li>yarn is good</li>
  <li>Python is good</li>
  <li>Django is good</li>
  <li>Flask is good</li>
  <li>PyPI is good</li>
  <li>PHP is good</li>
  <li>Ruby is good</li>
  <li>Rails is good</li>
  <li>Atom is good</li>
  <li>TextWrangler is good</li>
  <li>Vim is good</li>
  <li>RSS is good</li>
  <li>Server-side rendering is good</li>
  <li>Client-side rendering is good</li>
  <li>Progressive enhancement is good</li>
  <li>Being positive is good</li>
  <li>Being earnest is good</li>
  <li>Being kind is good</li>
</ul>

<p>
  There are infinite more things that are good about being a designer and
  engineer. If you read my list and thought; “Thing X is also good, why isn’t it on your list?”.
  It’s not that I don’t think thing X is good. It’s either that I; didn’t think
  of it, or don’t have enough knowledge about it to know it’s good. For those,
  I encourage you to make your own list of Good Things.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>A Library</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/a-library</link>
      <guid>https://tylergaw.com/articles/a-library</guid>
      <pubDate>Sun, 21 Apr 2019 10:30:00 EST</pubDate>
      <description>
        <![CDATA[
<p>
  I don’t read enough books. And when I do, I don’t read a wide enough variety
  of them. I’m working to fix this by doing three things:
</p>

<ol>
  <li>I’m reading more books</li>
  <li>Always having a book I want to read (that I’m actually interested in, if I don’t like it, I put it down and walk away)</li>
</ol>

<p>
  For step number two, I either buy two books at once or as soon as I
  finish a book, I go get another one. Lately, at the excellent
  <a href="https://www.mcnallyjackson.com/">McNally Jackson</a> on Prince St.
  near my apartment.
</p>

<p>
  The third thing I’m doing is documenting the book I’m reading and the books
  I’ve read in <a href="https://tylergaw.com/library">Library</a>. Nothing fancy, just a list
  book titles, authors, and the month and year I read the book. I’m not making
  an attempt to list books before 2019 right now. Just starting with what’s
  fresh in my mind.
</p>

<p>
  I decided I wanted this after reading
  <a href="https://austinkleon.com/2019/03/21/how-to-read-more-3/">Austin’s, “How to read more”</a>.
  In it, he mentions <a href="https://www.artgarfunkel.com/library/list1.html">Art Garfunkel’s</a>
  list of read books. That list is amazing. I hope to maintain
  my list for even a portion as long as he as.
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>No More Google Analytics</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/no-more-google-analytics</link>
      <guid>https://tylergaw.com/articles/no-more-google-analytics</guid>
      <pubDate>Tue, 19 Feb 2019 09:00:00 EST</pubDate>
      <description>
        <![CDATA[
<p>
  This website has had Google Analytics tracking visitors for at a least a decade.
  I’m not sure of the exact date I added it, but I’d guess 2007-2008. As I published this,
  I removed the JavaScript for it and deleted the property in the GA dashboard.
</p>
<p>
  For this site, the numbers serve no purpose. They’re a vanity metric I either
  never look at or anxiously check when I have a burst of traffic for whatever
  reason. <b>That anxious checking</b>. It’s one more set of numbers to obsess
  over. The Unread emails, Slack notifications, Tweets, favorites, texts,
  and on and on and on. Ditching one set of numbers that want everything and give
  nothing feels good.
</p>
<p>
  Also, I don’t want this site to track people in any way. You have enough of
  that. I know my use of analytics isn’t responsible for you getting ads about
  a jacket you looked at once on every other website you visit forever. But, hey
  this is now one less site that’s collecting information about where you’ve been
  on the Web. We could all use that.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>Designing Anthologies: Initial Concepts</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/designing-anthologies-concepts</link>
      <guid>https://tylergaw.com/articles/designing-anthologies-concepts</guid>
      <pubDate>Sat, 21 Apr 2018 22:30:00 EST</pubDate>
      <description>
        <![CDATA[
<p>
  Another day, another side project. My latest is <a href="https://anthologies.co/">Anthologies</a>. It lets you package links to share stories. Or–more accurate–that’s what it will do when I launch it. I bought the domain name two months ago and now I’m getting to work. I plan to share as much of my design and development process as possible. This post is a look at early design concepts and a peek into my thoughts on how I approach a new design.
</p>

<p>
  First things first. If this idea sounds familiar, it’s because it’s been a thing before. In 2012, when I was on the Readability team, we designed and built <a href="https://web.archive.org/web/20160704152104/http://readlists.com:80/">Readlists</a>. Readlists shut down in 2016. It happens. But I miss it and I know other people do to. I don’t think it’s a huge number of people, and that’s OK. My goal for Anthologies is to build a small tool that does one thing well. If only a few people use it, that’s A-OK with me. And much cheaper. (I plan to write about my approach to building this in a cost-efficient way as soon as I figure out how to do that. I’m following the serverless path right now.)
</p>

<h2>MVP</h2>
<p>
  At its core, an Anthology is a title, description, and list of urls or “entries” as I’m referring to them. For the MVP, I’m also going to expand that to include details about each entry as they’re available. Each entry can have a title, excerpt, author, publication, and image. If each entry has those or not will depend on the contents of the page at the url. I know from experience that parsing pages for those details is unpredictable. So my design needs to be flexible.
</p>

<h2>Systems Check</h2>
<p>
  Like a lot of designers, I sit down and get right to work on a new design by staring at fonts for an hour or two. For Anthologies, I wanted what I usually want from a typeface. I wanted sturdiness at small sizes and a good range of weights. I wanted a super heavy weight for stand-out titles. I also had a specific type hang-up this time around; I wanted a typeface with a two-story lowercase “g”. In early logo and UI sketches I kept drawing two-story g’s so I had it stuck in my brain and didn’t see a reason to fight it. I settled on <a href="https://typekit.com/fonts/aaux-next">Aaux Next</a>. It has a beautiful two-story g. So far I’ve used the regular, medium, black, and ultra weights.
</p>

<p>
  Another way I like to put off designing is to fiddle with grid configurations. Sketch makes this way too easy. After the proper amount fiddling, I landed on a grid that seems to work well. The grid is seven–191 pixels wide–columns with ten pixel gutters. I set row gutters at twenty pixels, and every now and then actually set text on them the right way.
</p>
<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-anthology-grid.png" alt="The seven column grid in use for the first anthologies.co concepts. It's slightly off center to the right. The gap on the left gives room for the branding banner.">
  <figcaption>
    The 7-column grid.
  </figcaption>
</figure>
<p>
  If you’re looking at the grid image you might notice it’s off-center to the right. That’s on purpose. You’ll see later that each mock-up includes a banner with branding on the left. I didn’t want that to use the grid. I only place the primary content on the grid.
</p>

<h2>The Concepts</h2>

<p>
  With ample procrastination out of the way, I started designing. For content, I used <a href="https://longform.org/posts/the-longform-guide-to-sad-retired-athletes"><i>The Longform Guide to Sad Retired Athletes</i></a>. Their collections are a perfect match for the type of content I expect will make up most Anthologies.
</p>

<h3>1. Two-up</h3>
<figure>
  <picture>
    <source srcset="https://tylergaw.com/articles/assets/post-image-1-anthology-two-up.webp" type="image/webp">
    <source srcset="https://tylergaw.com/articles/assets/post-image-1-anthology-two-up.png" type="image/png">
    <img src="https://tylergaw.com/articles/assets/post-image-1-anthology-two-up.png" alt="A early mock-up of a single Anthology on anthologies.co">
  </picture>
  <figcaption>
    Iteration 1 “Two-up”: The first mock-up I got to a done-enough state.
  </figcaption>
</figure>
<p>
  The first go at it. A lot went into this that you don’t see in the mock-up. Much shuffling of text and images happened to get to this point. I don’t know that this is “good”, but it felt like a complete thought. It felt resolved enough. It displays the Anthology content and provides links to the articles. It accomplishes the goals.
</p>
<p>
  The most valuable artifact of this iteration are the discovered interface patterns. These are building blocks for every later iteration. There’s a “cover”–made up of the title, description, and byline. The entries–made up of individual entry elements. And within those are smaller components. I think about these in <a href="http://bradfrost.com/blog/post/atomic-web-design/">Atomic design</a> terms. The cover is a organism. It includes an “intro” molecule. The intro contains title, description, and byline atoms. And so on. These might not be the exact elements I end up with, but they’re a way for me to break down the interface into distinct parts. Finding interface building blocks is a goal of my early design process.
</p>
<h4>An Organized Tangent</h4>
<p>
  Keeping my artboards, groups, and layers organized in Sketch helps me focus. Naming those items also helps me get a jump on naming elements in HTML and CSS. The names I use in Sketch, I’ll use for future class names, file names, etc. When I look at the Sketch layers palette, I see the DOM.
</p>
<p>
  The name of each design concept is the name I gave the artboard in Sketch. Those names might not stick and aren’t always descriptive, but they help me organize my thoughts.
</p>
<figure>
  <picture>
    <source srcset="https://tylergaw.com/articles/assets/post-image-anthologies-1-sketch-layers.webp" type="image/webp">
    <source srcset="https://tylergaw.com/articles/assets/post-image-anthologies-1-sketch-layers.png" type="image/png">
    <img src="https://tylergaw.com/articles/assets/post-image-anthologies-1-sketch-layers.png" alt="A screenshot of the Sketch.app layers palette for these Anthologies designs">
  </picture>
  <figcaption>
    The layers palette of the Sketch file for Anthologies mock-ups.
  </figcaption>
</figure>

<p>
  The first iteration is always the hardest. With it out of the way I started having a lot of fun with the designs. The rest of the concepts are me asking; “what if…?” and then designing answers. These aren’t meant to be separate themes to choose from. They’re exploration of possibilities. It’s me searching for interesting ways to display the same content. In that searching I find design solutions, but also more questions that lead to more design. This was also just good, fast design fun. I could do fifty of these and keep going. I won’t though, because I want to ship this thing sooner than later.
</p>

<h3>2. Grid Dark</h3>
<figure>
  <picture>
    <source srcset="https://tylergaw.com/articles/assets/post-image-2-anthology-grid-dark.webp" type="image/webp">
    <source srcset="https://tylergaw.com/articles/assets/post-image-2-anthology-grid-dark.png" type="image/png">
    <img src="https://tylergaw.com/articles/assets/post-image-2-anthology-grid-dark.png" alt="A early mock-up of a single Anthology on anthologies.co">
  </picture>
  <figcaption>
    Iteration 2 “Grid dark”: An iteration on the first mock-up where the entries float on top of the cover image.
  </figcaption>
</figure>

<p>
  This was a tiny step from the first concept. A little more modern, techy aesthetic. Instead of cropping the cover image, I let it fill the layout, and let the entries float above the background. A joke name I had for it was “Textflix”, because it reminded me of Netflix’s aesthetic. I’m light on words for this one because I think it’s just…fine. I could ship this and it’d work, but it doesn’t really move the needle for me. It invokes a healthy shrug.
</p>

<p>
  These first two iterations were dark and felt heavy. For the next concept I lightened things up in color and design elements.
</p>

<h3>3. Text Light</h3>
<figure>
  <picture>
    <source srcset="https://tylergaw.com/articles/assets/post-image-3-anthology-text-light.webp" type="image/webp">
    <source srcset="https://tylergaw.com/articles/assets/post-image-3-anthology-text-light.png" type="image/png">
    <img src="https://tylergaw.com/articles/assets/post-image-3-anthology-text-light.png" alt="A early mock-up of a single Anthology on anthologies.co">
  </picture>
  <figcaption>
    Iteration 3 “Text light”: A light-weight, brighter concept using mostly text and only a cover image.
  </figcaption>
</figure>

<p>
  Along with a lighter aesthetic, in this concept I changed grid usage. In the first two concepts, I gave the cover three columns and the entries four. In this concept, I gave the cover image more prominence. When I saw that, it felt like it needed room to breathe. With that, I gave it another half column and reduced the entries to three-and-a-half columns.
</p>
<p>
  Content-wise, I introduced entry excerpts. The layout of the first two concepts worked better without excerpts. It feels a bit off to exclude them for entries though. It seems like the reader needs a bit more of a hook to decide where they want to start.
</p>
<p>
  In the future, excerpts could be curator-configurable per Anthology.
</p>

<h3>4. Text Light Imgs</h3>
<figure>
  <picture>
    <source srcset="https://tylergaw.com/articles/assets/post-image-4-anthology-text-light-imgs.webp" type="image/webp">
    <source srcset="https://tylergaw.com/articles/assets/post-image-4-anthology-text-light-imgs.png" type="image/png">
    <img src="https://tylergaw.com/articles/assets/post-image-4-anthology-text-light-imgs.png" alt="A early mock-up of a single Anthology on anthologies.co">
  </picture>
  <figcaption>
    Iteration 4 “Text light imgs”: A tweak on Text light. This time including an image thumbnail with each entry.
  </figcaption>
</figure>
<p>
  This was the first concept I tried using an image for each entry. When I saw them in place, it felt like the cover image could go away. The title and description could shoulder the work of being the cover.
</p>
<p>
  For this concept I also tried an off-white background color. I pulled this from Readability, Instapaper, Pocket, et al. In those, a similar color tended to be used for a sepia theme. I’m luke-warm on it.
</p>
<p>
  I explored options for the branding banner here too. I wanted to see if a stark contrast between banner and content could help the banner detach and fade away. Sometimes when an element is so much different than the rest of the design, it can make it easier to consider it, then ignore. I’m still not sure if it’s working or not, but it’s interesting.
</p>

<h3>5. Text Light Two</h3>
<figure>
  <picture>
    <source srcset="https://tylergaw.com/articles/assets/post-image-5-anthology-text-light-two.webp" type="image/webp">
    <source srcset="https://tylergaw.com/articles/assets/post-image-5-anthology-text-light-two.png" type="image/png">
    <img src="https://tylergaw.com/articles/assets/post-image-5-anthology-text-light-two.png" alt="A early mock-up of a single Anthology on anthologies.co">
  </picture>
  <figcaption>
    Iteration 5 “Text light two”: There's a lot more going on in this one.
  </figcaption>
</figure>
<p>
  In this concept, I let myself toss around more design elements. Scattered orange blocks here, a few tech lines there, an angle on the cover image, and so on. With the orange blocks, I was thinking about something like a 1970s jazz album cover. Or at least how I pictured them looking. I also switched the title and entry headings to the Ultra weight of Aaux Next. They felt like they needed some extra heft.
</p>
<p>
  With the introduction of more design elements, it felt like the eye needed a little break. To give it that rest, I removed the image thumbnails for each entry.
</p>

<h3>6. Spartan</h3>
<figure>
  <picture>
    <source srcset="https://tylergaw.com/articles/assets/post-image-6-anthology-spartan.webp" type="image/webp">
    <source srcset="https://tylergaw.com/articles/assets/post-image-6-anthology-spartan.png" type="image/png">
    <img src="https://tylergaw.com/articles/assets/post-image-6-anthology-spartan.png" alt="A early mock-up of a single Anthology on anthologies.co">
  </picture>
  <figcaption>
    Iteration 6 “Spartan”: Just text and images-as-content
  </figcaption>
</figure>

<p>
  After Text Light Two with all its fiddly bits, I wanted to try one the other way. For this concept I let the content speak for itself. Text and images as content with–almost–no other design elements. I kept one element in place, the subtle border between the cover and entries. You might also notice I increased all the font sizes. The title and each entry title felt like they needed more oomph to take center stage. I was looking at–and taking cues from–the New York Times on this one.
</p>
<p>
  At this point, this concept feels the strongest in terms of practicality and broad use. When I start building, I’ll most likely use this as a guide.
</p>

<h3>7. Party</h3>
<figure>
  <picture>
    <source srcset="https://tylergaw.com/articles/assets/post-image-7-anthology-party-1.webp" type="image/webp">
    <source srcset="https://tylergaw.com/articles/assets/post-image-7-anthology-party-1.png" type="image/png">
    <img src="https://tylergaw.com/articles/assets/post-image-7-anthology-party-1.png" alt="A early mock-up of a single Anthology on anthologies.co">
  </picture>
  <figcaption>
    Iteration 7 “Party 1”: Just throwing everything at the wall to see what sticks.
  </figcaption>
</figure>

<p>
  Oooooh shit, here we go! I needed to see one like this. I took about a day break between Spartan and this concept to clear my head. Then I threw everything at the wall. I’ve been reading posts on <a href="https://theoutline.com">The Outline</a> and enjoying the design they’re doing. I lifted ideas wholesale from them on this concept. That’s a fun thing about doing early concepts like this, it’s OK steal. It’s more about serious play than producing a finished piece.
</p>
<p>
  I don’t know if a concept like this will make it to browsers, but it could. I don’t think it’s “bad.” Depending on the tone of the Anthology, something like this could work well. I plan to explore this type of aesthetic more as I go.
</p>

<h2>HTML/CSS Here We Come</h2>
<p>
  Designs like this are never “done”, but this one is ready for the next step: browsers. Using Spartan as a base, I’ll build an Anthology page using static-content. As soon as you get in a browser you learn a ton. The design will shift and shimmy from the static mocks here to meet the needs of its ultimate home.
</p>

<p>
  As I mentioned before, I have more posts I want to write about my process. When I dive into these projects I let myself get pulled into the weeds in myriad areas of design and development. It’s tons of fun and it’s important to share. Keep an eye out for more.
</p>
        ]]>
      </description>
    </item>

    <item>
      <title>Jribbble Three and Helping People OAuth</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/jribbble-three-and-oauth</link>
      <guid>https://tylergaw.com/articles/jribbble-three-and-oauth</guid>
      <pubDate>Sun, 15 Apr 2018 22:30:00 EST</pubDate>
      <description>
        <![CDATA[
<p>
  In December 2017, Dribbble <a href="http://developer.dribbble.com/changes/2017-12-05-api-v2/">announced</a> version two of their API. In the same post, they set March 26, 2018 as the deprecation date of version one. I’ve maintained Jribbble for almost <a href="https://github.com/tylergaw/jribbble/commit/0895bed9b12d25dbf9ec04073acf64823a512267">8 years now</a>. It’s a JavaScript library to help fetch data from the Dribbble API. In that time it’s gone through plenty of iteration to keep up with Dribbble API changes. Jribbble 3.0 continues that trend.
</p>

<p>
  To date, Dribbble API changes have been additive. API features have given users more access to more of data via the API. As Dribbble explained in <a href="http://developer.dribbble.com/changes/2017-12-05-api-v2/">their post</a>, version two goes the other way, it removes a lot of functionality.
</p>

<p>
  The reduced functionality seems fine though. In my time working on Jribbble, most conversations I’ve had with designers are about using it to get their own shots. That’s still available, and the focus of API version two. My guess is most users of Jribbble won’t lose out. They will need to make code changes to continue using Jribbble though.
</p>

<p>
  I’ve had a few conversations with folks on <a href="https://github.com/tylergaw/jribbble/issues/18">GitHub</a> and email about updating to Jribbble 3.0. The biggest change is that Jribbble is no longer a jQuery plugin. It’s now a standalone library. I write more about the decision to remove jQuery later in this post.
</p>

<p>
  The other big change is the shape of Jribbble’s public API methods. Here’s a 2.x vs 3.x example of a user getting their own shots with Jribbble:
</p>

<pre><code class="language-javascript">// Jribbble 2.x
$.jribbble.setToken("&lt;access_token&gt;");
$.jribbble.users("tylergaw").shots().then(function(shots) { /* Work with shots JSON */ });</code></pre>

<pre><code class="language-javascript">// Jribbble 3.x
jribbble.setToken("&lt;access_token&gt;");
jribbble.shots(function(shots) { /* Work with shots JSON */ });</code></pre>

<p>
  The Jribbble API is no longer chainable or promise-based. Again, I detail more of the code decisions later in this post. We can still do the same thing, but now in a more concise way.
</p>

<h2 id="a-new-barrier-to-entry">A New Barrier to Entry</h2>
<p>
  With Dribbble API version one, we could make requests using read-only client access tokens. When creating an application via the Dribbble UI, they would generate the client access token. We could then use that token to make requests to the API.
</p>
<p>
  In version two, those easy-to-use, auto-generated client access tokens are gone. Now, all requests must use a <a href="https://stackoverflow.com/questions/25838183/what-is-the-oauth-2-0-bearer-token-exactly">bearer token</a>. To get a bearer token, you have to go through the <a href="http://developer.dribbble.com/v2/oauth/#web-application-flow">OAuth2 flow</a>.
</p>
<p>
  Depending on your experience with OAuth, you might read that and think; “Sure, no problem. OAuth is easy” or “uhhhh, what’s an OAuth?” or somewhere in between. At any experience level, if you only want to make a single API request to display your Dribbble shots on your personal site, it’s a rigmarole.
</p>
<p>
  When I first read about the new auth I thought, “welp, Jribbble is dead. As is all client-side JS access to the Dribbble API.” I thought this beause in most cases, you shouldn’t put OAuth-generated bearer access tokens in public code. Doing so makes them available for anyone to find and act on behalf of token owners in any–malicious–way they want.
</p>
<p>
  <a href="https://twitter.com/tylergaw/status/951532274482368512">I asked the Dribbble folks</a> about this and they let me know that in this case, putting access tokens in public code would be OK because they’re read-only. A bad actor could still snag your access token and use it to abuse the API on your behalf. I’d guess by making a ton of nonsense requests. The Dribbble API has rate-limiting in place to help mitigate this. At the worst, Dribbble could revoke your access token or disable your app. You’d then need to go back though the process to get a new token and/or create a new app.
</p>

<p>
  This change still didn’t sit well with me though. What are less code-savvy folks supposed to do? The main users of Dribbble and Jribbble are Designers. There’s a good chance many of them don’t have experience creating and deploying OAuth flows. That’s exactly who I’ve had conversations with over the years. And that’s who has reached out to me since the API version two announcment. Folks that know enough code to copy, paste, and tweak a snippet of JS, but not enough code to build and deploy a custom OAuth setup.
</p>
<p>
  With that, I set out to come up with a way to help them. Because that’s what we do here.
</p>

<h3 id="options">Options</h3>
<p>
  My first idea was to create a site where people would authenticate with
  a Dribbble application that I owned. They’d show up, tap “connect”, confirm
  with Dribbble, then get directed back to my site where they’d get an access token.
</p>
<p>
  That would get the job done, but felt wrong. Having people authenticate through my app would mean their access tokens would be in my hands. If I deleted the
  app—or my Dribbble account—their tokens would stop working. If a bad actor generated a token then used it to abuse the API, my app could get suspended. Again, taking every user’s access token with it.
</p>
<p>
  I needed a way for every Jribbble user to be able to generate access tokens using an application they owned.
</p>
<h3 id="glitch">Glitch</h3>
<p>
  If you haven’t used it yet, <a href="https://glitch.com/">Glitch</a> is an excellent tool for sharing and learning code. What sets Glitch apart from similar code-sharing tools is that it gives you the ability to create and share web servers.
</p>
<p>
  That’s exactly what I needed for this OAuth problem. I could write the server
  necessary for the OAuth flow, then make it available for anyone to–in the parlance of Glitch–“remix” the code to get full access to it. And they can do so without even creating a Glitch account.
</p>
<p>
  So that’s what I did. <a href="https://jribbble.glitch.me">jribbble.glitch.me</a>
  is template that anyone can remix to get their own OAuth flow for generating bearer tokens to use with Dribbble API version two.
</p>
<p>
  I won’t say that writing a server for this is “easy” because it’s only easy if
  you already know how to do it. But, I hope that this will help folks
  see that it’s attainable. To demonstrate, I’m going to plop the entire server here. The code is under 100 lines. The rest is comments to help people understand what’s happening at each step along the way:
</p>
<pre><code class="language-javascript">const express = require("express");
const tiny = require("tiny-json-http");
const nunjucks = require("nunjucks");
const cookieParser = require("cookie-parser");

// We store the relavant Dribbble URLs here for convenience.
// authUrl is passed along to our template in the `/` handler below.
const authUrl = "https://dribbble.com/oauth/authorize";
const tokenUrl = "https://dribbble.com/oauth/token";

// The path of the Callback URL we set when creating our application on Dribbble.
// We reference this URL below in an app.get
const callbackUrl = "/oauth_callback";

// This isn't oauth or dribbble specific, but storing the value of the original
// Glitch project to check if we're seeing the original or a remix.
const ogGlitchUrl = "https://jribbble.glitch.me";

// These environment variables need to be set in the `.env` file. Look to your left 👈
// client id is safe as public value, if those are seen, it's OK.
// client_secret is not safe for public. You need to keep that private at all times.
const client_id = process.env.DRIBBBLE_APP_ID;
const client_secret = process.env.DRIBBBLE_APP_SECRET;

// When we receive an access_token from the api.dribbble.com server, we'll store it
// in this variable.
// Q: Why do we use `let` here instead of `const`?
// A: We use `let` so we can reassign `access_token` to a new value. Here, we set
//    an initial value of `null`. Below in the `/oauth_callback` handler we set
//    it to a new value of the access_token from the server.
let access_token = null;

// Standard express setup code.
const app = express();
app.use([express.static("public"), cookieParser()]);

// Set up our template library.
// I–Tyler–didn't look too deep into this, this block of code came from the nunjucks
// docs and got me up and running, so good enough for me at this time.
nunjucks.configure(["views", "public"], {
  autoescape: true,
  express: app
});

// This is our homepage and the page that does most of the work.
app.get("/", (req, res) => {
  const pageUrl = `https://${req.get("host")}`;

  // Here, we'll try to set the access_token from a cookie.
  // In the callback handler below, we set the access_token cookie on successful auth.
  // This isn't something you need to do in your Jribbble uses.
  access_token = req.cookies.access_token;

  // We use render so we can pass along variables to our template.
  // In index.html any time you see {{thing}} or {% %}, we're referencing
  // a variable we set here.
  res.render("index.html", {
    authUrl,
    accessToken: access_token,
    clientId: client_id,
    // Just in case we've hit an authentication error we'll use this to display a message in the template
    error: req.query.error,
    // We create new boolean value here so we don't send the actual secret to the template.
    // Note: I–Tyler–am not sure this is 100% necessary, but it felt best to be overly
    // cautious when our app secret. You don't want anyone to have that.
    hasClientSecret: client_secret.length,

    pageUrl,
    isRemix: pageUrl !== ogGlitchUrl,
    callbackUrl
  });
});

// This is where our Dribbble applications will come back to after a GET to authUrl
app.get(callbackUrl, async (req, res) => {
  const data = {
    code: req.query.code,
    client_id,
    client_secret
  };

  try {
    // We required `tiny` above in tiny-json-http
    // That's a small http library I preferred to use https://github.com/brianleroux/tiny-json-http
    // It's not the only way to make requests, there are many different was to accomplish
    // this http post request to Dribbble
    // Note we are using async/await here. If you're unfamiliar, that's OK. The number one thing
    // to know is `await` makes this code act like it's pausing here and waiting for the http
    // request to complete before moving on to the following lines of code.
    const { body } = await tiny.post({ url: tokenUrl, data });

    // As mentioned above, here we're assigning access_token a new value that is your
    // shiny oauth access token that gives you public read access to your Dribbble account
    access_token = body.access_token;

    // NOTE: Setting a cookie want be required in your uses of Jribbble, because you will
    // include the access_token in your JavaScript.
    res.cookie("access_token", access_token);

    // We don't want to stay on the /oauth_callback page, so redirect back home.
    res.redirect("/");
  } catch (err) {
    // If we hit an error we'll handle that here
    console.log(err);
    res.redirect("/?error=😡");
  }
});

app.get("/logout", (req, res) => {
  res.clearCookie("access_token");
  res.redirect("/");
});

app.listen(process.env.PORT);</code></pre>

<p>
  Again, you can remix this at <a href="https://jribbble.glitch.me">jribbble.glitch.me</a>
  to get full access to the code.
</p>
<p>
  Another cool thing. This example is intended for the Dribbble API, but with a
  few minor changes, it will work for any standard OAuth flow.
</p>

<p>
  If you use that and run into trouble–or think it’s great–let me know on <a href="https://twitter.com/tylergaw">Twitter</a>.
</p>

<h2 id="the-jribbble-rewrite">The Jribbble Rewrite</h2>
<p>
  The main part of this project was writing Jribbble 3.0.
  <a href="https://github.com/tylergaw/jribbble/blob/v2.0.4/src/jribbble.js">Jribbble 2.x</a> has six public methods to access resources from Dribbble
  API version one. Many of those methods have subresources like comments, likes, et al. It also has a chainable, promise-based interface. All these things add a non-trivial amount of plumbing code to make them possible.
</p>
<p>
  Dribbble API version two reduces functionality to getting the current
  user’s profile, shots, and projects. For Dribbble-approved applications you can get a user’s likes and a list of popular shots.
</p>
<p>
  The reduced functionality meant I could cut most of Jribbble’s code. The first
  thing I did was audit the usage of jQuery. The only jQuery methods in use were; <code>$.ajax</code>, <code>$.Deferred</code>, and <code>$.extend</code>.
  Because of the limited usage, I decided Jribbble 3.0 would not use jQuery.
</p>
<p>
  It also didn’t feel necessary to use any type of transpiling process. So no Babel or
  TypeScript or the like. I would only write good-ole browser JavaScript. Also, I wanted 3.0 to work in as many browsers as possible, so I only used old-timey
  JS. No arrow functions, no <code>let</code> or <code>const</code>, etc. It wasn’t that bad. This is a small library so
  restraining myself to older JS wasn’t a problem. And writing it directly instead of relying on Babel keeps the file size smaller.
</p>
<p>
  I could have used newer features and still probably ended up with wide-enough support, but it was a fun challenge. And it reminded me of writing JS in years past.
</p>

<p>
  This was enough of a rewrite that I opened a new, blank file and started
  writing instead of reusing 2.0 code.
</p>

<h3 id="30-details">3.0 Details</h3>
<p>
  Like 2.0, this version also has six public methods;
  <code>setToken</code>, <code>shots</code>, <code>user</code>, <code>projects</code>, <code>likes</code>, and <code>popular</code>. Each method is available on the window-scoped <code>jribbble</code> object.
</p>
<p>
  <code>setToken</code> is the same as in 2.0. It’s how users give Jribbble
  their access tokens.
</p>
<pre><code class="language-javascript">jribbble.setToken("12345");</code></pre>
<p>
  For 3.0 I decided to also allow users to provide their token as an option when calling any of the other methods. For example:
</p>

<pre><code class="language-javascript">jribbble.shots({token: "12345"}, callback);</code></pre>

<p>
  That accomplishes the same thing as <code>setToken</code> in a more concise way.
</p>
<p>
  From here, I’ll detail three internal functions that handle the lion’s share of what Jribbble can do.
</p>
<h3 id="function-one-get">Function one: <code>get</code></h3>
<p>
  I took it back in time for this one. For day-to-day work I use <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">fetch</a> or a wrapper like <a href="https://github.com/axios/axios">Axios</a> to make network requests. For Jribbble I didn’t want to use a third-party library or polyfills for requests. So instead of fetch, I went back to <code>XMLHttpRequest</code>.
</p>
<p>
  All Jribbble requests have the same requirements so I was able to abstract the functionality to a common function. I use the internal <code>get</code> function for all requests to the Dribbble API:
</p>
<pre><code class="language-javascript">var get = function(path, callback) {
  var url = "https://api.dribbble.com/v2/" + path;
  var req = new XMLHttpRequest();
  req.addEventListener("load", function() {
    if (callback) {
      if (typeof callback === "function") {
        var ret = {};

        if (this.status < 400) {
          try {
            ret = JSON.parse(this.responseText);
          } catch (err) {
            ret = {
              error: "There was an error parsing the server response as JSON"
            };
          }
        } else {
          ret = {
            error:
              "There was an error making the request to api.dribble.com.",
            status: this.status
          };
        }

        callback(ret);
      }
    }
  });
  req.open("GET", url);
  req.setRequestHeader("Authorization", "Bearer " + accessToken);
  req.send();
};</code></pre>

<p>
  Not bad. It’s a standard <code>XMLHttpRequest</code> GET request with an Authorization header. The bulk of the function is guard code to protect against type errors, JSON parsing problems, and network errors. It does what it can to fail with grace if there is a problem.
</p>
<p>
  I use the Authorization header to send along the user’s Dribbble access token with every request.
</p>

<p>
  The <code>get</code> function will work back to IE7. I figured It’d be safe to not include the old fork to check for <code>ActiveXObject</code>. It’s been years since I’ve typed that, it’s giving me a good chuckle to see it here.
</p>

<h3 id="function-two-createApiMethod">Function two: <code>createApiMethod</code></h3>
<p>
  The <code>user</code>, <code>projects</code>, <code>likes</code>, and <code>popular</code> methods all do the same thing. They process N-number of arguments, make a request to the Dribbble API, and call a user-provided callback. The callback receives a single argument, the JSON response from the request.
</p>
<p>
  Because they’re all similar, I didn’t want to have to repeat the same code when defining each method. Instead, I abstracted the functionality to <code>createApiMethod</code>.
</p>
<pre><code class="language-javascript">var createApiMethod = function(path) {
  return function() {
    var args = processArguments.apply(null, arguments);
    get(path + args.query, args.callback);
  };
};</code></pre>
<p>
  The <code>path</code> parameter is passed along to <code>get</code> to build the URL to the Dribbble API.
</p>
<p>
  I then define each public method as a member of the <code>api</code> object. Each is a function with a unique path based on its needs.
</p>
<pre><code class="language-javascript">var api = {
  ...
  user: createApiMethod("user"),
  projects: createApiMethod("user/projects"),
  likes: createApiMethod("user/likes"),
  popular: createApiMethod("popular_shots")
};</code></pre>
<p>
  This doesn’t provide any extra functionality. It only makes it so I don’t
  have to repeat as much code when defining methods. <code>createApiMethod</code> returns a function. Let’s look at what’s happening in the body of that function.
</p>
<pre><code class="language-javascript">var args = processArguments.apply(null, arguments);
get(path + args.query, args.callback);</code></pre>
<p>
  The first line is the heavy lifting. It’s function number three that I’ll write about next. For now, it’s important to know that it returns an object with <code>query</code>, <code>callback</code>, and <code>resourceId</code> keys.
</p>
<p>
  Those values, plus the <code>path</code> argument, let us build the arguments needed to make the request to Dribbble with <code>get</code>.
</p>

<h3 id="function-three-processArguments">Function three: <code>processArguments</code></h3>
<p>
  For me, this is the most interesting bit of code in Jribbble. Every public Jribbble method except <code>setToken</code> uses <code>processArguments</code>. Its purpose is to inspect all arguments passed to Jribbble methods.
</p>
<p>
  To show its usefulness, consider the following usage examples:
</p>
<pre><code class="language-javascript">jribbble.shots(
  "456789",
  {token: "12345"},
  function(shotObject) { /* Work with JSON */ }
);

jribbble.shots(
  {token: "12345", page: 3, per_page: 5},
  function(shotsArray) { /* Work with JSON*/}
);

jribbble.projects(
  function(projectsArray) { /* Work with JSON */ },
  {token: "12345"}
);

jribbble.user(
  function(userObject) { /* Work with JSON */ }
);
</code></pre>
<p>
  Notice in each I’m providing a different number of arguments of different types. And in the case of <code>projects</code> I’m providing the arguments in a different order. This type of flexibility isn’t possible with a typical method signature. One where I define each parameter when I create the method.
</p>
<p>
  For example, imagine a method signature for <code>jribbble.shots</code> like this:
</p>
<pre><code class="language-javascript">var shots = function(shotId, options, callback) { /* Do the work */ };</code></pre>

<p>
  That would work for the first usage example, but what about the second? What if I don’t need a single shot and I don’t need to provide an <code>options</code> argument?
</p>
<p>
  This is where <code>processArguments</code> comes into play.
</p>

<p>
  First, notice when I call the function in <code>createApiMethod</code> I use <code>apply</code>:
</p>
<pre><code class="language-javascript">var args = processArguments.apply(null, arguments);</code></pre>

<p>
  This took me a few minutes to get my head around. It’s also hard to write about, but here goes. I need the function that <code>createApiMethod</code> returns to take zero to
  three arguments. The Jribbble user, provides those. Using <code>apply</code> let me pass along the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments"><code>arguments</code></a> object received from calls to public API methods. Then, within
  <code>processArguments</code> I have access to that original <code>arguments</code> object. Once I have it, I convert it to an array for manipulation:
</p>
<pre><code class="language-javascript">var args = [].slice.call(arguments);</code></pre>

<p>
  An important thing to note is <code>processArguments</code> doesn’t define parameters:
</p>
<pre><code class="language-javascript">var processArguments = function() {...};</code></pre>

<p>
  Again, that’s not something I can define ahead of time. This whole dance lets
  the public methods be flexible in number and order of parameters. It
  lets them say “yeah, whatever you got, I’ll take it.”
</p>

<p>
  I mentioned earlier, <code>processArguments</code> returns an object with <code>query</code>, <code>callback</code>, and <code>resourceId</code> keys. The work of the function focuses on creating that object.
</p>

<p>
  While I can’t define parameters, I do know a few things about potential arguments. I know any callback should be a function and any options should be an object. I also know <code>resourceId</code> is the identifier for a Dribbble shot. A shot id can be a string or a number.
</p>

<p>
  I use that knowledge to inspect each item in the <code>args</code> array I created from the <code>arguments</code> object. Depending on the type I assign the item’s value to a local variable.
</p>
<pre><code class="language-javascript">...
var resourceId = null;
var opts = {};
var callback = function() {};
...
for (var i = 0; i < args.length; i += 1) {
  switch (typeof args[i]) {
    case "string":
    case "number":
      resourceId = args[i];
      break;
    case "object":
      opts = args[i];
      break;
    case "function":
      callback = args[i];
  }
}</code></pre>
<p>
  That snippet sets two out of three variables this function needs to return. <code>resourceId</code> and <code>callback</code> are ready to to go. I only use <code>resourceId</code> in <code>jribbble.shots</code>. The Dribbble API has different paths if you’re requesting a single shot or a list of shots. I use the value–or lack of a value–of <code>resourceId</code> to <a href="https://github.com/tylergaw/jribbble/blob/3.0.0/src/jribbble.js#L104">determine the path</a>.
</p>
<p>
  What about the third item, <code>query</code>? Also what is this <code>opts</code> object in favor of?
</p>
<p>
  In earlier examples, I showed providing an object with token, page, and per_page keys to public methods. Token is the most important one. If a user doesn’t provide an access token, they can’t make requests. After the for loop I check for a token key on the <code>opts</code> object:
</p>
<pre><code class="language-javascript">if (opts.token) {
  accessToken = opts.token;
}</code></pre>
<p>
  I define <code>accessToken</code> at the root level of the main Jribbble function. This is how I provide the flexibility of setting token with the <code>setToken</code> method or via an options object.
</p>
<p>
  At this point in <code>processArguments</code>, if there’s no value for <code>accessToken</code> there’s no reason to continue. I throw an error and let the user know they need to update their code.
</p>
<pre><code class="language-javascript">if (!accessToken) {
  throw new Error(
    "jribbble needs a valid access token. You can either include this as an option: jribbble.shots({token: '1234'}) or with jribbble.setToken('1234')"
  );
}</code></pre>
<p>
  If the user provided <code>page</code> or <code>per_page</code>, those keys will also be on the <code>opts</code> object. <code>query</code> needs to be a string that I can append to the URL of any request. So, I need to create that string if necessary. I know that “page” and “per_page” are the only query parameters allowed because they’re <a href="http://developer.dribbble.com/v2/#pagination">documented</a>. I can use that knowledge to build the string based on the user-provided values.
</p>
<pre><code class="language-javascript">var params = ["page", "per_page"]
  .map(function(p) {
    return opts[p] ? p + "=" + opts[p] : null;
  })
  .filter(function(i) {
    return i;
  })
  .join("&");</code></pre>
<p>
  For each item in the array check to see if that key exists in <code>opts</code>. If it does, return a string beginning with the item plus an equals sign plus the value in <code>opts</code>. If the key isn’t in <code>opts</code> return <code>null</code>.
</p>
<p>
  At this point, we could have an array that looks like:
</p>
<pre><code class="language-javascript">["page=4", null]</code></pre>
<p>
  That’s if the user set a <code>page</code>, but not a <code>per_page</code> value.
</p>
<p>
  Next, I use <code>filter</code> to remove any <code>null</code> values. And finally join the items of the array with an ampersand between each. In the above example we’d end up with “page=4”.
</p>
<p>
  <code>processArguments</code> is ready to return an object with the three keys needed. I also need to do one last thing for <code>query</code>. If the user doesn’t need a query, return an empty string. If they do, prefix the <code>params</code> string with a “?” so it’s ready to append to a URL.
</p>

<pre><code class="language-javascript">return {
  resourceId: resourceId,
  callback: callback,
  query: params ? "?" + params : ""
};</code></pre>

<p>
  And that’s <code>processArguments</code>. Syntax and even code-wise it’s not fancy, but it does a job and does it in what I think is a clear way.
</p>

<h2>One more iteration complete</h2>
<p>
  This was another iteration on a little library I keep going. It has a small userbase, but I know it provides value for them when it’s needed. As the Dribbble API changes, I’ll keep Jribbble in sync as best I can.
</p>
<p>
  Again, the OAuth flow project is at <a href="https://jribbble.glitch.me">jribbble.glitch.me</a> or just <a href="https://jribbble.com">jribbble.com</a>. The Jribbble source is <a href="https://github.com/tylergaw/jribbble">available on GitHub</a>. If you run into trouble, please open an issue or submit a pull request.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>Dynamic Redux Reducers</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/dynamic-redux-reducers</link>
      <guid>https://tylergaw.com/articles/dynamic-redux-reducers</guid>
      <pubDate>Tue, 09 Jan 2018 04:00:00 EST</pubDate>
      <description>
        <![CDATA[
<p>
  This post is specific to a need I had on recent React / Redux project. It’s a
  common need and one I’d run into before, but this was the first time I needed to
  come up with a solution for it. This was difficult for me. I had to slow down
  and take time to internalize what I was trying to do and all the pieces involved.
  My hope is that this post will help someone else also working to figure
  this out.
</p>
<p>
  I’ll detail my process in this post. Here’s a
  <a href="https://qk3n9xmm3w.codesandbox.io">live demo</a> and an
  <a href="https://codesandbox.io/s/qk3n9xmm3w">editable sandbox</a>.
  The best way to see the effects is to use the <a href="https://github.com/zalmoxisus/redux-devtools-extension">Redux DevTools Chrome extension</a>.
</p>

<p>
  If you’re reading this I’m going to assume you have knowledge of Redux and
  are using it with React by way of <code>react-redux</code>. I'm also going to
  assume you’re looking for a solution to a similar problem.
</p>

<h2>What am I trying to do and why?</h2>
<p>
  In standard Redux usage, you provide reducer functions at the time you create
  the store with <code>createStore</code>. I wanted a way to add reducer functions
  later, on demand.
</p>
<p>
  A lot of folks need this because their reducers are not available at
  <code>createStore</code> time due to code-splitting. That’s a perfect use for
  dynamic reducers.
</p>
<p>
  My project doesn’t use code-splitting. For this, dynamic reducers were a preference. I
  didn’t want to spread info about modules throughout the project structure.
  I wanted each feature to live in a directory, isolated as much as possible.
  That meant co-locating reducers, components, styles, and so on. I could do
  that and still import the reducers to the main reducer creation, but that
  would couple module reducers to the main reducer.
</p>

<h2>Existing solutions</h2>
<p>
  In my Googling for an existing solution I landing on
  <a href="https://stackoverflow.com/a/33044701">this Stack Overflow question and answer</a>.
  The answer is from Dan Abramov so I knew it was a way to go. My solution uses
  most of the code from that answer.
</p>
<p>
  In Dan’s answer, it all made sense to me until his example of how to inject
  reducers. I’m using React Router, but I don’t define routes the way he described. I didn’t
  want to have to change how I defined my routes for this. I also couldn’t find
  official documentation for methods he used in his example so I wanted to avoid
  copy / paste. I also wanted to fully understand the code I was adding to my project.
</p>
<p>
  It’s worth mentioning two projects I came across in my search.
  <a href="https://github.com/ioof-holdings/redux-dynamic-reducer">redux-dynamic-reducer</a>
  and <a href="https://github.com/asteridux/paradux">paradux</a>. I didn’t try
  either of them because I didn’t see the need in adding another dependency, but
  they might work for you.
</p>

<h2>What the demo shows</h2>
<p>
  The <a href="https://qk3n9xmm3w.codesandbox.io">demo</a> shows a simple page
  with a link to <code>/records</code>. When page loads, the Redux state tree
  contains two keys. One for each reducer function introduced at store creation.
</p>
<p>
  There’s a link to the <code>/records</code> page. When you navigate to that
  page, I add another reducer function for Records. In the rest of this post
  I’ll decribe how I do that.
</p>

<h2>The code</h2>
<p>
  You can follow along in the <a href="https://codesandbox.io/s/qk3n9xmm3w">CodeSandbox</a>.
  I’ll start with creating the root reducer in <code>/rootReducer.js</code>.
</p>

<pre><code class="language-javascript">import { combineReducers } from "redux";
import layout from "./reducers/layout";
import home from "./reducers/home";

/**
 * @param {Object} - key/value of reducer functions
 */
const createReducer = asyncReducers =>
  combineReducers({
    home,
    layout,
    ...asyncReducers
  });

export default createReducer;</code></pre>

<p>
  I pulled this code from Dan’s <abbr title="Stack Overflow">SO</abbr> answer.
  It has two reducer functions; <code>layout</code> and <code>home</code>.
  They’re global reducers, not module level, so they fit well in the root reducer.
</p>
<p>
  The key detail here is the <code>asyncReducers</code> parameter. Adding the
  contents of it to the object given to <code>combineReducers</code> is
  how we add reducers later.
</p>
<p>
  Next up is store creation in <code>/initializeStore.js</code>. Again, most
  of this code is from Dan’s example.
</p>

<pre><code class="language-javascript">import { createStore } from "redux";
import createReducer from "./rootReducer";

const initializeStore = () => {
  const store = createStore(createReducer());

  store.asyncReducers = {};
  store.injectReducer = (key, reducer) => {
    store.asyncReducers[key] = reducer;
    store.replaceReducer(createReducer(store.asyncReducers));
    return store;
  };

  return store;
};

export default initializeStore;</code></pre>

<p>
  The first line of <code>initializeStore</code> is where we create the Redux
  store with the initial reducers from <code>createReducer</code>. In standard
  Redux usage, this is all you’d need. The store is set up and ready with the
  <code>home</code> and <code>layout</code> reducers.
</p>
<p>
  <code>createStore</code> returns a plain object, so we’ll take advantage of that
  by tacking helpful items onto it. We’ll use <code>store.asyncReducers</code> to house
  our dynamic reducers. With <code>store.injectReducer</code> I deviate from
  Dan’s example. The function does the same thing as his <code>injectAsyncReducer</code>,
  but I attach it to the <code>store</code> object for convenience that I’ll
  show later.
</p>
<p>
  <code>injectReducer</code> has two responsibilities. First, store all dynamic
  reducers in <code>asyncReducers</code>. This ensures that each time we invoke
  <code>injectReducer</code> we don’t lose other dynamic reducers. Next up
  is the main work. <a href="https://redux.js.org/docs/api/Store.html#replaceReducer"><code>replaceReducer</code></a>
  isn’t custom, it’s part of Redux. It does does what it says on the tin.
  Invoking it replaces the reducer function with one you give it.
</p>

<h3>Where things got tricky for me</h3>
<p>
  At this point everything seemed straightforward to me, but then I got lost fast. I
  have a store, I have a function to add new reducers. But where can I access
  that function to invoke it? In all my frantic Googling, I couldn’t find an
  example that worked for my setup. So, I sat down to figure out a solution.
</p>
<p>
  It took me a while to figure out where I could access that <code>store</code>
  object. I had clues though. In my entry point file <code>/index.js</code> I
  use the <code>Provider</code> component. This is standard for React / Redux projects.
</p>
<pre><code class="language-javascript">import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import initializeStore from "./initializeStore";
import App from "./App";

const store = initializeStore();
render(
  &lt;Provider store={store}&gt;
    &lt;App /&gt;
  &lt;/Provider&gt;,
  document.getElementById("root")
);</code></pre>

<p>
  Giving the <code>store</code> to <code>Provider</code> makes it available to
  all child components by way of the <code>connect</code> function. I read more about it and learned that <code>store</code>
  is also available in the <code>context</code> of each component.
  If you’ve read anything about
  React <code>context</code>, you’ve read that you probably shouldn’t use it.
  For my purposes here it seemed isolated enough to be OK. Time will tell if that’s correct or not.
  More details on my <code>context</code> usage to later.
</p>

<h2>Putting the pieces together</h2>

<p>
  I want to use as little code as possible to add reducers. I do that with a
  <a href="https://reactjs.org/docs/higher-order-components.html">higher-order component</a>
  in <code>/withReducer.js</code>.
</p>

<pre><code class="language-javascript">import React from "react";
import { object } from "prop-types";

const withReducer = (key, reducer) => WrappedComponent => {
  const Extended = (props, context) => {
    context.store.injectReducer(key, reducer);
    return &lt;WrappedComponent {...props} /&gt;
  };

  Extended.contextTypes = {
    store: object
  };

  return Extended;
};

export { withReducer };</pre></code>

<p>
  And example usage in <code>routes/Records/Records.js</code>:
</p>

<pre><code class="language-javascript">import { withReducer } from "../../withReducer";
import reducer from "./ducks";

const Records = () => (...);
export default withReducer("records", reducer)(Records);</code></pre>

<p>
  I’ll start with usage in <code>Records.js</code>. I import the records
  reducer from <code>routes/Records/ducks/index.js</code>. The reducer doesn’t
  do much. It sets hard-coded initial state, then returns it as-is.
  The component acts like a container component.
  I could <code>connect</code> it, but for the purposes of this
  demo, left it out.
</p>
<p>
  The pertinent bit is the last line. There I invoke <code>withReducer</code> and
  provide it a key of “records” and the record reducer. Then I invoke the
  returned function, providing the <code>Records</code> component.
</p>
<p>
  <code>Records</code> is a React component I import to use as
  the value of the <code>component</code> property of a React Router <code>&lt;Route /&gt;</code>.
</p>

<h3>The withReducer component</h3>
<p>
  <code>withReducer</code> is a <a href="https://reactjs.org/docs/higher-order-components.html">Higher-Order Component</a>.
  The <code>key</code> parameter becomes the key in the Redux state tree.
  The <code>reducer</code> parameter is the reducer to add. It returns a function
  that accepts a single parameter, <code>WrappedComponent</code>. That’s expected
  to be a valid React component. In the earlier usage example, that’s the
  <code>Records</code> component.
</p>
<p>
  I’ll jump ahead to an important part of <code>withReducer</code> that was
  new to me and might be confusing.
</p>
<pre><code class="language-javascript">...
Extended.contextTypes = {
  store: object
};
...</code></pre>
<p>
  <code>Extended</code> is a stateless component, so it must define
  a <code>contextTypes</code> property to gain access to <code>context</code>.
  From the React docs:
</p>
<blockquote>
  <p>
    Stateless functional components are also able to reference context if contextTypes is defined as a property of the function.
  </p>
  <cite>
    <a href="https://reactjs.org/docs/context.html#referencing-context-in-stateless-functional-components">reactjs.org/docs/context.html#referencing-context-in-stateless-functional-components</a>
  </cite>
</blockquote>

<p>
  In <code>contextTypes</code> I defined the property I want to access in the
  component, <code>store</code>. That uses the
  <code>object</code> type from the <code>prop-types</code> library.
</p>

<p>
  When a component defines a <code>contextTypes</code> property, it receives
  a second parameter, <code>context</code>. That’s visible in
  the <code>Extended</code> signature:
</p>
<pre><code class="language-javascript">...
const Extended = (props, context) => {...}
...</code></pre>

<p>
  <code>Extended</code> now has access to the <code>store</code> object. That’s
  because <code>&lt;Provider store={store}&gt;</code> in <code>/index.js</code>
  makes it available to all child components via <code>context</code>.
</p>

<p>
  This happens in the <code>Provider.js</code> source with
  <a href="https://github.com/reactjs/react-redux/blob/master/src/components/Provider.js#L26"><code>getChildContext</code></a>
  and <a href="https://github.com/reactjs/react-redux/blob/master/src/components/Provider.js#L52"><code>childContextTypes</code></a>.
  That code is good reading if you’re looking for examples of <code>context</code> usage.
</p>

<p>
  In <code>initializeStore.js</code> I created a function on the store object,
  <code>store.injectReducer</code>. Now, I use that to add the new reducer:
</p>

<pre><code class="language-javascript">...
const Extended = (props, context) => {
  context.store.injectReducer(key, reducer);
  return &lt;WrappedComponent {...props} /&gt;;
};
...</code></pre>

<p>
  The orginal component doesn’t change. <code>Extended</code> only returns
  it with any original properties.
</p>

<h2>How to see this working</h2>
<p>
  At this point, the code works. But, this type of change can be difficult to
  visualize. As mentioned earlier, the <a href="https://github.com/zalmoxisus/redux-devtools-extension">Redux DevTools Chrome extension</a>.
  works best for me.
  In the <a href="https://qk3n9xmm3w.codesandbox.io">demo</a> I included the
  devtools snippet when creating the store. If you install the extension and
  view the Redux panel, you can see new reducers change the state tree.
</p>
<figure>
  <img src="https://d3vv6lp55qjaqc.cloudfront.net/items/1n0J3V3G0j1X2a1M1C00/Screen%20Recording%202018-01-07%20at%2009.52%20PM.gif" alt="Animated gif showing a new reducer added in Redux devtools Chrome extension.">
  <figcaption>
    Demo of the records reducer being added when navigating to the /records route.
  </figcation>
</figure>

<p>
  To further show the results in the demo, I <code>connect</code>ed the record
  route to display record data from the store.
</p>
<pre><code class="language-javascript">...
const mapStateToProps = (state, props) => {
  const { match: { params: { id } } } = props;

  return {
    recordId: id,
    record: state.records[id] || {}
  };
};

export default connect(mapStateToProps)(Record);</code></pre>

<p>
  The full code is in <code>/routes/Records/routes/Record.js</code>.
</p>

<h2>A solution</h2>
<p>
  As I mentioned earlier, this is a common need in React/Redux projects for
  different reasons. I’ve used other, similar methods for dynamic
  routes in the past. Other folks have different approaches. With that, this is <em>a</em>
  solution, not necessiarly <em>the</em> solution.
</p>
<p>
  If this is helpful and you use it as-is or change it to fit your needs, let
  me know. There’s always room for improvement.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>Building My First PWA</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/building-my-first-pwa</link>
      <guid>https://tylergaw.com/articles/building-my-first-pwa</guid>
      <pubDate>Mon, 20 Nov 2017 01:10:00 EST</pubDate>
      <description>
        <![CDATA[
<p class="entry-intro">
  I’ve been looking for time and a sandbox to sit down and learn how to build offline-capable/first web sites or “Progressive Web Apps” (PWA) for a while. I learn best with a hands-on approach. <a href="https://colorme.io/" target="_blank" rel="noopener">ColorMe</a> is a site I maintain and a perfect candidate for offline experimentation. This post details the steps I took, the issues I ran into, and things I learned building my first PWA.
</p>

<p>
  Quick note. This isn’t a general “How to make a PWA” post. It may not even be a good intro. There are plenty of articles and tutorials to get the basics. This post is specific to the work involved in making ColorMe a PWA.
</p>

<p>
  In a lot of what I’ve read about PWAs, there’ve been common steps for building them. A simple-on-the-surface looking process. Here’s one list from Jeremy:
</p>

<blockquote>
  <ol>
    <li>switch over to HTTPS,</li>
    <li>add a JSON manifest file with your metacrap, and</li>
    <li>add a service worker.</li>
  </ol>
  <cite>
    <a href="https://adactio.com/journal/12461">Jeremy Keith, “Progressing the Web.”</a>
  </cite>
</blockquote>

<p>
  OK, that seems easy enough. Except that last step. That seems like it could be a lot to unpack. He does offer a disclaimer:
</p>

<blockquote>
  <p>
    That last step can be tricky if you’re new to service workers, but it’s not unsurmountable.
  </p>
</blockquote>
<p>
  I’ll ignore the service worker part of the process for now and focus on the first two items.
</p>
<h2>Using HTTPS</h2>
<p>
  ColorMe already has HTTPS in place. I host it on S3 and serve it through CloudFront. I used <a href="https://aws.amazon.com/certificate-manager/">Amazon’s Certificate Manager</a> to add an SSL certificate. HTTPS is in place and has been since launch.
</p>

<h2>Adding a Manifest</h2>
<p>
  This is where things got more interesting. I built ColorMe with <a href="https://github.com/facebookincubator/create-react-app">Create React App</a> (CRA) so I made a <code>manifest.json</code> file in the <code>public</code> directory. The same directory as the favicon and <code>index.html</code>. Anything in the <code>public</code> directory gets copied to the <code>build</code> directory as is. That’s what we need for <code>manifest.json</code>.
</p>

<h3>Doesn’t CRA do PWA stuff out of the box?</h3>
<p>
  Yes. CRA added built-in support for PWAs in version <code>1.0.0</code>. ColorMe is still on version <code>0.8.4</code>. That was the latest version when I created the project and haven’t had a reason to update.
</p>
<p>
  I could have updated CRA to use the built-in PWA, but I didn’t want to miss the opportunity to learn step-by-step. Doing this myself, taking the long, “dumb” way, helped internalize the why and the how of each step. On future CRA-built projects, I’ll use the latest version with built-in PWA support. With that out of the way, here’s all my “metacrap.”
</p>

<p>
  I knew there was “stuff” that went in the manifest, but I wasn’t sure about specifics. What keys can I use? What are example values for each key? And what key-values am I supposed to have? The best resource I found for questions number one and two is the MDN <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest">Web App Manifest documentation</a>. It lists available keys and example values.
</p>

<h3>Lighthouse</h3>
<p>
  For the third question, I turned to <a href="https://developers.google.com/web/tools/lighthouse/">Lighthouse audits</a> in Chrome dev tools. Before adding anything to the manifest, I ran a PWA audit. It reported items needed to meet the minimum requirements for a PWA (according to the audit).
</p>

<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-pwa-audit-before.png" alt="">
  <figcaption>
    A pre-pwa audit of colorme.io
  </figcation>
</figure>

<p>
  With the audit report as a starting point, I hit each item on the list. Most important, I referenced the manifest in the <code>head</code> of <code>index.html</code>. This uses the CRA-specific <code>%PUBLIC_URL%</code>.
</p>

<pre><code class="language-javascript">&lt;link rel="manifest" href="%PUBLIC_URL%/manifest.json"&gt;</code></pre>

<p>
  Most items in the manifest are straightforward enough so I won’t go line-by-line. But I will call out a couple items that took a bit more work. You can see the complete file <a href="https://github.com/tylergaw/colorme/blob/develop/public/manifest.json">on GitHub</a> and below:
</p>

<pre><code class="language-javascript">{
  "background_color": "#ffffff",
  "theme_color": "#B50003",
  "display": "standalone",
  "short_name": "ColorMe",
  "name": "ColorMe",
  "start_url": "/",
  "icons": [
    {
      "src": "launcher-icon-48x48.png",
      "type": "image/png",
      "sizes": "48x48"
    },
    {
      "src": "launcher-icon-96x96.png",
      "type": "image/png",
      "sizes": "96x96"
    },
    {
      "src": "launcher-icon-192x192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "launcher-icon-256x256.png",
      "type": "image/png",
      "sizes": "256x256"
    },
    {
      "src": "launcher-icon-384x384.png",
      "type": "image/png",
      "sizes": "384x384"
    },
    {
      "src": "launcher-icon-512x512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ]
}</code></pre>

<h3>Icon sizes</h3>
<p>
  As you can see in the manifiest, I included six different icon sizes. The audit requires two sizes; 192x192 and 512x512. The former for the homescreen icon on Android, the latter as an icon for a splash screen on Android.
</p>
<p>
  I’m not sure if the other four sizes are necessary, but I saw those in examples so I figured it wouldn’t hurt to include them.
</p>

<h3>Theme color</h3>
<p>
  A requirement–per the audit–was to add a <code>theme-color</code> meta tag to <code>index.html</code>:
</p>

<pre><code class="language-javascript">&lt;meta name="theme-color" content="#B50003"&gt;</code></pre>

<blockquote>
  <p>
    The theme-color meta tag ensures that the address bar is branded when a user visits your site as a normal webpage.
  </p>
</blockquote>

<h3>noscript</h3>
<p>
  Another failing audit was “Contains Some Content When JavaScript Is Not Available.” For that, I added <code>noscript</code> content. It doesn’t do anything except apologize for not working without JavaScript. It would be better to have some type of useful experience without JS, but I’ll save that for another time. It’s interesting to think how to make a site like this provide value without JavaScript.
</p>

<h3>“According to the audit”</h3>
<p>
  I’m using specific language like; “per the audit” and “according to the audit” on purpose here. Some of these aren’t universal. For example, the <code>theme_color</code> property and the <code>theme-color</code> meta tag have no effect on Mobile Safari or Mobile Chrome on iOS as far as I can tell. For the purposes of this exercise, I’m working towards 100% on the audit. I’m sure not every project needs every item. As usual, it depends.
</p>

<h3>The Chrome Dev Tools Manifest Tab</h3>
<p>
  Every time I’d make a change to the manifest I’d re-run the PWA audit to check the results. This was slow. I didn’t realize there was a tab in Chrome dev tools for inspecting the <code>manifest.json</code> results. I found it early enough in the process that it helped speed things along. It also has an “Add to homescreen” button to test that mechanism. That’s much appreciated because I don’t have access to an Android device for proper testing. I wrote this in case someone else also doesn’t know about the manifest tab in Google Chrome Dev Tools.
</p>

<p>
  At this point I still don’t have a PWA. The audit turns up one last failure:
</p>

<blockquote>
  <p>
    Failures: Site does not register a Service Worker, Manifest start_url is not cached by a Service Worker.
  </p>
</blockquote>

<p>
  With the basics of the manifest in place, I turned my attention to the service worker.
</p>

<h2>The Service Worker</h2>

<p>
  Like I mentioned above, this was the biggest mystery for me. I understood the general concept of service workers, but I didn’t understand what the goals of a service worker for a PWA were. Sure, it’s JS, it runs in the background, but what’s that JS supposed to <em>do</em>? After spending time with tutorials, examples, and fiddling, I got a clearer picture.
</p>
<h3>The goals of ColorMe’s service worker:</h3>
<ul>
  <li>
    Store the site’s static files–HTML, CSS, JavaScript, and images–in the <code>window.caches</code> object
  </li>
  <li>
    Intercept all network requests. If the name of the requested file is in <code>window.caches</code>, respond with the cached file instead of making a request to the server
  </li>
  <li>
    Delete stale caches when the cache key changes
  </li>
</ul>

<p>
  One thing that stuck out early was the <code>caches</code> member on the global <code>window</code> scope. When I first saw <code>caches</code> in use in example service workers, I thought it was a global only in the service worker context. That’s not the case. <code>window.caches</code> is available from any JS.
</p>

<p>
  Here’s a quick example of <code>caches</code>. Go to <a href="https://colorme.io">colorme.io</a>. Open the developer console and run this snippet:
</p>
<pre><code class="language-javascript">caches.keys().then(names => {console.log(names)});</code></pre>

<p>
  That should output <code>["colorme-v7"]</code> (the version number might be different). Not much to look at, but you can see that <code>window.caches</code> is a thing in this context. That means you can access caches from any client side JavaScript, not only service workers. That’s pretty cool.
</p>

<h3>Goal 1: Cache Static Files</h3>
<p>
  For ColorMe to work offline it needs to cache all critical static files. It’s a single page site so there are only a few; <code>index.html</code>, <code>main.css</code>, <code>main.js</code>, <code>manifest.json</code>, an svg image, and a Google Fonts stylesheet.
</p>
<p>
  The CRA build process creates or renames the CSS, JS, and image files. That made things difficult and I’ll describe my process for fixing it later. For now, I’ll pretend the file names are what they are and walk through the code.
</p>
<p>
  I created <code>service-worker.js</code> in the <code>public</code> directory. The full file is available <a href="https://github.com/tylergaw/colorme/blob/03946e9540a031075f3f691356d7aa3f4e457a2d/public/service-worker.js">on GitHub</a>.
</p>

<pre><code class="language-javascript">const STATIC_CACHE_NAME = "colorme-v1";
const STATIC_URLS = [
  "/",
  "/index.html",
  "/manifest.json",
  "/static/css/main.css",
  "/static/js/main.js",
  "/static/media/bgTransparent.svg",
  "https://fonts.googleapis.com/css?family=Cousine:400|Karla:400,700"
];
self.addEventListener("install", event => {
  event.waitUntil(
    caches.open(STATIC_CACHE_NAME).then(cache => {
      return cache.addAll(STATIC_URLS);
    }).then(() => self.skipWaiting())
  );
});</code></pre>

<p>
  <code>STATIC_CACHE_NAME</code> is a unique key for this cache. <code>STATIC_URLS</code> is the list of files to cache. I’ll explain how I update this list later to account for dynamic file names.
</p>

<p>
  In broad strokes, the next lines say:
</p>
<ol>
  <li>when the service worker finishes the install process,</li>
  <li>find or create a cache with our name,</li>
  <li>and put the files we specified in that cache.</li>
</ol>
<p>
  There are full descriptions of the install event and <code>waitUntil</code> available. <a href="https://developer.mozilla.org/en-US/docs/Web/API/InstallEvent">MDN</a> is a great one.
</p>

<h4>Skip Waiting?</h4>
<p>
  I’ve read docs about <code>skipWaiting</code>, but I’m still a bit hazy on what it does or if I even need it in this context. Enough examples I saw recommended it, so I went with it for now. I’ll learn more about it as I work with it.
</p>

<p>
  With those lines, ColorMe’s static assets are snug in a cache. There’s still work to be done though.
</p>

<h3>Goal 2: Serve Cached Files</h3>
<p>
  Putting static files in <code>caches</code> isn’t enough on its own. For ColorMe to work offline, we need to tell the browser to look in the cache for those files.
</p>

<pre><code class="language-javascript">self.addEventListener("fetch", event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => response || fetch(event.request))
  );
});</code></pre>

<p>
  This snippet listens for all HTTP requests from the user’s browser. <code>event.respondWith</code> prevents the browser’s default <code>fetch</code> handling. That allows us to check if the requested URL–<code>event.request</code>–is in the cache. If it is, respond with the cached file. If it’s not cached, continue with the request to the server using <code>fetch()</code>.
</p>

<p>
  ColorMe now works with or without an Internet connection.
</p>

<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-pwa-audit-100-percent.png" alt="">
  <figcaption>
    A post-pwa audit of colorme.io
  </figcation>
</figure>

<h3>Goal 3: Delete Stale Caches</h3>
<p>
  If I stopped here ColorMe works offline, but I’d have no way to release updates to users. Cached items have to be deleted, they never expire. The service worker should remove stale caches.
</p>
<pre><code class="language-javascript">self.addEventListener("activate", event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames
          .filter(name => name.includes("colorme") && name !== STATIC_CACHE_NAME)
          .map(name => caches.delete(name))
      )
    }).then(() => self.clients.claim())
  );
});
</code></pre>
<p>
  This is the most involved-looking code in the service worker, but it doesn’t do much.
</p>
<ol>
  <li>get an array of all cache keys with <code>caches.keys()</code>,</li>
  <li>remove any keys that don’t contain our name and remove a cache with the exact <code>STATIC_CACHE_NAME</code>,</li>
  <li>for each of the remaining keys, delete that cache.</li>
</ol>
<p>
  When the service worker activates–every time the page loads–check to see if there are stale caches. If there are, delete them. I determine if a cache is stale with #2 above.
</p>
<p>
  In the first code snippet I defined the cache key; <code>const STATIC_CACHE_NAME = "colorme-v1"</code>. When I make changes to any of the cached files and deploy the site, I also change that version number. If a user visited the site when <code>colorme-v1</code> was latest, then visits again when <code>colorme-v7</code> is the latest, the service worker deletes v1 and caches v7.
</p>

<h2>Caching Dynamic Filenames</h2>

<p>
  At this point, I’ve met all the goals for ColorMe’s service worker and have a functioning PWA. If you visit ColorMe then turn off your network connection, you should be able to refresh the page and use it as normal.
</p>
<p>
  There is one issue I mentioned above. The filenames for the CSS, JS, and image are incorrect. In the earlier snippet, I cached files named;
</p>

<ul>
  <li>
    <code>"/static/css/main.css"</code>
  </li>
  <li>
    <code>"/static/js/main.js"</code>
  </li>
  <li>
    <code>"/static/media/bgTransparent.svg"</code>
  </li>
</ul>

<p>
  The CRA build process fingerprints those filenames to something like;
</p>

<ul>
  <li>
    <code>"/static/css/main.2ebebc14.css"</code>
  </li>
  <li>
    <code>"/static/js/main.7e7a1a8f.js"</code>
  </li>
  <li>
    <code>"/static/media/bgTransparent.e6317315.svg"</code>
  </li>
</ul>

<p>
  This is common for cachebusting and not only for CRAs. I’ve used and built tons of different asset pipelines over the years that use fingerprinting.
</p>

<p>
  I got hung up on this big time. How am I going to tell the service worker to cache files when I have no control over their names? I didn’t find much info on the topic. <a href="https://github.com/w3c/ServiceWorker/issues/657">This issue</a> raises the question and the discussion helped me figure out an approach. I’d need to somehow generate the list of filenames during the build process. I had a few options, a couple of them not good ones;
</p>
<ol>
  <li>Run the CRA eject script and change the default webpack build process to generate the service worker</li>
  <li>Update to latest CRA and use their built-in PWA support</li>
  <li>Walk away from computer, sleep on it, think about a better solution the next morning while walking my <a href="http://leela.dog" target="_blank" rel="noopener">dog</a></li>
</ol>

<p>
  I didn’t want to eject from CRA, because it didn’t seem worth it. I mentioned above, updating to the latest CRA would take away the opportunity to learn. Option #3 sounded best. After getting away from the computer, I realized I had more options than I realized and a solution that <em>should</em> work. It was’t going to be elegant or scalable, but it’d do the job.
</p>

<h3>Piggybacking on the CRA build script</h3>

<p>
  The default CRA build script builds the project according to the baked in webpack config. I can’t change the config–without ejecting–but I can add to the build script in <code>package.json</code>. I’d already modified the script to include <code>NODE_PATH=src</code>. That makes it easier to import modules without referencing the full path. ColorMe’s starting build script looked like:
</p>

<pre><code class="language-javascript">NODE_PATH=src react-scripts build</code></pre>

<p>
  I knew a couple things. I knew the build process creates a file named <code>asset-manifest.json</code>. The contents of that file include the full fingerprinted names of all static assets used in the site. Example contents of the manifest file:
</p>

<pre><code class="language-javascript">{
  "main.css": "static/css/main.2ebebc14.css",
  "main.css.map": "static/css/main.2ebebc14.css.map",
  "main.js": "static/js/main.7e7a1a8f.js",
  "main.js.map": "static/js/main.7e7a1a8f.js.map",
  "static/media/bgTransparent.svg": "static/media/bgTransparent.e6317315.svg"
}</code></pre>

<p>
  Those are the full filenames I need to cache with my service worker. I need to get those filenames into the service worker file.
</p>
<p>
  The first thing I did was add to the build script. Back in <code>package.json</code> I updated the script to look like:
</p>

<pre><code class="language-javascript">NODE_PATH=src react-scripts build && npm run generate-sw</code></pre>

<p>
  This says; “run the normal build process, when you’re done with that run this other npm script”. That script looks like:
</p>

<pre><code class="language-javascript">"generate-sw": "node scripts/generate-sw.js"</code></pre>

<p>
  To make sure that worked, I created <code>/scripts/generate-sw.js</code> and added a single line; <code>console.log('hello')</code>. Then I ran the build script <code>npm run build</code> to make sure the project built and I saw “hello” in my terminal output. So far so good.
</p>

<h4>The generate script</h4>

<p>
  I need to get the filenames out of <code>asset-manifest.json</code> and into the array of filenames to cache in <code>service-worker.js</code>. My plan was to not get fancy with this. I only need to take strings from one file and write them into another file. The fact that the target file is JavaScript is immaterial to this process.
</p>

<p>
  The full file is available <a href="https://github.com/tylergaw/colorme/blob/03946e9540a031075f3f691356d7aa3f4e457a2d/scripts/generate-sw.js">on GitHub</a> and I’ll go through the code in detail here.
</p>

<pre><code class="language-javascript">const manifest = require("../build/asset-manifest.json");
const fs = require("fs");
const swPath = "build/service-worker.js";</code></pre>

<p>
  First is setup. <code>asset-manifest</code> is JSON so I <code>require</code> it here for use as an object. I’ll use the <code>fs</code> package for reading and writing files. I store the path of the service worker for convenience.
</p>

<pre><code class="code-muted">const manifest = require("../build/asset-manifest.json");
const fs = require("fs");
const swPath = "build/service-worker.js";</code>
<code class="language-javascript">const urlsCSV = Object.keys(manifest)
  .filter(k => !k.includes(".map"))
  .map(k => manifest[k]);</code></pre>

<p>
  Getting more interesting, but still not fancy. The goal of this chunk of code is to build an array of filenames. First, use <code>Object.keys</code> to get the keys from the manifest JSON to loop over an array.
</p>
<p>
  Next, use <code>filter</code> to remove keys that include the string “.map.” If you look at <code>asset-manifest.json</code> you’ll see source maps. We don’t want to cache those. I’m not sure if there’s a best practice for or against that, but I decided it didn’t seem right for this project.
</p>
<p>
  Now that we only have keys for the files we want to cache, use <code>map</code> to create the array of filenames stored as <code>urlsCSV</code>;
</p>

<pre><code class="language-javascript">[
  "static/css/main.2ebebc14.css",
  "static/js/main.7e7a1a8f.js",
  "static/media/bgTransparent.e6317315.svg"
]</code></pre>

<p>
  <strong>A short tangent.</strong> Given the code above, you might be asking; <em>“why didn’t you just use <code>Object.values</code> instead of <code>Object.keys</code> plus <code>map</code>?”</em> That’s a great question with a quick answer. As of this writing, I’m running Node.js version 6.9.1. <code>Object.values</code> is not supported without the <code>--harmony</code> flag until version <code>7.0.0</code>. I didn’t want to upgrade Node.js for this. I’ll do that another time. That’s all.
</p>

<p>
  I need to get that array of filenames into the service worker file. Again, this isn’t meant to be fancy or scalable. It’s meant to do the work.
</p>

<pre><code class="code-muted">const manifest = require('../build/asset-manifest.json');
const fs = require('fs');
const swPath = 'build/service-worker.js';
const urlsCSV = Object.keys(manifest)
  .filter(k => !k.includes('.map'))
  .map(k => manifest[k]);</code>

<code class="language-javascript">fs.readFile(swPath, "utf8", (err, data) => {
  if (err) { return console.log("Error trying to read SW file", err); }

  const result = data.replace("%MANIFESTURLS%", JSON.stringify(urlsCSV));

  fs.writeFile(swPath, result, "utf8", err => {
    if (err) { return console.log("Error trying to write SW file", err); }
  });
});</code></pre>

<p>
  Let’s break this down. First, open the service worker file (<code>swPath</code>) for reading. The error condition isn’t important. I included it to be nice to myself in case something odd happens during a build.
</p>

<p>
  The next line is the point of this script. It searches the contents of the service worker file (<code>data</code>) for the unique string <code>“%MANIFESTURLS%”</code>. When found, it’s replaced with a JSON stringified version of our filenames array, <code>urlsCSV</code>. Then, the updated contents are written back to the service worker file.
</p>

<h3>Updates to service-worker.js</h3>
<p>
  As mentioned above the generate script needs to find <code>“%MANIFESTURLS%”</code> in <code>service-worker.js</code>. I went back and updated the script to account for that.
</p>
<pre><code class="language-javascript">const STATIC_CACHE_NAME = "colorme-v1";
const BASE_STATIC_URLS = [
  "/",
  "/index.html",
  "/manifest.json",
  "https://fonts.googleapis.com/css?family=Cousine:400|Karla:400,700"
];
const STATIC_URLS = BASE_STATIC_URLS.concat(JSON.parse('%MANIFESTURLS%'));

// The install handler is the same as when we started.
self.addEventListener("install", event => {
  event.waitUntil(
    caches.open(STATIC_CACHE_NAME).then(cache => {
      return cache.addAll(STATIC_URLS);
    }).then(() => self.skipWaiting())
  );
});</code></pre>

<p>
  Here’s what I've done. I moved the filenames I know about to <code>BASE_STATIC_URLS</code>. I don’t fingerprint those file names, so they’re safe to hard-code. The important change is next. <code>STATIC_URLS</code> still ends up being an array of filenames, but now it’s a combination of two arrays. The filenames we know about and the generated array of filenames written to this file.
</p>
<p>
  <code>STATIC_URLS</code> ends up looking something like this;
</p>
<pre><code class="language-javascript">[
  "/",
  "/index.html",
  "/manifest.json",
  "https://fonts.googleapis.com/css?family=Cousine:400|Karla:400,700",
  "static/css/main.2ebebc14.css",
  "static/js/main.7e7a1a8f.js",
  "static/media/bgTransparent.e6317315.svg"
]</code></pre>
<p>
  When the project builds the fingerprinted filenames and <code>asset-manifest</code> changes. Then the service worker gets updated with the new filenames.
</p>
<p>
 That all adds up to a build process that handles caching of fingerprinted files and one offline-capable PWA.
</p>
<h2>A New Normal</h2>
<p>
  I’ve seen folks talking at length about how transformative PWAs are, but I couldn’t grasp it until I went through this process. This felt like building a responsive design for the first time. It’s the realization that this isn’t going to be a gimmick or extra or nice-to-have. It’ll take time for habit to kick in and browsers to catch up, but this will become my default when building sites.
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>Starting Something New</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/something-new</link>
      <guid>https://tylergaw.com/articles/something-new</guid>
      <pubDate>Tue, 26 Sep 2017 01:35:00 EST</pubDate>
      <description>
        <![CDATA[
<p>
  On December 15, 2016 I became unemployed. This was the first time in my career the unemployment was not my choice. The entirety of the company I worked for was RIF’d. If you’re unfamiliar, RIF stands for “reduction-in-force”. We were out of money to pay salaries and RIF was the best case scenario for us at the time.
</p>

<p>
  We were lucky. The founder and team all wanted to keep doing the <a href="https://thegroundwork.com/">work</a> we all felt so close to. There’s a story behind that work that I plan to share in the future. Over the course of two months, we were able to rustle enough contract work to pay salaries for the team members that stuck around. We were hired back in late February 2017.
</p>

<p>
  In that couple month span I was able to stop and breathe for the first time in two and half years. I got to work on <a href="https://tylergaw.com/articles/rewriting-day-player-for-sketch-40">fun</a> <a href="https://tylergaw.com/articles/sass-to-postcss">side</a> <a href="https://tylergaw.com/articles/introducing-colorme">projects</a>. And I was able to ruminate on things I wanted out of my work, career, and life.
</p>

<p>
  Inspired by the loss of a job and the uncertainty and clarity that brings, my thoughts about want I wanted led towards one thing. I wanted to work on a <strong>business that I owned</strong> that I could help build from the ground up. I wasn’t sure what that business should be or how I’d go about it. Then I got an unexpected opportunity from a friend.
</p>

<figure>
  <a href="https://www.limbo.io">
    <img src="https://tylergaw.com/articles/assets/post-image-limbo-1.png" alt="">
  </a>
  <figcaption>
    Limbo is an anonymous job platform
  </figcation>
</figure>

<p>
  On Wednesday, September 13<sup>th</sup>, 2017 <a href="http://www.umbrae.net/">Chris</a> and I launched our new business, <a href="https://www.limbo.io/">Limbo</a>.
<p>
  Chris visited New York from California during my two month “vacation” and invited me for a drink to catch up. We hadn’t seen each other since I’d last visted the Dary’s Berkeley home about a year before. At one point in our conversation about family and work and life he asked; “Can I pitch you an idea?” Of course. He then laid out the idea and business model for Limbo and asked, “are you interested in working on it with me?”
</p>
<p>
  Chris and I met in 2008 when he interviewed me for a position at <a href="https://web.archive.org/web/20080828193240/https://arc90.com">Arc90 <sup>(RIP)</sup></a>. I ended up getting the job and staying there for <a href="https://tylergaw.com/articles/2000-days">five years</a>. In that time, I got to work alongside him on Readability and Readlists and occasional other projects. I saw him conceive of and build <a href="https://kindlingapp.com/">Kindling</a>–which stayed in business for close to a decade. Over the years I saw him do excellent work, make smart decisions, and in general be a great person to be around. Any time I had the chance to work with Chris or even just have a conversation over coffee, I jumped at it.
</p>
<p>
  So when Chris comes to me and asks; “I have an idea, do you want to work on it with me?” I say “yes” because I know it’s going to be a great project.
</p>
<p>
  As expected, Chris didn’t dissapoint this time around. The next day he emailed me what he had so far. It was a lot. He already had the project set up. He had wireframes and rough mocks. He even sent a spreadsheet full of projections and different business model scenarios. Real adult stuff.
</p>
<p>
  So we got to work. Or more accurate, I got to work, he’d already been hard at it.
</p>

<p>
  I was still working full-time at The Groundwork and wanted to keep a hard separation between it and Limbo. To do that, I worked on Limbo mornings, nights, and weekends. My schedule would usually go something like; get up, work on Limbo for 30 minutes or so. Switch to The Groundwork for the day. Stop in the early evening. Take a break. Eat food. Then hop back to Limbo and work until I couldn’t keep my eyes open. Repeat.
</p>

<p>
  Weekdays were always more productive than weekends even with splitting my time. An occasional rainy Saturday or Sunday I’d get things done, but it’s  tough for me to do more computering on the weekend. Too many other things I’d rather do including doing nothing.
</p>

<p>
  And so it went like that for months. My schedule made it take longer to get to launch, but we were making progress every day and now it’s a real, live thing.
</p>

<h2>Unknown unknowns</h2>
<p>
  The feedback we’ve received since launch has been overwhelmingly positive. So there’s <strong>something</strong> there but, this is a business so we can’t say for certain how things will shake out. The goal is for Limbo to be our full time–paying–jobs. We’re doing everything we can to get there.
</p>
<p>
  If you’ve worked with me you know I take ownership of what I’m working on. Not only design and code aspects of what I’m doing. I like to get in there. Co-workers have described me as “vocal.” When I see things in projects or teams or organizations that I think could be better, I offer my thoughts, ideas, and help. This is the opportunity we’re making for ourselves. <strong>We get to ask all the questions.</strong> We get to make decisions about every aspect of the business in a way that reflects our values. This is a chance to make something positive and put it into the world.
</p>
<p>
  One example of the decisions we’re making is around money. In our first conversation we discussed seeking funding versus bootstrapping. We’ve been around long enough and seen enough to know that seeking early outside investment often does not end well. We decided bootstrapping was the way to go. It’s not that we think VC is wrong, it just doesn’t feel right for us on this venture, at this time. We want to see if the business model is sustainable from the day one. Not work on it for years, then get to the common start-up point of “I guess it’s time to figure out how to make money?” We either offer enough value to make enough money or we don’t.
</p>
<p>
  A huge thank you to everyone who’s helped us with Limbo so far. We’re fortunate to have friends who will take time out of their day to try things out, give us feedback, point us in new directions, and offer encouragement.
</p>

<p>
  This feels like the cusp of something. This has the potential to be where we put our energy for years to come. Chris and I have helped design and build world class products for other smart people. Now we’ll see if we can build, nurture, and sustain a business for ourselves.
</p>
<p>
  Thanks for reading.
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>The Impact of Flash</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/the-impact-of-flash</link>
      <guid>https://tylergaw.com/articles/the-impact-of-flash</guid>
      <pubDate>Sat, 29 Jul 2017 06:30:00 EST</pubDate>
      <description>
        <![CDATA[
<p class="entry-intro">
  A short Twitter exchange with <a href="https://twitter.com/mciarlo/status/890931236054720513">Mike</a> made me pause and think about the positive impact <a href="https://blogs.adobe.com/conversations/2017/07/adobe-flash-update.html">Flash</a> has had on me. I felt the need to scribble a few loose thoughts and anecdotes about a technology near its end.
</p>

<p>
  I’m thinking about the time between late 2004 to sometime in 2008. That's the start of my time working with the Web and computers in any real capacity. Flash was still Macromedia Flash. It was pervasive and incredible.
</p>

<p>
  Building websites with HTML and CSS in 2004 to 2007-ish was interesting, but limited. We made 760 pixel fixed-width pages with tiled background images and bad image-based type. And that’s only if you’d made the switch from table-based layouts. Many folks had not. CSS transitions and animations weren’t even on the radar. jQuery wasn't a thing. JavaScript was still considered a toy language for “DHTML” gimmicks by most.
</p>
<p>
  Flash filled a need and did it well. It provided a path to design and publish web sites with as far out graphics and layouts as we could conjure. Being able to work outside the capabilities of browsers was helpful for someone like me. I had a traditional art background and still felt stifled by computer-based design tools and the limits of browsers of the day. Flash was a looser way to learn and develop digital design skills. At the time, making Flash sites was flat out more fun and exciting.
</p>

<h2>Movement and sound</h2>
<p>
  Flash helped many of us learn timing, pacing, and rhythm. It helped us learn how to design to music. Sometimes real music, sometimes the songs in our heads. These concepts aren’t limited to animation. Through the years, I’ve folded motion design concepts into tons of static design. It’s one way to arrive at richer, more interesting designs. I realize this sounds esoteric, it is. It’s squishy, designer-feel stuff, but it often results in design that stirs emotion.
</p>
<p>
  Working in Flash opened doors to designing with other mediums like audio. How many of you also used <a href="http://www.audacityteam.org/">Audacity</a> to create sound effects for use in <code>.swf</code>s? Homemade foley was a constant in my early Flash projects. It was fun and silly and great.
</p>
<p>
  Sites like <a href="http://www.2advanced.com/">2advanced.com</a> displayed a breadth and depth to designing with motion and sound on the Web. At the time, you could not find that with sites built with web standards alone. Seeing what was possible opened eyes and removed barriers. If 2Advanced and others could do work like that, so could we. And later, when technology caught up, we learned we could do it without Flash.
</p>
<p>
  These concepts and approaches we learned weren’t lost when we stopped using Flash. We rolled them along to CSS and JavaScript and iOS and Android. The rich motion we now expect from digital products is no doubt inspired and learned by earlier work done with Flash.
</p>

<h2>Typography</h2>
<p>
  We would not have the typographic capabilities on the Web we have today if not for Flash. <a href="https://en.wikipedia.org/wiki/Scalable_Inman_Flash_Replacement">sIFR</a> let us explore what was possible with custom type in web pages. There were no other options at the time. <code>@font-face</code> would not exist for a couple years. It would be more years still until it had wide enough browser support you could use it.
</p>
<p>
  In this instance, Flash was a polyfill. Using Flash to embed custom fonts helped us learn what we wanted from typography on the Web. That knowledge informed specifications and browser vendors were then able to make the specs reality. This feels, to me, like the most day-to-day, “bread and butter” impact Flash had on the Web.
</p>

<h2>Learning to code</h2>

<p>
  Everything in this post is from my perspective, but this section is 100% about my experience. Everyone who writes code learned in a different way. I learned to program using ActionScript 2.0 within Flash so I hold it in high regard. ActionScript was my introduction to basic programming concepts; variables, conditionals, loops, and functions. I took to it well I think because it was the first time I’d seen code tied to a fun end result. Write code, see something fun happen in a <code>.swf</code>. It was immediate and tangible so it stuck.
</p>
<p>
  Writing ActionScript also taught me how to think in code and convince a computer to do what I wanted. Often times the convincing wasn’t done in an elegant way. I remember my final for an Advanced ActionScript course in college. I got half credit on one programming task because I made it work, but did so in some roundabout way. That’s representative of how I write code today. I may not always have the most elegant solution, but I'll get it done no matter what.
</p>

<h2>It’s really the humans</h2>

<p>
  In Mike’s tweet he used “nostalgic.” I wasn’t sure at the time if I felt nostalgia about Flash or not.
</p>

<blockquote>
  <p>
    a sentimental longing or wistful affection for the past, typically for a period or place with happy personal associations.
  </p>
</blockquote>

<p>
  After thinking and writing about this, yes I do. I’m not nostalgic about the technology itself though. Flash, like all other technology, is a means to an end. Thinking about Flash reminds me of that time in my life. It reminds me of the work I was doing, the rush of new ideas, and the thrill of making new things. It reminds me of the people I was working with, most of whom I haven't spoken to in the years since. So yeah, I think I’ll always look back on that time with “wistful affection”.
</p>

<p>
  We’re doing what we always do, moving on. We learn, we evolve, we evolve our tools. This is a good thing and a constant in our field. At the same time it’s important to witness and remember the steps in our evolution.
<p>
  Even though it has a couple more years of life, Flash’s time has come and gone. Flash's positives far outweigh its negatives and its impact will persist for years after it’s gone.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>New ColorMe Features</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <link>https://tylergaw.com/articles/new-colorme-features</link>
      <guid>https://tylergaw.com/articles/new-colorme-features</guid>
      <pubDate>Wed, 18 Jan 2017 06:30:00 EST</pubDate>
      <description>
        <![CDATA[
<p class="entry-intro">
  Since releasing it two weeks ago, <a href="https://colorme.io">ColorMe</a> has had time to simmer and I’ve gotten a bunch of great feedback. In that time I’ve made improvements and feature additions. New features include; support for <code>hsl(a)</code> and <code>rrggbbaa</code> colors. Red, green, and blue color adjusters. And the option to choose the format of the output color.
</p>
<p>
  The first updates I made I snuck in the day after release. Those were improvements to the color adjuster sliders for touch devices. Or more accurate, devices without <code>hover</code> support. I knew the initial design had usability issues on mobile, but decided to ship it and iterate.
</p>
<p>
  The main problem was the horizontal scrolling interfering with the range sliders. The thumb of the sliders was too hard to get a hold of with a finger. And it was tough to adjust the slider instead of scrolling horizontal.
  For this first update I didn't change the UI completely for touch devices. For now I only increased the size of the thumb on the range sliders in <a href="https://github.com/tylergaw/colorme/pull/4">this pull request</a>.
</p>
<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-colorme-updates-touch-support.png" alt="A screenshot of touch device use of color adjusters on ColorMe" />
  <figcaption>
    The new size of an inactive and an active thumb on touch devices. It looks odd here, but easier to see and handle when you’re using a finger instead of a mouse pointer.
  </figcation>
</figure>
<p>
  By default the CSS targets all devices. The range slider thumb is large. Almost comical in size and off center vertically when active. It looks strange in a screenshot, but the larger size and y-offset make it easier to control when using a finger.
</p>
<pre><code class="language-css">.adjusterValRange::-webkit-slider-thumb {
  ...
  height: 1.6rem;
  margin-top: -0.8rem;
  width: 1.6rem;
}

.adjusterValRange::-webkit-slider-thumb:hover,
.adjusterValRange::-webkit-slider-thumb:active {
  margin-top: -1.8rem;
  height: 2.5rem;
  width: 2.5rem;
}</code></pre>

<p>
  To decrease the thumb size on devices with <code>hover</code> support , I used the <code>hover</code> media query available in <a href="https://www.w3.org/TR/mediaqueries-4/#hover">Media Queries Level 4</a>.
</p>
<pre><code class="language-css">@media (hover) {
  .adjusterValRange::-webkit-slider-thumb {
    ...
    height: 0.75rem;
    margin-top: -0.3rem;
    width: 0.75rem;
  }

  .adjusterValRange::-webkit-slider-thumb:hover,
  .adjusterValRange::-webkit-slider-thumb:active {
    margin-top: -0.5rem;
    height: 1.1rem;
    width: 1.1rem;
  }
}</code></pre>
<p>
  I’m a huge fan of using the <code>hover</code> media query instead of relying on screen size. The issue isn’t caused because the screen is too narrow. It’s caused by not having the fine control a mouse and cursor provide. The new media query isn’t available in all browsers yet, but support is good enough for these purposes.
</p>
<p>
  You can see the full CSS for the adjuster range sliders in <code><a href="https://github.com/tylergaw/colorme/blob/2017-01-17T22.58.04/src/components/Adjuster/Adjuster.css#L102">Adjuster.css</a></code>.
</p>
<p>
  Usability of the adjusters on touch devices still has room for improvement. There’s an <a href="https://github.com/tylergaw/colorme/issues/5">open issue</a> about it in the repo. Along with the ideas there, I have an alternate UI I’d like to try out. That will likely be the next round of changes I make to the project.
</p>

<h2>Red, green, and blue adjusters</h2>
<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-colorme-updates-rgb-adjusters.gif" alt="An animated gif showing red, green, and blue color adjusters ColorMe" />
  <figcaption>
    Adjust the red, green, and blue channels.
  </figcation>
</figure>
<p>
  In the first release I also left out three of the available <a href="https://drafts.csswg.org/css-color/#modifying-colors">color adjusters</a>. Red, green, and blue. Those are in place now. They do what they say on package. Each one adjusts that single channel. Could be helpful for finding that perfect reddish-greenish-blue. Or for finding a good complementary color.
</p>

<h2>New supported formats</h2>
<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-colorme-updates-formats.gif" alt="An animated gif showing new format inputs in ColorMe" />
  <figcaption>
    <code>hsl(a)</code> and <code>rrggbbaa</code> formats now supported.
  </figcation>
</figure>
<p>
  One of the first bits of feedback I got was from my coworker, <a href="https://twitter.com/thefivetoes">Justin</a>. He said ColorMe should support <code>rrggbbaa</code> colors for the base color. I’d never used them before so I took some time to educate myself. If they’re new to you too, read <a href="https://hashnode.com/post/understanding-rrggbbaa-color-notation-cisvdr52x088fwt53h1drf6m2">this post</a>. Then play with them in <a href="https://colorme.io?color=ff0000af">ColorMe</a> to get a better feel how they work. I’ve been using the alpha adjuster to go through each value to see what the percentage is in hex. It’s still taking me some time to think about alpha values as hex, but I'm a fan. You should give it a try if you haven’t already.
</p>
<p>
  I also added support for <code>hsl(a)</code> base colors. This was a no-brainer. It just didn't make the cut for first release.
</p>

<h2>Choosing output color format</h2>
<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-colorme-updates-format-select.gif" alt="An animated gif showing new format selection in ColorMe" />
  <figcaption>
    You can now choose the output color format.
  </figcation>
</figure>
<p>
  OK, this is a fun one! Also the one that required the most effort. A request I got was the ability to see the output color in <code>hex</code> or <code>hsl(a)</code> format instead of only <code>rgb(a)</code>. This was also something I’d been wanting. So I took the idea and ran with it.
</p>
<p>
  You now have the option to choose the format for the output color. There are up to seven different formats depending on the color. The full list of formats is; hex, hex shorthand, rrggbbaa, rrggbbaa shorthand, rgb(a), hsl(a), and keyword. Depending on the base color some of them are available and some aren’t.
</p>
<p>
  When you choose a format, ColorMe will do its best to hold on to your selection. Sometimes though there's no way to display a color in a certain format. For example; say you enter the color <code>red</code>. The output format is "keyword". Now, say you adjust the <code>alpha</code> value to 90%. There is no hex or keyword format for the new color. In that case ColorMe switches to the <code>rgba</code> format. It also disables the hex, hex shorthand, rrggbbaa shorthand and keyword formats because those aren't available. In the UI, this just happen for you. React makes short work out of maintaining view layer updates. Behind the scenes there’s a lot of logic to determining what formats work for each color.
</p>
<p>
  If you’re interested in that logic, have a look at the <code>getColorFormats</code> function in <code><a href="https://github.com/tylergaw/colorme/blob/2017-01-17T22.58.04/src/utils/color.js#L232">utils/color.js</a></code>. The code there isn’t clever. Most of it is <code>if</code> statements representing what I knew should be true about each format. Because this type of logic can be brittle when making changes, I also wrote a battery of <a href="https://github.com/tylergaw/colorme/blob/2017-01-17T22.58.04/src/utils/__tests__/color.test.js#L385">tests</a> to let me know when I break things.
</p>

<h2>A bug fix and open-source sleuthing</h2>
<p>
  As I was tinkering with the UI, I hit one of those voodoo, ghost-looking bugs. After fiddling with the knobs a while longer, I saw the pattern and was able to reproduce the <a href="https://github.com/tylergaw/colorme/issues/3">issue</a>.
</p>
<p>
  When using the <code>alpha</code> adjuster with the <code>tint</code>, <code>shade</code>, or <code>contrast</code> adjusters, the alpha value was wrong. For example, this code;
</p>
<pre><code class="language-css">color(red a(10%) tint(50%))</code></pre>
<p>
  produced:
</p>
<pre><code class="language-css">rgba(255, 128, 128, 0.19999999999999996)</code></pre>
<p>
  when I expected it to produce:
</p>
<pre><code class="language-css">rgba(255, 128, 128, 0.1)</code></pre>
<p>
  First I made sure my code wasn't up to anything fishy. Then started looking at my dependencies. The unexpected results were coming from the the <a href="https://github.com/ianstormtaylor/css-color-function">css-color-function</a> package. To complicate things more, the problematic code didn't end in css-color-function. It was its use of another <a href="https://github.com/Qix-/color">color package</a>.
</p>
<p>
  css-color-function uses the <code>mix</code> function of that color package for <code>tint</code>, <code>shade</code>, and <code>contrast</code> adjusters. The <code>Color.mix</code> function was ported from Sass. In Sass, the <code>mix</code> function not only mixes color channels, it also mixes the alpha channel.
</p>
<blockquote>
  <p>
    The opacity of the colors is also considered when weighting the components.
  </p>
  <cite>
    <a href="http://sass-lang.com/documentation/Sass/Script/Functions.html#mix-instance_method">
      From the Sass docs on the <code>mix</code> function
    </a>
  </cite>
</blockquote>
<p>
  As far as I can tell, this isn't the intended behavior of the color function adjusters. For ColorMe, I forked css-color-function and modified its <code>blend</code> method. The problematic adjusters use that method. What I did was take the alpha value out of play before mixing colors. Then put it back when finished. Here's my updated version of the <code>blend</code> method:
</p>
<pre><code class="language-javascript">exports.blend = function (color, args) {
  var targetAlpha = color.alpha();

  // Reset the alpha value to one. This is required because color.mix mixes
  // the alpha value as well as rgb values. For blend() purposes, that's not
  // what we want.
  color.alpha(1);

  var other = new Color(args[0].value);
  var percentage = 1 - parseInt(args[1].value, 10) / 100;

  // Finally set the alpha value of the mixed color to the target value.
  color.mix(other, percentage).alpha(targetAlpha);
};</code></pre>
<p>
  ColorMe is now using <a href="https://github.com/tylergaw/css-color-function/tree/tg-ignore-alpha-on-mix">my fork</a> of css-color-function. I opened a <a href="https://github.com/ianstormtaylor/css-color-function/pull/26">pull request</a> to get the changes the original fork. I also added more detail about the cause and solution in the description of that PR.
</p>
<h3>Pull requests beget pull requests</h3>
<p>
  When I started pulling at this thread I found two other projects affected by it. I'm sure there are others. I opened a <a href="https://github.com/postcss/postcss-color-function/pull/33">pull request</a> in postcss-color-function and <a href="https://github.com/MoOx/postcss-cssnext/issues/337">reported the problem</a> in postcss-cssnext.
</p>

<h2>More to come</h2>
<p>
  I had a lot fun with these updates, hope folks find them useful. As said I have more ideas for future ColorMe updates. And as always if you see issues or have ideas, <a href="https://github.com/tylergaw/colorme/issues">open an issue</a>.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
    </item>
    <item>
      <title>Introducing: ColorMe</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <description>
        <![CDATA[
<p class="entry-intro">
  <a href="https://colorme.io">ColorMe</a> is a site I built to make working with the <a href="https://drafts.csswg.org/css-color/#modifying-colors">CSS Color Function</a> easier. It provides a UI for applying adjusters to a base color and gives a visual of the adjustments. The excellent <a href="http://jim-nielsen.com/sassme/" target="_blank">SassMe</a> was my inspiration.
</p>

<p>
  The Color Function is a CSS feature in editor's draft stage of specification. This means it will be a while before it lands in any browser. That doesn't mean you can't use the proposed syntax today though. You can use the <a href="https://github.com/postcss/postcss-color-function">color function</a> PostCSS plugin. That plugin is also included in <a href="http://cssnext.io/features/#color-function">cssnext</a> if you want access to more future-facing CSS. The plugin uses the <a href="https://github.com/ianstormtaylor/css-color-function">css-color-function</a> package for its parsing and converting.
</p>
<p>
  I <a href="https://tylergaw.com/articles/sass-to-postcss">wrote</a> about switching this site to PostCSS. That was my first encounter with the color function. After years of using color transform functions in Sass it's easy to see how powerful a native CSS color function will be.
</p>
<p>
  There are more detailed <a href="https://topaxi.codes/modifying-css-colors-with-the-color-function/"> articles</a> about the color function. I'll give a brief overview of it here. On its own, <code>color</code> doesn't do anything with the given base color:
</p>
<pre><code class="language-css">color(#ea3333) /* output is the same as input */</code></pre>
<p>
  The power of the function is in adjusters. These are transformations applied to the base color. The current draft spec lists 15 available adjusters; <code>alpha</code>, <code>rgb</code>, <code>red</code>, <code>green</code>, <code>blue</code>, <code>hue</code>, <code>saturation</code>, <code>lightness</code>, <code>whiteness</code>, <code>blackness</code>, <code>tint</code>, <code>shade</code>, <code>blend</code>, <code>blenda</code>, and <code>contrast</code>.
</p>
<p>
  The <code>color</code> function allows you to apply one or more adjusters to the given base color:
</p>
<pre><code class="language-css">color(#ea3333 alpha(90%) saturation(75%) shade(20%)) /* rgba(182, 47, 47, 0.9) */</code></pre>
<p>
  Many of the adjusters also have short names:
</p>
<pre><code class="language-css">color(#ea3333 a(90%) s(75%) shade(20%)) /* rgba(182, 47, 47, 0.9) */</code></pre>
<p>
  Like Sass, using the CSS Color Function in code can be difficult visualize. Colors are easier to work with when you can see them. That's why SassMe is so darn useful. And that's why I thought we needed the same for CSS.
</p>
<figure>
  <a href="https://colorme.io">
    <img src="https://tylergaw.com/articles/assets/post-image-colorme-screenshot-1.png" alt="A screenshot of colorme.io" />
  </a>
  <figcaption>
   This is <a href="https://colorme.io">ColorMe.io</a>
  </figcation>
</figure>

<h2>Building It</h2>
<p>
  ColorMe is a React app. I used the wonderful <a href="https://github.com/facebookincubator/create-react-app">create-react-app</a> to get started.
</p>
<p>
  Before getting any UI in place I needed to see a first thing work. I needed to convert color function strings to rgb(a) strings. For that I used the <code>css-color-function</code> package. I started by importing the package and logging results of its use:
</p>
<pre><code class="language-js">import colorFn from 'css-color-function';
console.log(colorFn.convert('color(red alpha(50%))')); // rgba(255, 0, 0, 0.5)</code></pre>
<p>
  No major breakthrough, but that's how I get myself going. Just get one win no matter how small. From there it was a matter of building up functionality piece by piece.
</p>
<p>
  The workhorse of the project is the <a href="https://github.com/tylergaw/colorme/blob/2017-01-05T17.19.10/src/utils/color.js">color utils</a>. All color calculations happen there. When you enter a base color, the app <a href="https://github.com/tylergaw/colorme/blob/2017-01-05T17.19.10/src/utils/color.js#L10">breaks it apart</a> into individual color properties. Those properties are initial values for the adjusters. I used <a href="https://github.com/Qix-/color">this color package</a> to parse the base color string.
</p>
<p>
  Because the color utils do so much I took the time to write <a href="https://github.com/tylergaw/colorme/blob/develop/src/utils/__tests__/color.test.js">decent test coverage</a>. Writing tests isn't something I jump at doing (<i>read as: I get distracted, just wanna draw pictures, or go outside, or anything else. Please don't make me write tests!</i>). But it felt necessary for this and did save me a few headaches during refactoring.
</p>

<h2>Deploying It</h2>
<p>
  When I'm ready to release updates to ColorMe I run <code>npm run release</code>. That command creates a new Git tag and pushes it to GitHub. Travis then runs the tests. When Travis sees the build is for a tag it executes <a href="https://github.com/tylergaw/colorme/blob/2017-01-03T23.50.13/scripts/deploy.sh">deploy.sh</a>. That script does a production-ready build with <code>npm run build</code> and then syncs the artifacts to AWS S3. This is a similar build process that I wrote about in more detail in <a href="https://tylergaw.com/articles/rewriting-day-player-for-sketch-40">“Rewriting Day Player for Sketch 40+”</a>.
</p>

<h2>Try it out</h2>
<p>
  It's still early days for the CSS Color Function. It's a promising feature with momentum behind it. My hope is that more folks will start using it and giving feedback. We'll see the spec continue to evolve and sooner or later get implemented by browsers.
</p>
<p>
  Take <a href="https://colorme.io">ColorMe</a> for a spin. If you find bugs or have ideas, feel free to add <a href="https://github.com/tylergaw/colorme/issues">issues</a>.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
      <link>https://tylergaw.com/articles/introducing-colorme</link>
      <guid>https://tylergaw.com/articles/introducing-colorme</guid>
      <pubDate>Thu, 05 Jan 2017 05:15:00 EST</pubDate>
    </item>
    <item>
      <title>From Sass to PostCSS</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <description>
        <![CDATA[
<p class="entry-intro">
  Sass has been my go-to for years. But for a while now, I've wanted to try a new styling setup with PostCSS and the cssnext plugin. I love the idea of writing future CSS syntax today and using tooling more aligned with other tools I'm used to. This personal site is a perfect test bed to try this new setup.
</p>
<p>
  The first step I took was an inventory of my Sass usage. I needed to know what features I was using to make sure I found a replacement in the new setup. Here's a list of what I was using on this project:
</p>
<ul>
  <li>partial imports</li>
  <li>variables</li>
  <li>nesting</li>
  <li>mixins</li>
  <li>extend</li>
  <li>placeholder classes</li>
  <li><code>darken</code> and <code>rgba</code> color functions</li>
  <li>compression</li>
</ul>

<h2>Preparing</h2>
<p>
  Before getting to new syntax or other fun I needed to do some yak shaving. The project was using a file structure typical for Sass usage. I used the leading underscore naming convention for partials. And included the required <code>scss</code> extension. I used two directories to loosely organize Sass files. <code>modules</code> housed Sass that didn't produce CSS. Things like variables, placeholder classes, and mixins. <code>partials</code> housed all CSS-producing Sass.
</p>
<p>
  This was the beginning file structure:
</p>
<pre><code class="language-none">css/
  scss/
    modules/
      _module.scss
      ...
    partials/
      _partial.scss
      ...
    tylergaw.scss
</code></pre>
<p>
  Each Sass partial gets imported in <code>tylergaw.scss</code>.
</p>

<pre><code class="language-scss">@import "modules/setup";
@import "modules/reset";
@import "modules/fonts";
</code></pre>

<p>
  I reorganized and renamed the files. I first changed the extension from <code>scss</code> to <code>css</code>. Instead of doing it a file at a time, I used a Bash script:
</p>
<pre><code class="language-bash">for f in *.scss; do git mv -- "$f" "${f%.scss}.css"; done;
</code></pre>

<p>
  Because the leading underscore is from the Sass world I also removed that. I couldn't figure a way to do it with Bash so I removed it from each file by hand. <i>(note to self; learn how to Bash better)</i>
</p>
<p>
  The last step was to move all the CSS files to the <code>modules</code> directory and remove the <code>partials</code> directory. I decided referring to all CSS as modules made more sense than trying to split them along the modules/partials line.
</p>

<h2>Build setup</h2>
<p>
  I started with the <a href="https://github.com/postcss/postcss-cli">PostCSS CLI</a>. I added a temporary build script to <code>package.json</code>:
</p>
<pre><code class="language-json">"scripts": {
  "postcss": "postcss -o public/css/tylergaw.css src/css/tylergaw.css"
}</code></pre>
<p>
  Without changing any styles I compiled the CSS:
</p>
<pre><code class="language-json">npm run postcss</code></pre>
<p>
  It worked! Sorta. I didn't get errors in the console, but it left me with a naked page on the <a href="http://css-naked-day.github.io/">wrong day</a>.
</p>
<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-postcss-1.png" alt="A screenshot of tylergaw.com missing all styles" />
  <figcaption>
    Results of the first PostCSS build
  </figcation>
</figure>
<p>
  With the build process functional I could now work to get the styles back in order.
</p>

<p>
   Looking at the console in Chrome I saw a stack of 404s. This revealed the first missing feature; <code>@import</code> inlining. <code>tylergaw.css</code> contains only <code>@import</code>s for each CSS module. The browser saw those and did what it knows to do. It attempted to load each module via an HTTP request. My build process only copies the single CSS file, not each module. Because of that, the browser couldn't find them.
</p>

<p>
  I could change the build process to make default <code>@import</code>s work, but that would be inefficient. I needed a replacement for Sass style <code>@import</code> inlining.
</p>

<h2>The first plugin</h2>
<p>
  To get Sass-style <code>@import</code>s I used the <code>postcss-import</code> plugin. After installing the module via npm I updated the build script to use it:
</p>

<pre><code class="language-json">"scripts": {
  "postcss": "postcss -u postcss-import -o public/css/tylergaw.css src/css/tylergaw.css"
}</code></pre>
<p>
  And ran the script again with <code>npm run postcss</code>. The single CSS file contains all the modules and the site now has partial styling.
</p>

<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-postcss-2.png" alt="A screenshot of tylergaw.com with partial styles" />
  <figcaption>
    Results of the PostCSS build using postcss-import plugin
  </figcation>
</figure>

<h3>Will this be in a future CSS?</h3>
<p>
  The inlining of <code>@import</code>s was huge when it showed up in Sass. It's changed how we're able to organize styles for the better. I'm not sure this functionality will ever be native in though. It seems like we'll always need a build step for this type of functionality. Which doesn't seem all that bad.
</p>
<p>
  I imagine the <code>postcss-import</code> plugin will be a staple for all my future PostCSS setups. My guess is this will be true for other folks too. This quote from the plugin author sounds right on:
</p>
<blockquote>
  <p>
    This plugin should probably be used as the first plugin of your list. This way, other plugins will work on the AST as if there were only a single file to process, and will probably work as you can expect.
  </p>
  <cite>
    <a href="https://github.com/postcss/postcss-import#postcss-import">
      postcss-import
    </a>
  </cite>
</blockquote>

<h2>cssnext</h2>
<p>
  <a href="http://cssnext.io/">cssnext</a> is a PostCSS plugin for compiling future CSS syntax to syntax that works today. It's important to note that it's not a different language like Sass or Less. The features it offers are in-progress CSS specs. Some for features already showing up in browsers today. Others in beginning stages of the specification process.
</p>
<p>
  I used cssnext to fill in the rest of the gaps left by missing Sass features.
</p>

<h3>Vendor prefixes</h3>
<p>
  I built this site before I knew about <a href="https://github.com/postcss/autoprefixer">Autoprefixer</a>. I used <a href="https://github.com/tylergaw/tylergaw.com/blob/pre-postcss/src/css/scss/modules/_prefixed.scss">custom Sass mixins</a> to handle adding the needed prefixes. cssnext includes Autoprefixer, so I was able to remove that entire mixins module.
</p>

<h3>Variables</h3>
<p>
  Next I changed the Sass variables to CSS custom properties. In <a href="https://github.com/tylergaw/tylergaw.com/blob/pre-postcss/src/css/scss/modules/_setup.scss">_setup.scss</a> I had:
</p>
<pre><code class="language-scss">$grey: #1e1e1d;
$yellow: #ffad15;
$offwhite: #f8f8f8;
$darkerwhite: darken($offwhite, 15);</code></pre>
<p>
  This isn't all the Sass vars I was using, but it's the main ones. The rest are in individual modules.
</p>
<p class="note-special">
  <b>Note:</b> The “custom properties” vs. “variables” distinction. CSS custom properties are only valid for property values. They can't be used in selectors, property names, or media query values.
</p>
<p>
  The updated <code>setup.css</code>:
</p>
<pre><code class="language-css">:root {
  --white: #fff;
  --grey: #1e1e1d;
  --accent: #ffad15;
  --offwhite: #f8f8f8;
  ...
}</code></pre>
<p>
  and an example of updated usage:
</p>
<pre><code class="language-css">a {
  color: var(--accent);
}</code></pre>

<p>
  Apart from syntax, CSS custom properties work the same as Sass variables. Because of limited browser support, properties are still compiled out. In the above example, the compiled value is <code>color: #ffad15</code>.
</p>

<h3>Color functions</h3>
<p>
  In the previous example, I left out one variable; <code>$darkerwhite: darken($offwhite, 15);</code>. This is another Sass feature I needed a replacement for. There's a <a href="https://drafts.csswg.org/css-color/#modifying-colors">draft spec</a> for a CSS <code>color</code> function. cssnext includes this function today and it's super cool. Here's <code>setup.css</code> with a <code>darkerwhite</code> custom property created with the <code>color</code> function and <code>shade</code> adjuster:
</p>
<pre><code class="language-css">:root {
  ...
  --offwhite: #f8f8f8;
  --darkerwhite: color(var(--offwhite) shade(20%));
  ...
}</code></pre>
<p>
  The <code>color</code> function provides a bunch of <a href="https://github.com/postcss/postcss-color-function#list-of-color-adjuster">adjusters</a>. You can use multiple adjusters in a single usage:
</p>
<pre><code class="language-css">background-color: color(#d32c3f shade(40%) alpha(40%));</code></pre>
<p>
  compiles to:
</p>
<pre><code class="language-css">background-color: rgba(127, 26, 38, 0.4);</code></pre>

<p>
  To reiterate. Right now cssnext compiles the result of <code>color()</code> to hex or rgba values. When the <code>color</code> function arrives in browsers, the compilation won't be necessary. The color manipulation can happen at runtime.
</p>

<h3>Nesting</h3>
<p>
  Nesting is an indispensable feature introduced by CSS preprocessors. A must for any comfortable styling setup. Tab Atkins has a <a href="http://tabatkins.github.io/specs/css-nesting/">spec</a> in-progress for CSS nesting and cssnext makes it available today.
</p>
<p>
  This was mostly legwork. The CSS syntax for nesting includes a leading <code>&</code> before nested blocks. For example, the following is a Sass snippet from my Projects page:
</p>
<pre><code class="language-scss">.projects-list {
  ...

  li {
    & > div {...}
  }

  a {
    ...

    &:hover,
    &:focus {...}

    &::after {...}
  }

  @media (min-width: 640px) {...}
}</code></pre>
<p>
  For CSS nesting, I changed that to:
</p>
<pre><code class="language-css">.projects-list {
  ...

  & li {
    & > div {...}
  }

  & a {
    ...

    &:hover,
    &:focus {...}

    &::after {...}
  }

  @media (min-width: 640px) {...}
}</code></pre>
<p>
  Basic nesting requires the leading <code>&</code>. Pseudo classes and selectors are the same in Sass and CSS. Media queries don't need a leading <code>&</code>.
</p>
<p>
  Also worth noting is <code>@nest</code>. As mentioned in the <a href="http://cssnext.io/features/#nesting">docs</a>, complex nesting requires <code>@nest</code> instead of <code>&</code>. I didn't have any use cases for it on this project, but may in the future.
</p>

<h3>Extend and Placeholder classes</h3>

<p>
  I was using Sass <code>@extend</code> and placeholder classes for common styles. Here's an example usage responsible for styling Futura headings:
</p>
<pre><code class="language-scss">%futura {
  font-family: 'futura-pt', helvetica, sans-serif;
}

%futura-heading {
  @extend %futura;
  font-weight: 700;
  line-height: 1.1;
  text-transform: uppercase;
}</code></pre>
<p>
  and an example usage:
</p>
<pre><code class="language-scss">.my-heading {
  @extend %futura-heading;
}</code></pre>

<p>
  We looked at CSS custom properties usage earlier. There's a related in-progress <a href="http://tabatkins.github.io/specs/css-apply-rule/">spec</a> for the <code>@apply</code> rule. <code>@apply</code> allows you to store a set of properties and reference them in selectors. I used <code>@apply</code> in place of Sass's <code>extend</code>.
</p>
<p>
  Back in <code>setup.css</code> I added the updated Futura heading properties:
</p>

<pre><code class="language-css">:root {
  ...

  --franklin: {
    font-family: 'futura-pt', helvetica, sans-serif;
  };

  --franklin-heading: {
    @apply --franklin;
    font-weight: 700;
    line-height: 1.1;
    text-transform: uppercase;
  };
}</code></pre>

<p>
  and an example usage:
</p>

<pre><code class="language-scss">.my-heading {
  @apply --franklin-heading;
}</code></pre>

<p>
  <code>@apply</code> is not <code>extend</code>. In the current form in cssnext, <code>@apply</code> copies the properties and values to each rule. This is a small project so that's OK. On larger projects the extra properties may cause too much bloat. At that time it would probably be best to use a common class name to get similar results.
</p>

<p>
  At this point I had the site looking as it did before the changes. The Projects page was an exception. On it I used a different color for each project tile. Next I'll describe how styling that correctly without Sass required more work and more typing.
</p>

<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-postcss-3.png" alt="A screenshot of tylergaw.com/projects" />
  <figcaption>
    The colorful tiles of the Projects page
  </figcation>
</figure>

<h2>Mixins with arguments</h2>

<p>
  To make writing the projects styles easier I used a Sass mixin. The mixin took a single argument, the color for the tile. Here's the <code>project-block</code> mixin:
</p>

<pre><code class="language-scss">@mixin project-block ($c) {
  background-color: $c;

  a {
    color: $c;

    &:hover {
      background-color: $c;
      color: $offwhite);
    }
  }
}</code></pre>
<p>
  and example usage:
</p>
<pre><code class="language-scss">.p-jribbble {
  @include project-block(#ff0066);
}</code></pre>

<p>
  At the time of this writing, I couldn't find a way to mimic this functionality in CSS. Custom property sets with <code>@apply</code> aren't functions, so you can't pass them arguments. In the future, it might be possible to use custom selectors for argument magic. The <a href="https://drafts.csswg.org/css-extensions/#declarative-custom-selector">draft spec</a> has a complex example that looks promising. Right now, I'll admit, I don't fully understand how it works.
</p>

<p>
  That didn't mean I was out of luck. The CSS I write is longer than the Sass, but not by much. I also made use of another in-progress CSS feature; the <a href="https://drafts.csswg.org/selectors-4/#matches">matches</a> selector.
</p>

<p>
  Here's an example of the CSS replacement for the <code>project-block</code> mixin:
</p>
<pre><code class="language-css">.p-jribbble,
.p-jribbble a:matches(:hover, :focus) {
  background-color: var(--color-jrb);

  & a {
    color: var(--color-jrb);
  }
}</code></pre>
<p>
  The color variables are in a <code>:root</code> scope earlier in the file. cssnext compiles the above CSS to:
</p>
<pre><code class="language-css">.p-jribbble,
.p-jribbble a:hover,
.p-jribbble a:focus {
  background-color: #ff0066
}

.p-jribbble a,
.p-jribbble a:hover a,
.p-jribbble a:focus a {
  color: #ff0066;
}</code></pre>
<p>
  The last two selectors <code>...a a:hover</code> and <code>...a a:focus</code> won't match any elements. They're unecessary, but aside from a few more bytes they don't hurt anything. I preferred nesting the <code>a</code> selector for code readability.
</p>

<h2>More PostCSS</h2>
<p>
  With the styles back in proper order, I decided to take advantage of more PostCSS plugins. I used <a href="https://github.com/hail2u/node-css-mqpacker">css mqpacker</a> to combine media queries that share the same query. I also used <a href="http://cssnano.co/">cssnano</a> for code optimization.
</p>
<p>
  This is where I'm looking forward to using a PostCSS setup. With Sass I felt locked in to the features in the current version. Because PostCSS works as a collection of plugins, it's extendible. If I have a specific need, I can write a plugin for it. The potential there is exciting.
</p>

<h2>I'm sold</h2>
<p>
  After working with this setup for a few days, I'm all in. Making the switch from Sass syntax to new CSS syntax has been easy. And that's after five or six years of using Sass on every project I worked on.
</p>
<p>
  I enjoy the shift in thinking this brings. cssnext has a similar approach to CSS as <a href="https://babeljs.io/">Babel</a> has to JavaScript. They both allow you to write the language as it is and as it will be in the future.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
      <link>https://tylergaw.com/articles/sass-to-postcss</link>
      <guid>https://tylergaw.com/articles/sass-to-postcss</guid>
      <pubDate>Wed, 28 Dec 2016 04:30:00 EST</pubDate>
    </item>
    <item>
      <title>Rewriting Day Player for Sketch 40+</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <description>
        <![CDATA[
<p class="entry-intro">
  At 2 ½ my placeholder image plugin was showing signs of age and side-project neglect. I decided to make it a priority to get <a href="https://github.com/tylergaw/day-player" title="View Day Player on GitHub">Day Player</a> back in working order. In doing so, I would also need to educate myself on the latest in Sketch Plugin development. I didn't know it when I started, but I would also put together a fun build and release setup for Sketch plugins.
</p>
<p>
  Day Player has a limited scope. It requests images from placeholder image services and places them in Sketch documents. This <code>3.0.0</code> rewrite and release hasn't changed the scope. I did end up cutting two features and adding a new one.
</p>
<h2>What got removed?</h2>
<p>
  The first feature cut was the placeholder service <a href="http://lorempixel.com/">Lorempixel.com</a>. I removed it because it doesn't support <code>https</code>. Day Player doesn't transport sensitive data. Still, I decided not to use services without <code>https</code> support.
</p>
<p>
  The second feature cut was <a href="https://github.com/tylergaw/day-player/pull/9">remembering user values for each service</a>. I cut this for time. I didn't want to hold up the release for a non-essential feature. There's a good chance I'll circle back and add this in the future. Or, if you're feeling up to it, <a href="https://github.com/tylergaw/day-player#contributing-to-this-project">open a pull request</a>.
</p>
<h2>What’s new?</h2>
<p>
  The one new feature is the ability to add an image with the same dimensions and position <a href="https://github.com/tylergaw/day-player#creating-images-with-dimensions-and-position-of-existing-layers">as an existing layer</a>.
</p>
<figure>
  <a href="https://github.com/tylergaw/day-player">
    <img src="https://d3vv6lp55qjaqc.cloudfront.net/items/0o1M3n07223o223D2C3R/Screen%20Recording%202016-11-13%20at%2003.23%20PM.gif" alt="Animated gif showing new Day Player functionality" />
  </a>
  <figcaption>
    Placing an image with the same dimensions and position as an existing layer
  </figcation>
</figure>
<p>
  I added this because of a <a href="https://github.com/tylergaw/day-player/issues/27">user request</a>. While it's not exactly what the user was asking for, it feels close enough to get the job done. And, as a user myself, it's a welcome addition.
</p>
<h2>What’s next?</h2>
<p>
  As expected, within a day or two of release, I got the first “this doesn't work” <a href="https://github.com/tylergaw/day-player/issues/37">issue</a>. After a quick back and forth, it seems we've isolated the problem to macOS Sierra. I'm a slow new adopter, so I still haven't reproduced the issue. Sooner than later I'll update a machine and find and fix what's broken. If you have any tips for what may have changed, let me know here or on that issue.
</p>
<h2>How do you build and release this?</h2>
<p>
  So glad you asked. This is a topic I want to write in more detail about in the future because it's fun, but I'll give a few highlights now.
</p>
<h3>Building</h3>
<p>
  I'm not using Babel or any other code transmogrifying. I write in the JavaScript available in JavaScriptCore. In my day-to-day JS I use Babel and enjoy using ES2015+ features. With the current JSCore we have access to a limited set of new features. We can use <code>const</code> (but not <code>let</code>). Template literals are available to make short work of string munging. And that's about it. No arrow functions or other niceties yet. They'll get there. As I do more plugin development, I'll likely update my build tooling to use Babel.
</p>
<p>
  Sketch plugins give you access to the Cocoa Framework. This allows you to write JS to create and interact with Cocoa elements. If you've ever written code to do this, you know that it doesn't have the smoothest API. A fun aspect of Day Player was writing components to act as <a href="https://github.com/tylergaw/day-player/tree/3.0.0/src/components">facades</a> for each Cocoa element needed. These components allow me to write more idiomatic code. They also allow for writing less code and provide a chainable interface.
</p>
<p>
  For example, here's the code required to create a text label using regular Cocoa:
</p>
<pre><code class="language-javascript">const textField = NSTextField.alloc().initWithFrame(NSMakeRect(0, 0, 350, 16));
textField.setDrawsBackground(false);
textField.setEditable(false);
textField.setBezeled(false);
textField.setSelectable(true);
textField.setStringValue('My Label:');
</code></pre>
<p>
  and the code required using the <code>Label</code> <a href="https://github.com/tylergaw/day-player/blob/3.0.0/src/components/Label.js">component</a>:
</p>
<pre><code class="language-javascript">new Label({
  value: 'My Label:'
});
</code></pre>
<p>
  Each image service has similar options. Because of this, I was able to abstract common setup code to a few functions. Those functions live in <a href="https://github.com/tylergaw/day-player/blob/3.0.0/src/utils.js">utils.js</a>. Utils.js also includes comments describing my learning process with the excellent and in-progress <a href="http://developer.sketchapp.com/reference/api/">Sketch JS API</a>.
</p>
<h3>Releasing</h3>
<p>
  <a href="https://github.com/tylergaw/day-player#installation">Installing</a> Day Player takes 3 steps; download, unzip, double-click. It was important to make that process as easy as possible. I want the user to use the plugin, not spend time figuring out how to get it.
</p>

<p>
  As as plugin author, it's also important that the release process is as painless as possible. The release process works by chaining 5 primary tools together. Make, Node.js, Git, Travis CI, and AWS S3.
</p>
<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-dayplayer-process.png" alt="An illustration showing the steps of the Day Player release process">
  <figcaption>
    A diagram showing the steps of the release process
  </figcation>
</figure>
<p>
  Here's how it works. When I'm ready to ship a new release I update the <code>version</code> in <code>package.json</code>. I then run <code>make release</code>. That in turn uses Node.js to execute the <a href="https://github.com/tylergaw/day-player/blob/3.0.0/scripts/release.js">release.js</a> script. I could run <code>node scripts/release.js</code> directly, but I prefer to put it in a Make target so all build commands are in one place.
</p>
<p>
  <code>Release.js</code> first confirms I want to release. I put this in as check to keep myself from releasing an incorrect version. The script then creates a new Git tag and pushes it to the origin at GitHub. At this point, the process is on autopilot.
</p>
<figure>
  <a href="https://github.com/tylergaw/day-player/blob/3.0.0/scripts/release.js">
    <img src="https://cl.ly/2p0j2O0Q1a3p/Screen%20Shot%202016-12-18%20at%208.18.39%20PM.png" alt="A screenshot of a command prompt confirming the release of Day Player" />
  </a>
  <figcaption>
    The friendly prompt I see when releasing Day Player
  </figcation>
</figure>
<p>
  Travis CI now takes over. In <a href="https://github.com/tylergaw/day-player/blob/3.0.0/.travis.yml">travis.yml</a> we have the following:
</p>
<pre><code class="language-javascript">deploy:
- provider: script
  script: make publish
  on:
    tags: true
</code></pre>
<p>
  This tells Travis to watch for builds that create new Git tags. When it sees a tag it should run <code>make publish</code> after all other tasks are complete.
</p>
<p>
  The <a href="https://github.com/tylergaw/day-player/blob/3.0.0/Makefile#L53">publish</a> Make target handles packaging the plugin and uploading it to S3. It first creates a zip file of the Sketch plugin <a href="http://developer.sketchapp.com/introduction/plugin-bundles/">bundle</a>. Then using the AWS CLI, copies the zip file to the Day Player bucket; <code>s3://day-player</code>.
</p>
<p>
  Interacting with AWS CLI in this way requires extra setup. First, we install the tool during the Travis <a href="https://github.com/tylergaw/day-player/blob/3.0.0/.travis.yml#L13">install step</a>.
</p>
<pre><code class="language-javascript">install:
- pip install --user -U awscli
</code></pre>
<p>
  The next steps ensure Travis can make safe requests to AWS on my behalf. I set two environment variables in the <a href="https://github.com/tylergaw/day-player/blob/3.0.0/.travis.yml#L24">travis conf</a>. <code>AWS_ACCESS_KEY_ID</code> and an encrypted value for the <code>secure</code> key. The encrypted key:value is for the <code>AWS_SECRET_ACCESS_KEY</code>. The access key is not sensitive, so it's OK to be out in the open. The secret is for my eyes only. I used the Travis CLI tool to encrypt that value. You can read about that process in the <a href="https://docs.travis-ci.com/user/environment-variables/#Encrypting-environment-variables">Travis docs</a>.
</p>
<p>
  By default, the AWS CLI tool looks for the environment variables set above. Requests use them for authentication if found.
</p>
<p>
  You may be wondering where the AWS access key and secret come from. This is an area where I use a best practice for handling AWS credentials. I've set up an AWS IAM Role that only has permission to upload objects to the Day Player S3 bucket. Setting up a restricted IAM Role is outside the scope of this post. A Google search for “IAM role for single bucket” returns up-to-date articles on the topic. That's how I figured it out for this process.
</p>
<h2>All this for placeholder images?</h2>
<p>
  Yeah! This is a side project and my side projects are for learning, tinkering, and fun. If I also build things that are useful to other folks at the same time, that makes it even better.
</p>
<p>
  Download <a href="https://github.com/tylergaw/day-player/releases/tag/3.0.0">Day Player 3.0</a>.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
      <link>https://tylergaw.com/articles/rewriting-day-player-for-sketch-40</link>
      <guid>https://tylergaw.com/articles/rewriting-day-player-for-sketch-40</guid>
      <pubDate>Wed, 21 Dec 2016 08:30:00 EST</pubDate>
    </item>
    <item>
      <title>Overawe</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <description>
        <![CDATA[
<p class="entry-intro">
  Using Web technolgies as a medium for motion graphics was maybe not on the
  mind of Tim Berners-Lee when he came up with the WWW. For the past few months
  I’ve been working on a <a href="http://lab.tylergaw.com/overawe">project</a>
  that does just that.
</p>
<p>
  Web folks–especially those of us working on the front-end of things–tend to be
  a bit consumed by tools and process. I sometimes get fatigued by the
  near constant chatter of new frameworks, build tools, package managers,
  plugins and on and on. I enjoy those tools and their benefits, but when
  I get worn down by them I need something to help pull me up and remind me why
  I love working on the Web. Our tools and processes are just means. I needed
  to work on something that would allow me to focus 100% on the end. Where the code creating
  it was unimportant. Where I could do things the long, hard, stupid way. And where I could bend and
  break best practices. That’s what I set out to do with what became
  <a href="http://lab.tylergaw.com/overawe">Overawe</a>.
</p>
<h2>What is this?</h2>
<p>
  I’m no stranger to using HTML/CSS to build projects that <a href="http://lab.tylergaw.com/themanfromhollywood/">exist</a>
  <a href="http://lab.tylergaw.com/css-shaky-animation/">a bit</a>
  <a href="http://lab.tylergaw.com/css-true-titles/">outside the norm</a>. Until now
  I’ve considered these projects “experiments”. They seemed like a way to try
  out new–at the time–CSS features. I think I’m beyond that point. Things like
  CSS animations are here to stay. With Overawe–and other projects like
  it I’ll do–I’m not experimenting with CSS anymore. I’m choosing the web as my
  preferred medium for creating art.
  HTML, CSS, and JavaScript are my paintbrushes and web browsers are my canvases.
  And how cool is that? There's not a single, fixed size, always-the-same canvas.
  Each browser and each operating system and each computer that runs Overawe will
  run it a bit different. This is due to feature implementation, processor
  strength, etc. It’s also different based on each viewers' preferences. What size
  is their screen? Are they running their browser in full screen or presentation mode?
  Do they have a slow network connection?
  Do they have a bunch of other programs running that will cause Overawe to stutter?
</p>
<p>
  For each one of the questions and many others, there is an effect on the project
  and how each viewer experiences it. I think that's incredible. Some people will
  hate it because it doesn't appear to do anything. For some it will run
  the same as it does for me on my local development server. I have no control
  over it and that makes it fun.
</p>
<h2>Where did this come from?</h2>
<p>
  The idea for the format came from those cool MTV identities. You know, the short clips
  they play between segments. If you're not familiar,
  <a href="https://www.youtube.com/watch?v=3u_UQoMViEM">here’s an hour of them </a>.
  I wanted to make something similar by pairing graphics with short audio
  clips. Each section is independent of the others. With some of them I have an
  idea about what it means and what I’m saying with it. Others, I made just because they
  give me chuckle.
</p>
<p>
  Coming up with each section would be sporadic. Sometimes I would sit down and
  just start writing words and phrases that popped into my head. Other times I would
  be walking or taking shower or just doing other things and an idea for a section
  would come into my head. The process was loose. Once I had a phrase or an idea
  I'd hop into Photoshop and just play. I tried not to spend too much time on any
  one section. I would just throw together text and images and apply whichever
  styles and filters felt right at the time. I didn't worry about “mistakes”. Because
  this project was about creating a little world of it's own, there are no mistakes.
  Whatever ended up on the screen was supposed to be there in that form.
</p>
<h2>How did you do this?</h2>
<p>
  While I don’t want this article or poject to be defined by the means of creation, I do want
  them to be available to anyone curious. As with most of my projects, you can
  access the <a href="https://github.com/tylergaw/overawe">source code on Github</a>.
  All the animations are done using CSS. I did make use of the Web Audio API to
  fiddle with the audio clips. Each one is different, but I do things like
  speed up or slow down playback, add distortion, and apply high and low band filters.
  This is another area that gets interesting in different browsers. The Web Audio API
  is implemented different across browsers so the audio sounds different depending
  on which one you're using.
</p>
<p>
  This was a lot of fun. Any time I get to sit down and create my own little
  world is good time. If no other person saw this but me, it’s still served it’s
  purpose. It allowed me to just make a thing without the normal internal
  debates about if I was doing every little thing the "right" way. I encourage everyone to
  do the same regardless of the medium.
</p>
<p>
  <a href="http://lab.tylergaw.com/overawe">http://lab.tylergaw.com/overawe</a>
  <br>
  <a href="https://github.com/tylergaw/overawe">https://github.com/tylergaw/overawe</a>
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
      <link>https://tylergaw.com/articles/overawe</link>
      <guid>https://tylergaw.com/articles/overawe</guid>
      <pubDate>Thu, 25 Jun 2015 10:00:00 EST</pubDate>
    </item>
    <item>
      <title>Building OS X Apps with JavaScript</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <description>
        <![CDATA[
<p class="entry-intro">
  OS X Yosemite introduced JavaScript for Automation. This makes it possible to
  access native OS X frameworks with JavaScript. I’ve been
  digging in to this new world and putting together <a href="https://github.com/tylergaw/js-osx-app-examples">examples</a>
  along the way. In this post I’ll explain the basics and step through building
  a small <a href="https://github.com/tylergaw/js-osx-app-examples/tree/master/ChooseAndDisplayImage.app">example app</a>.
</p>

<p>
  WWDC 2014 included a session on
  <a href="https://developer.apple.com/librarY/prerelease/mac/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/index.html">JavaScript for Automation</a>.
  The session explained that you would be able to use JavaScript in place of
  AppleScript to automate applications. That alone is exciting news. Being able to automate
  repetitive tasks using AppleScript has been around a long time. AppleScript is
  not the most fun thing to write so using a familiar syntax in its place is welcome.
</p>
<p>
  During the session the presenter explains the Objective-C bridge. This is where
  things get super cool. The bridge allows you to import any Objective-C framework
  into JS applications. For example, if you want to build a GUI using standard OS X
  controls you would import Cocoa:
</p>
<pre><code class="language-javascript">ObjC.import("Cocoa");
</code></pre>
<p>
  The <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/ObjC_classic/_index.html">Foundation</a>
  framework does what its name suggests. It provides the building blocks for
  OS X apps. The framework includes a large amount of classes and protocols.
  <code>NSArray</code>, <code>NSURL</code>, <code>NSUserNotification</code>, and so on. You may not be familiar with
  those classes, but their names hint at what they do. Because of its importance,
  you can use its classes without needing to import it in new apps. They are
  available by default.
</p>
<p>
  As far as I can tell, you can use JavaScript to build anything that you can
  with Objective-C or Swift.
</p>
<h2>Building an example app</h2>
<p class="special-note">
  <b>Note:</b> You need Yosemite Developer
  Preview 7+ for the examples to work.
</p>
<p>
  The best way to get going is to jump in and build something. I’m
  going to walk through building a small app that lets you display an image
  from your computer.
</p>
<p>
  You can download the completed example from my <a href="https://github.com/tylergaw/js-osx-app-examples">example apps repo</a>.
</p>
<figure>
    <img src="https://tylergaw.com/articles/assets/post-image-jsosx-example-01.jpg" alt="">
    <figcaption>
      A screenshot of the app we’ll build.
    </figcation>
</figure>
<p>
  Making up the app is; a window, text label, text input, and button. Or by their
  class names; <code>NSWindow</code>, <code>NSTextField</code>, <code>NSTextField</code>,
  and <code>NSButton</code>.
</p>
<p>
  Clicking the Choose an Image button will show an <code>NSOpenPanel</code> that allows you
  to select a file. We’ll configure the panel to restrict the file selection to .jpg,
  .png, or .gif.
</p>
<p>
  After selecting an image, we'll display it in the window. The window will
  resize to match the width and height of the image plus the height of the controls.
  We’ll also set a minimum width and height for the window to make sure the controls
  don’t get cut off.
</p>
<h3>Setting up the project</h3>
<p>
  Open the Apple Script Editor application located in <code>Applications > Utilities</code>.
  Script Editor ain’t the best editor I’ve ever used, but it’s necessary for now.
  It has a bunch of features that we need for building JS OS X apps. I’m not sure
  what goes on beyond the scenes, but it can compile and run your scripts as apps.
  It also creates extra stuff that we need like an Info.plist file. My guess is there
  are ways to make other editors do the same, but I haven’t looked into it yet.
</p>
<p>
  Create a new document with <code>File > New</code> or <code>cmd + n</code>. The
  first thing we need to do is save the document as an application.
  Save with <code>File > Save</code> or <code>cmd + s</code>.
  Don’t confirm the save right away. There are two options that are necessary to make
  this run as an application.
</p>
<figure>
    <img src="https://tylergaw.com/articles/assets/post-image-jsosx-save.jpg" alt="">
    <figcaption>
      The Script Editor Save dialog with important fields highlighted
    </figcation>
</figure>
<p>
  Change the File Format to “Application”. Check the “Stay open after run handler”
  option. It’s important to set both of these options the first time you save the
  file because–as far as I can tell–there is no way to set them later. If you
  miss one of these you’ll have to create a new document and copy your code over.
</p>
<p>
  If anyone knows a way to change the settings after creation, let me know.
</p>
<p>
  If you don’t select “Stay open after run handler” your app will open, flash for
  a split second, and then close. There is not much in the way of documentation for
  this stuff online. I only learned this detail after hours of forehead/keyboard merging.
</p>
<h3>Let’s make something do something!</h3>
<p>
  Add the following two lines to your script and then run it with <code>Script > Run Application</code>
  or <code>opt + cmd + r</code>.
</p>
<pre><code class="language-javascript">ObjC.import("Cocoa");
$.NSLog("Hi everybody!");
</code></pre>
<p>
  Not much happened. The only visible changes are in the menu bar and dock. The app
  name along with <code>File</code> and <code>Edit</code> menus are in the menu bar.
  You can see the app is running because its icon is in your dock.
</p>
<p>
  Where is the “Hi everybody!”? What is with the dollar sign, what is this, jQuery?
  Quit the app with <code>File > Quit</code> or <code>cmd + q</code> and
  we’ll find out where that <code>NSLog</code> went.
</p>
<p>
  Open the Console app: <code>Applications > Utilities > Console</code>. Every
  application can log messages to the console. This console is not much different
  than Developer Tools in Chrome, Safari, or Firefox. The main difference is you
  use it for debugging applications instead of websites.
</p>
<p>
  There’s a lot of messages in the console. You can filter it down by
  typing “applet” into the search field in the top right corner. Leave “applet” in
  the search field and go back to Script Editor. Run the application again
  with <code>opt + cmd + r</code>.
</p>
<figure>
    <img src="https://tylergaw.com/articles/assets/post-image-jsosx-console.jpg" alt="A screenshot showing a log message in Console.app">
</figure>
<p>
  Did you see it!? The message “Hi everybody!” should have logged to the console.
  If you didn’t see it, quit your application and run it again. A lot of times
  I forget to quit and the code doesn’t run again.
</p>
<h3>What about the dollar sign?</h3>
<p>
  The dollar sign is your access to the Objective-C bridge. Any time you need
  to access an Objective-c class or constant, you use <code>$.foo</code> or
  <code>ObjC.foo</code>. There are a couple of other ways
  to use <code>$</code> that I’ll cover later.
</p>
<p>
  Console App and <code>NSLog</code> are indispensable tools, you’ll use them
  non-stop for debugging.
  For examples of logging things other than strings, have a look at my
  <a href="https://github.com/tylergaw/js-osx-app-examples/tree/master/NSLog.app">NSLog example</a>.
</p>
<h3>Creating the window</h3>
<p>
  Let’s make something we can see and interact with. Update your script to look
  like the following code:
</p>
<pre><code class="language-javascript">ObjC.import("Cocoa");

var styleMask = $.NSTitledWindowMask | $.NSClosableWindowMask | $.NSMiniaturizableWindowMask;
var windowHeight = 85;
var windowWidth = 600;
var ctrlsHeight = 80;
var minWidth = 400;
var minHeight = 340;
var window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer(
  $.NSMakeRect(0, 0, windowWidth, windowHeight),
  styleMask,
  $.NSBackingStoreBuffered,
  false
);

window.center;
window.title = "Choose and Display Image";
window.makeKeyAndOrderFront(window);
</code></pre>
<p>
  When that’s in place, run the app. <code>opt + cmd + r</code>. Now we’re
  talking! With a small amount of code we’ve built an app that launches a window
  with a title that we can move, minimize, and close.
</p>
<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-jsosx-basic-window.jpg" alt="">
  <figcaption>A basic NSWindow created with JS</figcaption>
</figure>
<p>
  If you’ve never built an app with Objective-C or Cocoa–like me–parts of this might
  look like a lot of gibberish. For me, that was due to the length of the
  method names. I enjoy descriptive names, but Cocoa takes it to the extreme.
</p>
<p>
  Looking past that though, this is JavaScript. This code looks just like code
  you’d write when building a website.
</p>
<p>
  What’s going on with that first new line where we set <code>styleMask</code>?
  You use style masks to configure windows. Each style option says what it adds;
  a title, a close button, a minimize button. These options are constants. Use a pipe “|”
  to separate options. That pipe is a C bitwise <code>OR</code> operator.
  I’m not gonna pretend to know what that means. I just know it’s
  needed to combine style options for a mask and that’s good enough for me.
</p>
<p>
  There are more style options. You can read about them in <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/WinPanel/Tasks/SettingWindowAppearance.html">the docs</a>.
  <code>NSResizableWindowMask</code> is one you’ll use. Try adding it to the
  style mask to see what it does.
</p>
<p>
  There are a couple interesting things about syntax that you’ll need to remember.
  <code>$.NSWindow.alloc</code> calls the <code>alloc</code> method of <code>NSWindow</code>.
  Notice there are no parenthesis “()” after alloc. In JavaScript that’s how we
  access properties, not how we call a methods. What’s with that? In JS for OS X parenthesis are only
  allowed when calling a method if you pass arguments to it. If you use parenthesis
  with no arguments, you will get a runtime error. Check Console for errors when
  things don’t do what you think they should.
</p>
<p>
  The next thing is about that super long method name:
</p>
<pre><code class="language-javascript">initWithContentRectStyleMaskBackingDefer</code></pre>
<p>
  Take a look at the <code>NSWindow</code> <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSWindow_Class/Reference/Reference.html#jumpTo_105">docs</a>
  for that method and you’ll notice it looks a bit different.
</p>
<pre><code class="language-javascript">initWithContentRect:styleMask:backing:defer:</code></pre>
<p>
  In Objective-C you would create the same window we did like this:
</p>
<pre><code class="language-clike">NSWindow* window [[NSWindow alloc]
  initWithContentRect: NSMakeRect(0, 0, windowWidth, windowHeight)
  styleMask: styleMask,
  backing: NSBackingStoreBuffered
  defer: NO];
</code></pre>
<p>
  The thing to note are the colons “:” in the original method signature. When you convert
  and Objective-C method to JS you remove the colons and capitalize the first letter
  following it. When you see two items in brackets “[]”, that is calling a method
  of a class or object. <code>[NSWindow alloc]</code> calls the <code>alloc</code> method
  of <code>NSWindow</code>. For JS, convert those to dot-notation and parenthesis
  if necessary; <code>NSWindow.alloc</code>.
</p>
<p>
  I think the rest of the code to create and display the window is straightforward-enough.
  I’ll skip any more detailed description of it.
  It takes time and a lot of reading the docs to understand what’s happening
  for each step along the way, but you’ll get there. As long as the window is
  showing up you’re doing great. Let’s do more.
</p>
<h3>Adding the controls</h3>
<p>
  We need a label, text field, and button in this window. We’ll use <code>NSTextField</code>
  and <code>NSButton</code> to make that happen. Update your script with the
  following code and then run the app.
</p>
<pre><code class="language-javascript">ObjC.import("Cocoa");

var styleMask = $.NSTitledWindowMask | $.NSClosableWindowMask | $.NSMiniaturizableWindowMask;
var windowHeight = 85;
var windowWidth = 600;
var ctrlsHeight = 80;
var minWidth = 400;
var minHeight = 340;
var window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer(
  $.NSMakeRect(0, 0, windowWidth, windowHeight),
  styleMask,
  $.NSBackingStoreBuffered,
  false
);

var textFieldLabel = $.NSTextField.alloc.initWithFrame($.NSMakeRect(25, (windowHeight - 40), 200, 24));
textFieldLabel.stringValue = "Image: (jpg, png, or gif)";
textFieldLabel.drawsBackground = false;
textFieldLabel.editable = false;
textFieldLabel.bezeled = false;
textFieldLabel.selectable = true;

var textField = $.NSTextField.alloc.initWithFrame($.NSMakeRect(25, (windowHeight - 60), 205, 24));
textField.editable = false;

var btn = $.NSButton.alloc.initWithFrame($.NSMakeRect(230, (windowHeight - 62), 150, 25));
btn.title = "Choose an Image...";
btn.bezelStyle = $.NSRoundedBezelStyle;
btn.buttonType = $.NSMomentaryLightButton;

window.contentView.addSubview(textFieldLabel);
window.contentView.addSubview(textField);
window.contentView.addSubview(btn);

window.center;
window.title = "Choose and Display Image";
window.makeKeyAndOrderFront(window);
</code></pre>
<p>
  If things worked you’ll now have a window with controls.
  You can’t type in the text field and the button doesn’t do anything, but hey,
  we’re getting somewhere.
</p>
<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-jsosx-controls.jpg" alt="">
  <figcaption>Label, field, and button elements in the window</figcaption>
</figure>
<p>
  What did we do in that addition? <code>textFieldLabel</code> and <code>textField</code>
  are similar. They’re both instances of <code>NSTextField</code>. We make them
  in a similar way as we made the window. When you see <code>initWithFrame</code>
  and <code>NSMakeRect</code>, it’s a good sign that the script is creating
  a UI element. <code>NSMakeRect</code> does what it says. It makes a rectangle
  with the given position and dimensions; <code>(x, y, width, height)</code>. That
  creates what’s called a struct in Objective-C. In JavaScript we’d refer to it as an
  object or hash or maybe dict. It’s a thing with keys and values.
</p>
<p>
  After creating the text fields we set a handful of properties on each to get the
  results we’re looking for. Cocoa doesn’t have anything like the html <code>label</code>
  element. For that we make our own by disabling editing and background styling.
</p>
<p>
  We’ll populate the text field programmatically, so we disable editing on it as well.
  If we didn’t need that, creating a standard text field is a one-liner.
</p>
<p>
  For the button we use <code>NSButton</code>. Like the text fields, creating
  it requires drawing a rectangle. There are two properties that stand out;
  <code>bezelStyle</code> and <code>buttonType</code>. The values for both are
  constants. These properties control how the button will render and the style
  it will have. Check out the <code>NSButton</code>
  <a href="https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSButton_Class/Reference/Reference.html">docs</a>
  to see everything you can do with it. I also have
  an <a href="https://github.com/tylergaw/js-osx-app-examples/tree/master/NSButton.app">example app</a>
  that shows the different button types and styles in action.
</p>
<p>
  The last new thing we do is add the elements to the window with <code>addSubview</code>.
  When I first tried this I did <code>window.addSubview(theView)</code>. That works
  for other standard views that you create with <code>NSView</code>, but not for
  instances of <code>NSWindow</code>. I’m not sure why that is, but for windows you
  need to add sub views to the <code>contentView</code>. The docs describe it as;
  “The highest accessible NSView object in the window’s view hierarchy”. Works for
  me.
</p>
<h3>Making the button do something</h3>
<p>
  When clicking the Choose an Image button, we want a panel to open that shows
  files on our computer. Before we do that, let’s warm up by logging a message
  to the console when clicking the button.
</p>
<p>
  In JavaScript you bind event listeners to elements to handle clicks.
  Objective-C doesn’t use quite that same concept. It has what’s called message passing.
  With it, you send a message containing a method name to an object. That object needs
  to have information about what to do when it receives a message containing
  the method name. That may not be a spot-on description, but it’s
  how I understand it.
</p>
<p>
  The first thing we need to do is set the button <code>target</code> and <code>action</code>.
  The <code>target</code> is the object we want to send the <code>action</code>
  to. If this doesn’t make sense now, just keep going and it’ll get more clear
  when you see the code. Update the button setup part of the script with the
  following properties.
</p>
<pre><code class="language-javascript">...
btn.target = appDelegate;
btn.action = "btnClickHandler";
...
</code></pre>
<p>
  <code>appDelegate</code> and <code>btnClickHandler</code> don’t exist yet. We need to
  make them. In the next addition to the script, order matters. I put
  comments in the following code to show where you should add the new stuff.
</p>
<pre><code class="language-javascript">ObjC.import("Cocoa");

// New stuff
ObjC.registerSubclass({
  name: "AppDelegate",
  methods: {
    "btnClickHandler": {
      types: ["void", ["id"]],
      implementation: function (sender) {
        $.NSLog("Clicked!");
      }
    }
  }
});

var appDelegate = $.AppDelegate.alloc.init;
// end of new stuff

// Below here is in place already
var textFieldLabel = $.NSTextField.alloc.initWithFrame($.NSMakeRect(25, (windowHeight - 40), 200, 24));
textFieldLabel.stringValue = "Image: (jpg, png, or gif)";
...
</code></pre>
<p>
  Run the app, click the Choose Image button, and watch Console. Do you see “Clicked!”
  when you click the button? If so, that’s pretty darn cool right? If not, double
  check your script matches the code here and look for any errors in Console.
</p>
<h3>Subclassing</h3>
<p>
  What’s this <code>ObjC.registerSubclass</code> all about? Subclassing is a way
  to create a new class that inherits from another Objective-C class. Side note; the likelihood
  I’m using incorrect terminology here is through the roof. Bear with me.
  <code>registerSubclass</code> takes a single argument;
  a JS object with the members of the new object. Members can be; <code>name</code>,
  <code>superclass</code>, <code>protocols</code>, <code>properties</code>, and
  <code>methods</code>. I’m not 100% sure that’s an exhaustive list, but that’s what’s
  in the <a href="https://developer.apple.com/librarY/prerelease/mac/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/index.html#//apple_ref/doc/uid/TP40014508-CH109-SW30">release notes</a>.
</p>
<p>
  That’s all well and good, but what did we do there? Since we didn’t provide a
  <code>superclass</code> we inherited from <code>NSObject</code>. That’s the root
  class of most Objective-C classes. Setting a <code>name</code> let’s us reference
  the object later through the bridge with <code>$</code> or <code>ObjC</code>.
</p>
<p>
  <code>$.AppDelegate.alloc.init;</code> creates an
  instance of our <code>AppDelegate</code> class. Again, notice we don’t
  use parenthesis with the <code>alloc</code> or <code>init</code> methods since
  we’re not passing arguments.
</p>
<h3>Subclass methods</h3>
<p>
  You create a method by giving it any string name. In this case “btnClickHandler”.
  Give it an object with <code>types</code> and <code>implementation</code> members.
  I haven’t found official docs for what the <code>types</code> array should
  contain. Through trial and error I found it goes like this:
</p>
<pre><code class="language-javascript">["return type", ["arg 1 type", "arg 2 type",...]]</code></pre>
<p>
  <code>btnClickHandler</code> won’t return anything so we set the return type to <code>void</code>.
  It takes one parameter, the sender object. In this case it will be the <code>NSButton</code>
  we named <code>btn</code>. We use the “id” type because represents any object.
</p>
<p>
  The full list of types is in the
  <a href="https://developer.apple.com/librarY/prerelease/mac/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/index.html#//apple_ref/doc/uid/TP40014508-CH109-SW32">release notes</a>.
</p>
<p>
  <code>implementation</code> is a normal function. Inside of it you write
  JavaScript. You have the same access to the <code>$</code> bridge as you do
  outside of an object. You also have access to variables you create outside
  the function.
</p>
<h3>A quick note on using protocols</h3>
<p>
  You can implement existing Cocoa protocols with sublcasses,
  but there’s a “gotcha”.
  I found out that if you use the <code>protocols</code> array your script
  will just stop with no errors. I wrote an <a href="https://github.com/tylergaw/js-osx-app-examples/blob/master/docs/subclass-using-NSUserNotificationCenterDelegate.md">example and explanation</a>
  that you’ll want to have a look at if you’re doing that type of thing.
</p>
<h3>Choosing and displaying images</h3>
<p>
  We’re ready to open the panel, choose an image, and display it. Update the
  <code>btnClickHandler</code> implementation function with following code:
</p>
<pre><code class="language-javascript">...
implementation: function (sender) {
  var panel = $.NSOpenPanel.openPanel;
  panel.title = "Choose an Image";

  var allowedTypes = ["jpg", "png", "gif"];
  // NOTE: We bridge the JS array to an NSArray here.
  panel.allowedFileTypes = $(allowedTypes);

  if (panel.runModal == $.NSOKButton) {
    // NOTE: panel.URLs is an NSArray not a JS array
    var imagePath = panel.URLs.objectAtIndex(0).path;
    textField.stringValue = imagePath;

    var img = $.NSImage.alloc.initByReferencingFile(imagePath);
    var imgView = $.NSImageView.alloc.initWithFrame(
    $.NSMakeRect(0, windowHeight, img.size.width, img.size.height));

    window.setFrameDisplay(
      $.NSMakeRect(
        0, 0,
        (img.size.width > minWidth) ? img.size.width : minWidth,
        ((img.size.height > minHeight) ? img.size.height : minHeight) + ctrlsHeight
      ),
      true
    );

    imgView.setImage(img);
    window.contentView.addSubview(imgView);
    window.center;
  }
}
</code></pre>
<p>
  The first thing we do is create an instance of an <code>NSOpenPanel</code>. If
  you’ve ever selected a file to open, or a location to save a file you’ve seen
  panels in action.
</p>
<p>
  We only want the app to open images. Setting <code>allowedFileTypes</code> lets
  us specify the allowed types for the panel. It expects an <code>NSArray</code>. We create
  a JS array with <code>allowedTypes</code>, but we need to convert that to an
  <code>NSArray</code>. We make that conversion with <code>$(allowedTypes)</code>.
  This is another use of the bridge. You use it in this way to bridge
  a JS value to Objective-C. To bridge an Objective-C value to JavaScript you’d
  use <code>$(ObjCThing).js</code>.
</p>
<p>
  We open the panel with <code>panel.runModal</code>. That pauses code execution.
  When you click Cancel or Open, the panel will return a value.
  If you clicked Open, the contstant value of <code>$.NSOKButton</code> is returned.
</p>
<p>
  The other note about <code>panel.URLs</code> is important. In JS, we access
  the first value of an array with <code>array[0]</code>. Because <code>URLs</code>
  is an <code>NSArray</code> we can’t use bracket notation. Instead we use the <code>objectAtIndex</code>
  method. They both give the same results.
</p>
<p>
  Once we have the URL of the image we can create a new <code>NSImage</code>.
  Since it’s common to create an image from a file URL, there’s a handy method
  doing it:
</p>
<pre><code class="language-javascript">initByReferencingFile</code></pre>
<p>
  We create an <code>NSImageView</code> using the same process we did for creating
  other UI elements. <code>imgView</code> handles displaying the image.
</p>
<p>
  We want to change the width and height of our window to match the width and height of
  the image. We also want to make sure we don’t go below a minimum width or height.
  We use <code>setFrameDisplay</code> to change the size of the window.
</p>
<p>
  We wrap things up by setting the image view image and
  adding it to the window. Since its width and
  height have updated we re-center the window.
</p>
<p>
  And that’s our little app. Kick it around, open a bunch of images. And yes,
  animated gifs will display and play so be sure to try some.
</p>
<h3>Tidbits</h3>
<p>
  So far, we’ve been running the app from Script Editor
  with <code>opt + cmd + d</code>. You run the actual app by double clicking
  its icon like any other application.
</p>
<figure>
  <img src="https://tylergaw.com/articles/assets/post-image-jsosx-appicon.jpg" alt="">
  <figcaption>Double click the icon to run the app</figcaption>
</figure>
<p>
  You can update the app icon by replacing <code>/Contents/Resources/applet.icns</code>.
  To access the app resources, right click on the app icon and choose “Show Package Contents”.
</p>
<h2>Why I’m excited</h2>
<p>
  I’m excited about this because I think it has a lot of potential. Here’s
  how I’ve been thinking about it. When Yosemite is available, anyone that installs
  the OS will be able to sit down and write a native application. They will be
  able to do so using one of the most ubiquitous programming languages that exists. They
  won’t need to download or install anything extra. You don’t even have to install
  or open Xcode if you don’t want to. That removes a massive barrier to entry.
  It’s incredible.
</p>
<p>
  I know there’s a whole lot more to building OS X apps than just being able
  whip up a quick script. I don’t have any illusions that JavaScript will become
  the defacto way to build for the Mac. I do think this will allow developers
  to build small apps that make development easier for themselves and for other
  people. Have a team member that’s not so comfortable using the command line?
  Build a quick GUI for them. Need a quick, visual way to create and update
  a large configuration file? Make a little app for it.
</p>
<p>
  Other languages have these capabilities. Python and Ruby have access to the same
  native APIs and people make apps using those languages. Something just feels
  different about being able to use JavaScript. It feels subversive. It feels
  like some of the DIY of the Web knocking on the door of the Desktop.
  Apple left the door unlocked, I’m inviting myself in.
</p>
<p>
  <i>Thanks for reading</i>
</p>
        ]]>
      </description>
      <link>https://tylergaw.com/articles/building-osx-apps-with-js</link>
      <guid>https://tylergaw.com/articles/building-osx-apps-with-js</guid>
      <pubDate>Sat, 27 Sep 2014 17:45:00 EST</pubDate>
    </item>
    <item>
      <title>Media Seek from URL</title>
      <dc:creator>Tyler Gaw</dc:creator>
      <description>
          <![CDATA[
          <p class="entry-intro">
            I recently had a need to be able to control the time of <code>audio</code>
            elements via a URL query string. I whipped up a bit of JavaScript to handle it.
            There’s a demo <a href="http://lab.tylergaw.com/media-seek-from-url/">here</a> and
            the source is <a href="https://github.com/tylergaw/media-seek-from-url">on GitHub</a>.
            I had fun figuring it out and thought maybe you’d enjoy reading about it.
            The following are details about why I needed it and the code I wrote.
          </p>
          <p>
            As with most projects, this one started with a problem that needed fixin’ The
            site this was for had a listing of audio tracks. Each list item included; the <code>audio</code>
            element, a title/link to the page for that track, and a short description.
            Some of list items had extra links to the track’s page in the description.
          </p>
          <p>
            With this setup, the links to the track pages were causing the problem. Here’s
            what was happening; Users were pressing play on one of the tracks in the list.
            They would then click on one of the track’s links. That would take them away
            from the listing to that track’s page. The navigation would cause them to
            lose their place in the track they were listening to. The tracks were podcasts that
            ran from 45 to 90 minutes in length so losing your spot at 33 minutes and 45 seconds was annoying.
          </p>
          <p>
            This is where controlling the time of the <code>audio</code> element came into play.
            Clicking on a link should take the user to the track’s page and
            the track should resume playing where they left off.
          </p>
          <h2>Writing The Thing</h2>
          <p>
            YouTube does this style of jumping around in videos via the URL. I think
            the query string style they use works well, so I took my cues from them. I decided
            I’d use URLs like:<br> <code>http://site.com/tracks/track-name?t=1h22m35s</code>.
          </p>
          <p>
            Two quick things about the code examples and descriptions:
          </p>
          <ol>
            <li>
              I wrote the original code for a specific project. The code I’ll be showing
              here is a modified version of that project’s code. The main difference
              is that there are no links to separate pages. I left that out because I
              felt like the interesting thing is not the navigation between pages.
            </li>
            <li>
              The examples only show <code>audio</code> elements, but you can use the same code
              with <code>video</code> elements.
            </li>
          </ol>
          <p>
            On page load, we run a function called <code>seekIfNeeded</code>. This
            function checks the <code class="language-javascript">window.location.search</code> for the presence of
            the string “t=”. This determines if we need to bother trying to parse
            a time from the URL. This check is by no means fool-proof, but it gets the job done.
          </p>
<pre><code class="language-javascript">function seekIfNeeded () {
  var q = window.location.search;

  if (q.indexOf('t=') > -1) {
    // Do parsing stuff
  }
}
</code></pre>
          <p>
            Once it’s determined there’s a time to parse the fun code starts.
            We declare a couple convenience variables for later use.
            We bind a <code>canplay</code> event to the <code>audio</code>.
            We convert the query string to seconds and then update <code>currentTime</code>
            property of the <code>audio</code> element.
          </p>
<pre><code class="language-javascript">function seekIfNeeded () {
  var q = window.location.search;

  if (q.indexOf('t=') > -1) {
    // Store the "1h34m27s" part of the query string
    var timeString = q.split('=')[1],

      // Store a reference to the audio element with an id of "media"
      media = document.getElementById('media'),

      // Have we updated the time of the media element from the URL before?
      seekedFromURL = false;

    // We can only interact with audio elements when they are ready.
    // Listen for the "canplay" event to know when that is.
    media.addEventListener('canplay', function () {

      // The "canplay" event is triggered every time the audio element
      // is able to play. We only want to change the currentTime of
      // the audio the first time this event fires.
      if (!seekedFromURL) {

        // The currentTime property seeks to a value of seconds in
        // the media element.
        media.currentTime = secondsFromTimeParts(partsFromTimeString(timeString));

        // We've done the seeking, don't do this again.
        seekedFromURL = true;
      }
    });

    media.play();
  }
}
</code></pre>
          <h2>Converting a String to Time</h2>
          <p>
            Things get interesting with the line that sets
            <code>media.currentTime</code>. It’s being set to the return value of the
            <code>secondsFromTimeParts</code> function. That function is given the
            return value of another function, <code>partsFromTimeString</code>.
          </p>
          <p>
            I’ll break down that line of code from the inside out. <code>timeString</code>
            is a string like; “1h32m23s” or “15m10s” or “12s”. Any combination of hours,
            minutes, and seconds. Even though humans can figure out the string
            represents time, the <code>audio</code>
            element isn’t going to understand it. We pass the string to <code>partsFromTimeString</code>.
            That function converts the string into an object of key/value pairs. In the object, the
            keys represent a part of the time and the values the amount of time for each part.
          </p>
<pre><code class="language-javascript">function partsFromTimeString (str) {
  var parts = {h: 0, m: 0, s: 0};

  // Wrapping in a try to avoid an error in case someone gives the 't='
  // query string with no time. It'll just default to zero without it.
  try {

    // The regex match breaks the string into an array
    // like ["1h", "32m", "6s"]
    str.match(/[0-9]+[hms]+/g).forEach(function (val) {

      // Creates an array with two elements, the time and part
      // key like ["32", "m"]
      var part = val.match(/[hms]+|[0-9]+/g);
      parts[part[1]] = parseInt(part[0], 10);
    });
  }
  catch (e) {}

  return parts;
}
</pre></code>
          <p>
            My first idea wasn’t to use a regular expression. I tend to avoid them because
            I only have a cursory knowledge of them. But the query string posed a problem
            I couldn’t solve without a regex. When trying
            to break up a string into specific parts, I look for patterns. A common letter or
            symbol or certain number or anything that repeats. A string like “1h32m23s”
            doesn’t have that, so I needed a more complex pattern.
          </p>
          <p>
            The pattern that sticks out is; a number, followed by one of three letters.
            That letter could be “h”, “m”, or “s”. There’s no certainty
            that all the letters will be there. And they may not appear in the same order. Ambiguity lead
            me down the regular expression path. I suppose that’s what they’re for?
          </p>
          <p>
            The <code>string.match</code> method returns an array of all matches found
            by a given regex. The regex <code class="language-javascript">/[0-9]+[hms]+/g</code> reads the string from left to right
            and says, “I want an integer with a value of 0 through 9, followed by the letter ‘h’, ‘m’, or ‘s’.”
            If the regex finds that alphanumeric combination, it puts it in the array.
          </p>
          <p>
            We’re getting closing to being able to separate the letters from the numbers.
            That’s what we’re after. Even though we want to separate them, we don’t want
            to disassociate them. Each letter gives us valuable information about the
            number. It tells us what part of the time it represents.
          </p>
          <p>
            The next step is to iterate over the array of number/letter pairs one by one.
          </p>
          <p>
            Again, we use the <code>match</code> method with a regex. The <code class="language-javascript">/[hms]+|[0-9]+/g</code>
            regex says, “I want either the letter ‘h’, ‘m’, ‘s’ or a number, 0 through 9.”
            When either of those are found they’re placed in the array we named “part”.
          </p>
          <p>
            The last bit of work is to add each of the time parts to the parts object we
            created at the start of the function. Since we get the time value from a
            string we need to use <code>parseInt</code> to convert it to a integer.
          </p>
          <p>
            Now we have an object of time parts. The object looks like <code class="language-javascript">{h: 1, m: 22, s: 37}</code>.
            The next step in <code>seekIfNeeded</code> is to get seconds from those parts.
            For that, we use the <code>secondsFromTimeParts</code> function.
          </p>
<pre><code class="language-javascript">function secondsFromTimeParts (parts) {
  var seconds = 0;

  seconds += parts.s;
  seconds += parts.m * 60;
  seconds += parts.h * 3600;

  return seconds;
}
</pre></code>
          <p>
            <code>secondsFromTimeParts</code> adds up the values of the time parts object to
            get a total number of seconds. To get the number of seconds from
            minutes we multiply by 60. To get the number of seconds from
            hours we multiply by 3600. 3600 equals 60 minutes per hour times 60 seconds per minute.
          </p>
          <p>
            At this point, we’ve converted a string that represents a time to an actual
            time value the media element can understand. We use that value–the number
            of seconds into the media we want to go–to set the <code>currentTime</code>
            property and then tell it to play.
          </p>
          <h2>Converting a Time to String</h2>
          <p>
            So now we can update the time of a media element with a query string, but what
            if we want to go the other way? What if we have the seconds and we want
            to convert that to our time string?
          </p>
          <p>
            For that we’ll follow the previous process in reverse. We’ll get an object of
            time parts from the seconds and then a time string from the parts.
          </p>
<pre><code class="language-javascript">var timeStr = timeStringFromParts(timePartsFromSeconds(media.currentTime));
          </pre></code>
          <p>
            Again, from the inside out, let’s look at the <code>timePartsFromSeconds</code>
            function.
          </p>
<pre><code class="language-javascript">function timePartsFromSeconds (seconds) {
  var parts = {},
    secondsInt = Math.floor(seconds);

  parts.h = Math.floor((secondsInt / 3600) % 24);
  parts.m = Math.floor(secondsInt / 60);
  parts.s = secondsInt % 60;

  return parts;
}
</pre></code>
          <p>
            We start by creating the empty parts object where we’ll store the key/value pairs.
            <code>media.currentTime</code> gives the number of seconds and milliseconds
            as a float. In this script I decided that I didn’t need milliseconds.
            <code>Math.floor(seconds)</code> removes the decimal point and everything after it.
          </p>
          <p>
            The next few lines use the total number of seconds to determine the number of
            hours and minutes. Those values are set to the appropriate members of the parts
            object. These calculations are the reverse of what we used in <code>secondsFromTimeParts</code>.
            We know there are 3600 seconds in an hour so we divide the total seconds by it.
            We use the modulus–<code>% 24</code>–because anything over 24 hours would be a new
            time part: days. This script doesn’t handle days, but with a handful of additions
            it would be able to. We determine the number of minutes by dividing the total
            seconds by the number of seconds in a minute: 60.
          </p>
          <p>
            That gives us an object of parts–<code class="language-javascript">{h: 1, m: 32, s: 27}</code>–that we need
            to convert into a time string. We’ll do that with the <code>timeStringFromParts</code>
            function.
          </p>
<pre><code class="language-javascript">function timeStringFromParts (parts) {
  var str = '';

  for (key in parts) {
    if (parts[key] > 0) {
      str += parts[key] + key;
    }
  }

  return str;
}
</pre></code>
          <p>
            This small function iterates over the keys in the parts object. For each key,
            if the value is greater than zero we add the value followed by the key to the
            <code>str</code> variable that we’ll return. The string returned will look something
            like “1h32m28s”.
          </p>
          <p>
            <code>secondsFromTimeParts</code> and <code>timeStringFromParts</code> could have
            been combine into a single function like <code>timeStringFromSeconds</code>. I
            chose to separate them because I like to make functions that are as small and
            as specific as possible. Doing so provides clarity and allows for reuse. This
            approach is a tenet of functional programming.
          </p>
          <p>
            And with that, we have our script soup to nuts. If you take a look at the source
            code you’ll see a function named <code>displayTimeURL</code>. That function
            converts a time to a time string for display. It also contains a couple other
            tidbits that may be useful if you need to build something like this yourself.
          </p>

          <h2>Bonus: Soundcloud</h2>
          <p>
            Soundcloud allows for embedding of most of their tracks. The embedded player
            also has a <a href="https://developers.soundcloud.com/docs/api/html5-widget">pretty cool API</a>.
            I whipped up a version of this script to work with Soundcloud players. I’d say
            it’s about 98% the same. All the little functions I wrote aren’t concerned
            with the whole story. They just want numbers, strings, arrays, etc. Because of
            that I was able to use them with the Soundcloud version. <a href="http://lab.tylergaw.com/media-seek-from-url/soundcloud.html">Have a look</a> at the
            demo. Like the HTML demo, you can add "?t=1m35s" to the URL to jump to that
            point in the track.
          </p>
          <p>
            The only differences are how you interact with the media element
            and that Soundcloud takes milliseconds instead of seconds
            to set the current time. Soundcloud provides events and
            methods that you can use to control the player. They aren’t the same as their HTML
            counterparts, but the ideas are the same. The Soundcloud example code is in
            the <a href="https://github.com/tylergaw/media-seek-from-url">same repo</a> as the HTML
            version.
          </p>
          <p>
            This was a fun problem to solve. Both from the UX view and from the code. I’m
            sure there are quite a few similar implementations, but I’m sure there are
            none just like this one.
          </p>

          <p>
            <i>Thanks for reading</i>
          </p>
          ]]>
      </description>
      <link>https://tylergaw.com/articles/media-seek-from-url</link>
      <guid>https://tylergaw.com/articles/media-seek-from-url</guid>
      <pubDate>Sat, 20 Sep 2014 04:00:00 EST</pubDate>
    </item>
      <item>
          <title>How I Manage NGINX Config</title>
          <dc:creator>Tyler Gaw</dc:creator>
          <description>
              <![CDATA[
              <p class="entry-intro">
                  I host my personal sites–this one, <a href="http://lab.tylergaw.com/">my lab</a>, <a href="http://againwiththis.com/">my quips</a>,
                  <a href="http://listentoslayer.com/">nonsense</a>, and others–on a
                  Rackspace <a href="http://en.wikipedia.org/wiki/Virtual_private_server">VPS</a>.
                  <a href="http://nginx.com">NGINX</a> is my web server of choice. Each site
                  has its own configuration file. I’m definitely not Mr. Sys Admin,
                  but I have an easy way of organizing those config files to help me make quick
                  updates and additions.
              </p>

              <p>
                  I’m assuming some knowledge with this post. Folks who have middle ground knowledge
                  of NGINX will get the most out of this. It’s not a how-to NGINX post, so beginners may not find it useful.
                  It also may not be helpful if you have advanced NGINX knowledge as you likely
                  have a better way to handle these things.
              </p>

              <h2>What I Do</h2>
              <p>
                  I keep my config files in a private Github repo called “server-conf”. In it
                  I have two directories; “local” and “prod”. Local for the NGINX install
                  on my developement machine and prod for my VPS. I have different sites
                  locally than I do production. Each site whether local or prod has its
                  own config file.
              </p>
              <figure>
                  <img src="https://tylergaw.com/articles/assets/post-image-nginx-conf-editor.jpg" alt="The server-conf repo opened in a text editor">
                  <figcaption>
                      server-conf in my current preferred text editor, Atom. Being able to use it instead of something
                      like Vim is much more comfortable for me
                  </figcation>
              </figure>
              <p>
                  Both locally and in production, I clone the server-conf repo to my home directory.
                  To tell NGINX where the config files are, I use <code>include</code>
                  in the <code>http</code> block of the main NGINX config file, <code>nginx.conf</code>.
                  The location of this file is different for each install. Locally, mine is at
                  <code>/usr/local/etc/nginx</code>. I can never remeber where it is
                  on my VPS, I just have to peck around until I find it. Here are
                  the relevant snippets from my local and prod <code>nginx.conf</code>.
              </p>
              <pre><code># local
http {
  ...
  include /path/to/home/server-conf/local/*;
}</code></pre>

              <pre><code># production
http {
  ...
  include /path/to/home/server-conf/prod/*;
}</code></pre>
              <h2>Why the Home Directory?</h2>
              <p>
                  Mainly because I can never remember the default location of NGINX config
                  files. This is especially true on my VPS since I access it infrequently. Having it in the home
                  directory allows me to stumble on it since it’s the first directory
                  I see when I SSH into the VPS.
              </p>
              <h2>How is this helpful?</h2>
              <p>
                  This helps me in a number of ways. First, like I mentioned above, it
                  makes locating the config files much easier.
              </p>
              <h4>Comfort</h4>
              <p>
                  Doing this allows me
                  to use a text editor I’m most comfortable with to make updates to
                  production configs.
              </p>
              <p>
                  When I need to make an addition or change to production config I can make the changes
                  on my machine. I then commit and push the changes to Github. Then SSH into
                  my VPS, and navigate to the <code>server-conf</code> directory. There
                  I <code>git pull</code> and after an <code>nginx -s reload</code> my
                  changes are live.
              </p>
              <p>
                  Before this, to make production changes I would have to SSH into the VPS,
                  navigate to the default NGINX config location
                  and use Vim to make changes. This was a slow and error-prone
                  process for me because I’m not comfortable with Vim and I get a bit
                  lost sometimes navigating with the command line. Also, my server
                  isn’t the fastest so editing there is much slower than it is on my
                  laptop.
              </p>
              <h4>Git</h4>
              <p>
                  The ability to quickly roll back breaking changes is huge.
                  If I pull down changes that cause the prod server to stop working, I
                  can use git to check out the previous version of the offending file(s) while I fix the problem.
                  If I’m making larger changes I can create a different branch to make it even
                  easier.
              </p>
              <p>
                  Config backup via Github. If I get a new computer or if mine is damaged,
                  I won’t have to recreate my configs for local development. I can just clone the repo and
                  make the quick update the <code>nginx.conf</code> to get back up and running.
              </p>
              <p>
                  Another cool thing is having a config revision history. Since everything
                  is in git and on Github, I can see when and what changes I’ve made.
                  This isn’t that helpful for me since I’m the only one making changes
                  and they tend to be small, but it could be a huge plus if you have
                  multiple people making updates.
              </p>
              <figure>
                  <img src="https://tylergaw.com/articles/assets/post-image-nginx-conf-log.jpg" alt="Github commit log for the server-conf repo">
                  <figcaption>
                      The server-conf git log provides a history of config changes
                  </figcation>
              </figure>
              <p>
                  NGINX is relativily easy to configure, but I used to dread making
                  changes in production because I’m not as handy with the command-line
                  and Vim as I am a graphical text editor. Having this setup in place
                  makes it as easy as updating any other files associated with my sites.
              </p>

              <p>
                  <i>Thanks for reading</i>
              </p>
              ]]>
          </description>
          <link>https://tylergaw.com/articles/how-i-manage-nginx-config</link>
          <guid>https://tylergaw.com/articles/how-i-manage-nginx-config</guid>
          <pubDate>Thu, 24 Jul 2014 14:30:00 EST</pubDate>
      </item>
      <item>
          <title>A Logo Design Process</title>
          <dc:creator>Tyler Gaw</dc:creator>
          <description>
              <![CDATA[
                  <p class="entry-intro">
                      As part of my site <a href="https://tylergaw.com/articles/how-i-start">redesign</a>
                      I wanted new branding. It's not really a logo I guess, but
                      it's what I'll be using on this site and any other promotional crud that I make
                      like business cards. This was a fun process that let me dive deeper
                      into hand-lettering than I have before. Here's a look into the design of the
                      new logo from pencil sketch to final (enough) image.
                  </p>
                  <p>
                      I'm not trying throw out everything and
                      start from zero. Because of that, my first step was look at what I have now. My current
                      logo appears in three variations. The largest appears
                      on my homepage. On medium size screens a smaller, stacked
                      version with "web" on top and "designer" below my name is shown. On small screens,
                      a side-by-side version with my name on the left and "web designer" on the right is shown.
                  </p>
                  <figure>
                      <img src="https://tylergaw.com/articles/assets/post-image-logo-process-existing.jpg" alt="">
                      <figcaption>
                          Variations of my current logo displayed at different screen sizes
                      </figcation>
                  </figure>

                  <p>
                      I always start as rough as possible. I grabbed the "Tyler Gaw" portion
                      of the existing logo and plopped it into a new PSD. I didn't have a
                      plan for what I'd do with the logo so just started
                      typing out different titles next to it in different typefaces to get
                      things rolling. I tried side-by-side, top and bottom, serifs, sans-serifs, handwriting,
                      pulling, prodding, stretching, scaling, etc. I let myself play and make
                      as many ugly messes as I could.
                  </p>
                  <p>
                      After a good amount of messin' I landed on something that definitely
                      wasn't ready, but felt like a good path. My name with "Design & Development" and "Brooklyn NY, USA"
                      below it. I added a slight angle to both sides to make it a more
                      interesting.
                  </p>

                  <p>
                      The image below shows that everything was out of whack, it's
                      not even fully readable. That was OK. It was enough that I could see
                      where I wanted to go with it.
                  </p>

                  <figure>
                      <img src="https://tylergaw.com/articles/assets/post-image-logo-process-roughage.jpg" alt="">
                      <figcaption>
                          The results of messing with the existing logo. This was bad on
                          purpose. It got me started down the right path.
                      </figcation>
                  </figure>

                  <p>
                      In the existing logo I refer to myself as a "Web Designer". That feels like
                      an accurate title because I design and
                      write code, but I wanted to be more clear so I went with "Design & Development".
                      Using "Web Designer"
                      as a title I often have to answer; "no, not only design. I write code too".
                  </p>

                  <h2>Back to Sketching</h2>
                  <p>
                      The next step was to get off the computer and do some sketching. I gave
                      myself some loose guides to follow for the width/height and for the
                      shape the text would be in. I redrew the same thing five or six times
                      until I got one that was close enough.
                  </p>
                  <figure>
                      <img src="https://tylergaw.com/articles/assets/post-image-logo-process-sketches.jpg" alt="">
                      <figcaption>
                          Trying to get a solid sketch to start
                      </figcation>
                  </figure>

                  <figure>
                      <img src="https://tylergaw.com/articles/assets/post-image-logo-process-sketch.jpg" alt="">
                      <figcaption>
                          The close enough pencil &amp; Ink sketch that was used as a starting point in Illustrator
                      </figcation>
                  </figure>

                  <h2>Moving Into Illustrator</h2>
                  <p>
                      I brought the close enough sketch into Illustrator and live traced it.
                      The conversion to vector was as good as could be expected. With any
                      hand-drawn text there are a lot of unneeded and rough points.
                  </p>
                  <p>
                      I did a pass where I cleaned up the spacing and removed many points to
                      make the letters of "Design &amp; Development" smoother. In the image
                      below you can see some sharp edges in places on the "Tyler Gaw". Those
                      were caused by me changing the height and rotation of different letters
                      and portions of letters.
                  </p>

                  <p>
                      When I thought I had the basic size and shape where I wanted it I
                      brought it over to Photoshop and placed it in a very early layout of
                      the new site.
                  </p>

                  <figure>
                      <img src="https://tylergaw.com/articles/assets/post-image-logo-process-first-thought.jpg" alt="">
                      <figcaption>
                          This is what I originally imagined the logo would resemble. Very
                          hand-drawn, close to my existing logo. There would still have been a lot of
                          clean-up required for this to work.
                      </figcation>
                  </figure>
                  <p>
                      This felt wrong. It was too spindly. I didn't feel like it was standing up on its own.
                      Felt like it could get lost easily at smaller sizes. I wanted
                      it to feel more involved, more interesting, just more.
                  </p>

                  <p>
                      "What if I add some serifs?" I asked myself. I started with the "D"
                      in "Design". I widened the stroke and added slight serifs to it.
                      That's what it needed. With that small change I could tell immediately that this was where
                      it needed to go. That also let me know that I was in for a whole lot
                      of tedious work as I would have to take the same steps for each letter.
                  </p>

                  <h2>The Work</h2>

                  <p>
                      It took me about about fours days to update all the letters. I
                      always started by removing as many points as possible. A general rule
                      I figured out was mo' points, mo' problems. Fewer points allow for
                      smoother curves and fewer pinches and nicks where they aren't wanted.
                      After deleting the points it was a matter of moving them around until
                      the stroke width and serifs matched the rest of the letters. I used
                      some loose guides to keep the serif sizes similar, but since this is
                      supposed to have a hand-drawn look I could take liberties with them.
                  </p>

                  <p>
                      The removing, adding, and moving of points feels a lot like working with clay. You start with these
                      rough shapes and you slowly push and pull and smooth it into the shape
                      that you're looking for. I've written about this process before
                      for <a href="https://tylergaw.com/articles/slenderman">Slenderman</a>.
                      For some reason it felt more like chiseling rock that time.
                  </p>

                  <p>
                      As a way to check that things were shaping up correctly and as a break
                      from tedium, I would often bring the logo into the rough layout in Photoshop.
                      Looking at it as a raster image at the approximate size it would be
                      displayed helped me make sure it was readable and looked how I wanted it.
                      I worked on the letters in bursts. I would do a letter, maybe two. Then
                      stop, get some air. Move it to Photoshop, check it, repeat.
                  </p>
                  <figure>
                      <img src="https://tylergaw.com/articles/assets/post-image-logo-process-ani.gif" alt="">
                      <figcaption>
                          Animation showing the rough steps of the process
                      </figcation>
                  </figure>

                  <p>
                      I'm happy with how it's turned out. I'm also working on colors
                      for the redesign so I added bits of color to the logo and put it up on
                      <a href="https://dribbble.com/shots/1644137-GAW-Mark-WIP">Dribbble</a> for feedback.
                      From the beginning I've had my brain set on a red for some reason, but
                      all of the feedback points to yellow. I've been playing with yellow
                      as an accent color for the the rest of the site and it feels like it's working so far.
                  </p>

                  <p>
                      Here's a final (as-of-right-now) version of the logo embedded as SVG.
                      I removed the yellow drop shadow from my name that is shown in the Dribbble shot.
                      It was kind of cool, but didn't feel necessary. I think I can get more
                      mileage out of the logo without it.
                  </p>
                  <figure>
                      <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
               width="3267.3px" height="440px" viewBox="0 0 3267.3 2116.3" style="width:100%; height:auto; min-height:200px;background-color:#1e1e1d;"
               xml:space="preserve" title="Tyler Gaw Design & Development - Brooklyn, NY U.S.A.">
                      <g id="Logo">
                          <path id="gaw-w" style="fill-rule:evenodd;clip-rule:evenodd;fill:#F9F9F9;" d="M2783.4,977c19.8,1,52-275.5,54.8-279.7
                              c3.2-4.7-29.4-47.2-22.1-47.5c1.6-0.1,96.1-21.8,124.4,0c8.3,6.4-19.9,14-15.8,33.3c9,41.6,87.5,202.7,94.3,204.2
                              c12.2,0-9.5-125.3-9.2-143c0.5-28.8-2.9-132.7-4-146.9c-1.5-19.3-6.5-110.3-6.5-127.6c0-79.5,2.8-178,6-199
                              c1.6-10.6-4-46.6-2.8-65.3c3.1-45.3-37.7-51.7-36.2-84.7c-0.8-8.4,152.9,15.5,151.1,18.2c-13.6,38.8-41.8,33.8-44.4,53.2
                              c-1.8,13.7-12.7,79.5-20.8,157.7c-0.4,0.4-3.7,78.6-2.4,103.1c1,19.4,10.8,100.4,10.6,106.2c-1.4,40,0.1,30.4,0.5,72
                              c0.2,19.8,15.4,82,11.2,111.1c-3.3,22.6,6.7,168.1,6,175c-2.1,20.1,41.9,30.9,39.5,55c-0.6,5.9-136.4,50.2-142.1,38.9
                              c-4.5-9,19.7-36.7,1.5-75c-10.4-21.7-28.6-52.9-45.2-89.6c-27.7-61.2-36.6-128.9-41.1-128.9c-5.7,0-4.7,67.5-15.5,142.2
                              c-11.9,82.7-35.1,174.1-28.5,192.3c-2.1,9.5-121.1,56.8-125.8,49.6c-3.2-4.9,11.9-62.1,9.7-74.8c-2.4-13.6-23.4-137.5-25.9-183.1
                              c-0.7-12.3-16.3-114.5-19.6-136.7c-3.2-21.2-15.4-94.7-17.4-102.9c-5.3-21.4-15.1-59.9-23.5-83.5c-5.9-16.4-8.4-33.8-13.4-50.5
                              c-5.3-18.1-28.4-138.5-32.8-154c-3.9-13.9-39.8-126.7-45.5-155.2c-1.9-9.4-41-58.8-44-68c-2.2-6.7,153.2,4.2,156,6
                              c17-4.9-30.3,61.4-29.7,65.5c1.8,11.2,34.8,135.9,37.7,144.7c8.1,24.5,27.5,129.3,30.4,145.8c2.7,15.3,48.6,274.5,47.7,289.9
                              c-0.7,12.1,6.1,92.2,4.7,115C2754.1,878.5,2763.9,976.5,2783.4,977z"/>
                          <path id="gaw-a" style="fill-rule:evenodd;clip-rule:evenodd;fill:#F9F9F9;" d="M2446.7,797.9c-17.4,2.8-66.3,24.6-74.6,26.4
                              c-15.5,3.4-88.7,13.5-93.8,17c-5.8,7.7,26.6,280.8,27,304c2,27,19.7,15.7,24.3,47.1c1.8,12.2-159.5,66.3-158.3,57.9
                              c2.4-15.8,18.3-34.1,21-49.8c2.7-15.8,21.6-110.2,22.2-126.1c0.2-5.5,0.1-11.1,1.1-16.5c5.1-27.7-8.3-171.9-5.7-199.8
                              c1.5-15.9,1.3-177.4,0.2-200.2c-1-19.8,2.8-291.5-1.3-310.9c-2.6-12.2-13.6-195.6-14.5-205.7c-1.1-12.5-26.4-44.2-26-57
                              c0-9.4,167,0.6,173,8c2.9,9.1-19.8,23.3-19,39c1.9,36.2,20.1,94.9,38,126.2c4.2,7.3,65.9,180.7,72.9,203.6
                              c3.3,10.9,58.6,132.3,62.7,157.7c3.1,19,21.2,109.6,21.4,128.8c0.2,18.2,41.2,314.4,40.3,332.7c-1.1,21.2,19,18,25.7,49
                              c1.2,5.8-133.2,45-135.9,40.3c-2.2-18.6,20.2-18.3,19.2-55.9C2464.6,1035.5,2450.9,797.2,2446.7,797.9z M2283.4,767.7
                              c1.9,1.2,66.9-1.9,79.3-6.7c10.3-3.9,72.2-22.5,71.9-31.5c-10.2-77.1-45.4-196.1-53.4-224.1c-2.7-9.3-69.2-225-71.8-232.2
                              c-3.5-9.5-46.9-117.3-52-117.8c-7-0.6-3.4,173.7-2,181c3.1,16.4,5.7,169.2,9.5,180.8C2270.7,535.4,2281.9,763.1,2283.4,767.7z"/>
                          <path id="gaw-g" style="fill-rule:evenodd;clip-rule:evenodd;fill:#F9F9F9;" d="M2005.4,258.9c-26.7-12.8-33.9-21.7-44.3-56.2
                              c-39.5-131.4-157.3-82.4-171-71.1c-5.9,4.1,24.3,560.6,23.9,586.6c-0.4,26.7-15.1,252.2-16.9,267.8c-0.2,1.9,1.4,55.8-1.6,80.6
                              c-2.6,21.5-40.2,246.2,24.8,237.6c50-6.6,114-76,133-121c9.5-17.6,27.9-395.9,23.7-395c-24,5.4-44.5,12.2-65.7,15.7
                              c-9.9,11.5-18.8,15.1-35.7,14.7c-4.8-0.3-2.2-95,1.3-98.2c5-8.7,44.7-6.6,72.7-7.5c21.1-1.6,77.5-10.4,78.1-8.9
                              c2.4,6.2,9.9,220.2,8,239.7c-1.4,14.3-2.7,348.2-15.4,350.5c-39.3,4.8-48.2-39.4-48.2-39.4c-5.8,18.2-19.8,70.3-62.1,82
                              c-16,0.3-125,54.7-159.8,48.6c-35-6.1-33.9-244-32.1-278c0.7-1.9,25.1-241.6,25.1-250c-0.1-5.6,3.2-206,3.1-231
                              c0-15.9-16.8-235.1-21.6-314c-3.4-19.1,3.2-170.9,10.1-181c14.8-30.9,15.5-67.7,139.2-67.7c84.9,0,78.3,38.2,102.6,41.5
                              c26.4,3.6,26.4-18.1,37-1.7C2014.7,120.5,2014.6,234.7,2005.4,258.9z"/>
                          <path id="tyler-r" style="fill-rule:evenodd;clip-rule:evenodd;fill:#F9F9F9;" d="M1488.2,432.6c-0.1,11.3-7.8,101.4-7.7,117.9
                              c0-21.1,2.1,242.4-18.5,333.9c-5.3,17.7-68.6,34.2-83.6,36.2c-0.3,0,74.8,23.7,69.8,79.7c-2.8,30.9-9.9,186-10.8,217
                              c-0.4,15.1-24.8,170.5,73.5,112.5c10.9-6.5,22.5-20.2,30.6-25.1c13.3-8,30.9,78.8-56.4,100.8c-4.7,0.1-106.3-36.7-113-40.9
                              c-13.9-29.4,1.3-79-1.3-104.3s9.2-222.5,9-229.7c-0.6-23.3-5.7-72.3-36.3-64.3c0,0-3.9,66.8-7.3,89.2
                              c-3.5,23.1-14.7,128.1-18,142.2c-3.1,13.2-6.6,83.7-4.1,95.8c0.8,4,14.5,53.9,10.2,55.2c-4.8,1.5-106.3-41.8-108.6-45.9
                              c-1.7-3.2,20.5-23.7,21.1-27.5c4.6-29.7,12.5-58.8,19.7-88c3.7-14.8,3.1-30.2,4.7-45.3c2.5-22.8,8.5-96.5,11.6-119.2
                              c2.4-17.5,6.1-41.9,7.4-59.6c0.3-4.8-0.4-9.7,0.1-14.5c6.6-64.2,4-192,4-177.4c0-20.8,1.4-41.3,2.6-61.9
                              c0.9-14.5,4.5-68.3,4.1-88.9c-0.4-21.4,4.6-220.4,4.3-225.6c-1.1-20.2-8.4-83.1-8.3-94.5c0.1-11-21.6-195.2-20-209.2
                              c0.3-2.9-32.7-17.4-31.2-28.4c5.2-4.1,68.9-6,74-6.1C1532,44.8,1480.6,248,1480.4,279.3C1480.4,296.4,1488.3,417,1488.2,432.6z
                               M1348.5,854.7c0.4,5.6-3,4.7,33.9-0.4c21.1-2.9,36.1-104.4,38.1-122.4c2.4-20.9,9.6-148.1,9.7-150c0.3-10.9-0.6-148.6,0-161.5
                              c0.5-11.3-4.5-60.4-4-72c1.3-28,1.1-167.5-5.8-188.1c-6.7-20.1-3.3-34.9-21-48c-29.4-21.8-25.5-25.7-63-22
                              c0,0,10.5,96.4,10.5,114.5c0,17.7,8,233,8,260.5c0,29-4.3,139-4,143c1.2,19.3-0.9,78.2-2.4,88.2
                              C1344.5,721.8,1348.8,832.7,1348.5,854.7z"/>
                          <path id="tyler-e" style="fill-rule:evenodd;clip-rule:evenodd;fill:#F9F9F9;" d="M1022.7,887c10.8-55,1.6-97.9,3.7-117.4
                              c0.8-8.1-9.2-196.6-10.5-214.1c-1.6-20.7-22.1-261.9-22.2-293.8c0-13.6-10.8-144.4-9.4-156.7c-1-20.6-41.9-15.3-42.2-35.7
                              c0-4.1,76.4-4.4,84.7-4.3c10.6,0.1,45.8-1.2,52.6,0.4c4.2,1,73.9-2.3,85.8-2.2c5.4,0,11.8,63.4-4.8,62.4c-16-1.3,28-32-109.3-32
                              c0.1,6,21.3,212.8,26.7,265.3c2.5,52.5,11.3,212.2,12.7,236.9c0.5,10.2,6.7,160.8,4,178.4c-2.2,13.9-2.9,75.6-4.6,88.3
                              c-1,7.9,53.3-3.2,74.6,1.7c0.6,0.1,1.9,76.8-5,74.3c-17.8-6.3-80.4-14.9-81.9-11c-2.7,7.5-2.7,100-9.4,138
                              c-5.1,24.9-11.8,93.4-15.7,118.5c1.1,4.2,69.4,21.7,81,26.5c4,1.7,27.1-1.9,31.7-2.7c3.8-0.7-2.4,95.9-6.7,94.8
                              c-4.2-1-112-41.6-122.5-45.5c-10.1-3.7-91.4-29-91.8-29c-9-11,33.3-56.3,33.3-56.3c4.8-9.3,10.6-46,12.9-67.7
                              C990.7,1100.5,1019.7,913.1,1022.7,887z"/>
                          <path id="tyler-l" style="fill-rule:evenodd;clip-rule:evenodd;fill:#F9F9F9;" d="M891.5,1107.5c-0.6,15.6,1,118.3-5.5,115.8
                              c-8.7-3.3-116.2-44.4-137.3-48.9c-3.2-0.7-98-28.7-101.3-29.1c-0.4-25.7,51.8-47.8,52.6-54.6c3.4-23,29.1-94.3,29.6-97.7
                              c3.4-22.4,19.3-61.8,20.8-84.3c1.6-22.8,6.3-60.8,8.3-83.7c1.5-16.7,27.3-180.5,28.7-195.7c0.9-9.5-2.9-28.5-1.3-38
                              c4-23.9-7.1-253.7-7.3-272.3c-0.3-20.7-0.8-38.1-3-58.7c-1.5-14.1,10.9-143.1,8.3-157c-1.6-8.6-33.7-14.7-33.7-28.4
                              c0,0,60.1-0.3,77.1-2.8s55.3,2.8,55.3,2.8c-0.7,19.1-36.2,19.8-33.8,37.1c2.1,12.1-1.1,141-0.2,155c1.7,25.7,3.1,62.1,3.3,77.3
                              c0.4,23.5,4.7,93.1,6.7,111c1.8,15.9,1.2,125.8,0,138c-1.4,14-9.9,124.2-12,157.7c-1.7,27.6-6.2,54.7-11.2,82
                              c-2.4,13.3-8.4,57.9-9.8,80.7c-1.4,22.1-15.6,73.4-17,81.3c-2.4,13.7-11.5,36.8-13,50.7c-4.1,38-10.1,61.7-10.1,61.7
                              c32.7,13.5,36.3,19.4,53.3,24.7C859.2,1138.4,874.2,1104.5,891.5,1107.5z"/>
                          <path id="tyler-y" style="fill-rule:evenodd;clip-rule:evenodd;fill:#F9F9F9;" d="M711.5,85.9c-3.3,22.8-22.1,14-29.5,36.5
                              c-4.2,12.7-17.1,70.2-21,83c-3.2,10.8-31.8,102.6-35,108c-3.5,5.9-50.2,223.8-52.5,228.9c-4.1,9.1-7.5,47.5-8.8,54.3
                              c-5.2,26.6-2.2,60.1-1.3,93.1c-3.7,24.5-9.2,160.9-10.4,182.9c-0.7,14.5-7.7,56.3-10.3,70.7c-0.9,4.8-8,115.5-6.8,131.8
                              c0.5,7.1,15.8,27.9,12.5,49.2c0,0-158.9-55.2-159.7-61.3c4-10.7,27.3-10.7,36.7-21.5c5.9-10.9,27.9-44.5,29-63.5
                              c0.7-11.4,15.3-61,20-78c1.3-4.8,16.3-97.8,17-123c0.6-20,0.5-84.3,4-113.9c2.1-44.7-6.3-48.6-7-75.9c-1.9-31.2,2-87-4-106.7
                              c-5.1-16.5-35.8-223.5-40.4-245.1c-3.6-17.3-13.7-100-22.3-118.4c-4.4-9.3-32.6-15-37.7-24c-2.4-4.3,94.1-8.3,95.5-6.2
                              c1.8,4.6-26.1,18.4-22,34.7c12.4,49,23.4,136.1,27,151.5c4.9,20.8,38.5,222.8,42.5,222.9c3.9,0.1,34.7-184.7,39.5-195.9
                              c9.4-22.1,36.9-164.3,42.4-181.5c4.5-14.3-21.9-29-23.4-35.5c-0.4-2,30.7,3.9,61.8,0.5C660,82,711.4,83.9,711.5,85.9z"/>
                          <path id="tyler-t" style="fill-rule:evenodd;clip-rule:evenodd;fill:#F9F9F9;" d="M137.7,929.3c8.4-14,28.7-258.7,31.3-277.9
                              c2-14.3,8.4-296.9,7.7-308.6s-3-185.9,0.7-205.2c0,0-42.4,2.7-66,6.9c-9.1,0.2-41.9,32.7-50.4,35.8c-2.6,1,6.8-62,9-63.7
                              c11.7-3.6,21.2-1.8,33.9-1.2c8.8,0.4,84.5-4.5,84.5-4.5s95.8-11.7,119.3-10.8c9.7,0.4,32.9-1.3,35-1.2c6.3,0.4,16.3,69.9,10,69.4
                              c-5.7-0.4-38.2-34.7-46-35.3c-16.8-1.5-56.1,1.7-56.1,1.7s6.2,223.5,8.1,245.7c-0.5,15.5-6.7,98.5-7.9,110.6
                              c-1.8,17.9,1.1,95.3-0.7,108.8c-2.7,20.9-11.2,347.1-11.3,356.5c-0.2,10,24.7,56.6,16.7,72c0,0-155-53.8-153.9-67
                              C105.2,951,135.3,935.7,137.7,929.3z"/>
                          <path id="dev-t" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M3188.7,1119.3c0,0,2.3,33.9-1.9,32.8
                              c-3.8,1.2-6.5-6.5-33.3,1.8c-2.2,0.7-1.7,149.2-5,168.2c-6.1,35.3-4,64.6-7.2,105.9c-2,26.2,3.4,52.1,1.1,78.2
                              c-1.7,19.3,0.9,38.5,2.3,57.8c1.6,21.2,3.6,42.4,5.4,63.6c2.5,28.8,8.1,171.8,10.7,187.7c1,4.5,16.6,14.5,15.4,21.9
                              c-1,1.7-63.1,2.5-62.8,0.6c2.3-12.3,13.6-19.5,13.9-24c1.1-13.8-1.2-124.6-1.7-153.8c-0.2-15-11.2-104.7-10.1-135
                              c0.9-23.8-4.3-47.7-0.5-71.3c3-18.4,3.5-87.9,5.9-104.2c2.1-14.5,2.8-28.6,4-43.3c1-12.9,4.7-148.5,2.7-148
                              c-36.3,9.2-27.3,17.7-32,19.8c-0.9,0.8-8.9-27.4-7.9-28.5c9.2-5.8,17.5-9,27.2-11.1c17.8-3.8,37.1-11.3,55.8-16.4
                              C3175.4,1121.5,3181.7,1118.9,3188.7,1119.3z"/>
                          <path id="dev-n" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M3040.2,1764.4c3.4-2.8-2.9-89.2-2.7-107.3
                              c0.2-19.2-4.3-38.1-4.6-57.2c-0.2-14.2-6.9-162.2-5-179c3.4-30.2-2-219.6-0.9-235.3c-4,0-15.1-4.8-19-10.3
                              c-1.2-4,52.4-17.3,52.2-15.7c3.1,12-9.8,11.9-9.2,23.5c0.8,8.2,3.9,123,1.8,143c-1.5,13.9,2.1,57.9,0.1,71.8
                              c-2.6,18.1,1.3,158.1,1.2,177.1c-0.2,27.7,7.3,219.9,8.4,238.2c0.1,2.7,15.9,17,14.3,23.9c-0.3,1.2-60.3,1.9-60.7-0.2
                              c-1.5-8.9,9.8-18.1,9.4-24.1c-1-14.6-11-76.9-14.5-89.6c-4.9-17.3-17.5-88-18-103.1c-0.6-18.7-14.1-164.6-15.4-190
                              c-0.9-17.7-17.8-178.4-24-176.2c-9.5,1.5,4.7,209.8,5.2,218.7c1.3,23.8-0.8,159.6-0.6,171.2c0.5,20.1,0.5,145.1-0.9,167.9
                              c0.1,5.2,14.1,15.5,16.5,25.2c0.8,3.3-56.9,3-56.8,0.4c-1.7-13.7,10.3-12.4,10.5-24.5c1.7-81.4,7.9-360.4,5.6-396.4
                              c-0.6-23.9-5.2-193.3-6.7-207.2c-0.8-4.4-12-8.2-15.1-15.1c-0.1-2.7,42.8-11.7,44.4-9.2c7.9,16.2,26.3,58.5,29.1,77.2
                              c5.5,37,21.6,243.3,25.5,266S3037.4,1762.4,3040.2,1764.4z"/>
                          <path id="dev-e-3" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M2800.8,1629.3c1.5-17.2-0.2-38.2,1.5-55.3
                              c5.3-53.1,5.7-217.8,4.3-229c-0.5-3.6-5.7-90.3-6.2-94.7c-0.9-1.7-14.9,1.6-14.9,1.6c-1.4-12.2-3.5-20.7-3.5-20.7
                              c26.5-11.5,79.9-26.6,90.7-29.9c4.9-1.5,8.5,36.7,7.6,39c-1,1.6-5.3-8-15.5-7.8c-15.8,0.3-31.5,7.3-38.7,10.8
                              c-1.9,1,0,53.8,2.7,65.8c3.6,16.3,3.8,72.1,3.5,92.7c-0.4,22.5,1.4,60.9,0.2,68.6c-2.8,18.4-2.7,30.9-3.2,49.5
                              c-0.4,15.2-1.8,31,0,31.2c23.9,1,33.9-6.4,41.6-5.8c0.8,0.8,2.5,37.2-2.1,36.7c-6.2-0.5-21-7.2-42.2-4.5
                              c-1.8-0.3-4.4,236.6-0.1,236.4c6.4-0.2,29,1.3,35.2,1.2c10.6,0,8.4-11.2,18.8-10c4.1,0.5,0.4,36.6-1,36.7
                              c-10.5-1.1-81.9-1.7-87.2-2c-0.4,0-4.6-10.4-1.6-25.1c0.5-2.3,10.2-1.6,10.2-1.6C2801.9,1760,2800.2,1636.5,2800.8,1629.3z"/>
                          <path id="dev-m" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M2659.8,1551.3c-0.1-7.7,6.7-11.2,6.6-16
                              c-0.3-18.9-25.2-164-27.5-163.5s-0.3,96.4-1.4,107.3c-1.2,11.6,0.5,23.4,0.3,35.1c-0.1,7.2,0,100.8-3.4,122.1
                              c-1.6,10.1-1.6,138.8-3.2,181.6c-0.3,6.9,11.2,9.3,9.9,19.7c-0.6,1.8-49.5,2.9-49.3,0.2c-0.1-8.6,7.7-11.6,8.7-16.6
                              c2.9-18.8,5.1-138.8,7.6-157.6c2.9-21.2,1.6-120.6,2.6-137.1c0.8-14.1,0-46.7-0.1-55.1c-0.1-35.5,7-99,4.4-134.5
                              c-0.9-12.7-2-26.6-4.1-41.6c-0.3-2-16-5.5-15-11.5c0.5-2.5,56-21.2,57-17.5c0.7,2.6-6.1,17.5-5.5,22c0.2,1.7-4.6,13.5,2.2,55.2
                              c0.6,7.3,32.1,157.9,32.1,157.9c4-4,13.9-130.3,18.6-179.4c4.1-42.5,5.4-42.8,6-55.8c-0.6-5.4-12.1-6.3-12-12.9
                              c1.1-3.5,47.3-18,47.5-15.1c1.6,7.4-7.6,14.4-7.5,21c-0.1,6.1,8.3,106.7,9.6,124.8c1.7,24.5,5.9,72,3.2,96.4
                              c-2.8,26-0.2,43.1-2.9,69.1c-1,9.9,2.8,52.9,1.1,62.8c-3.8,23.6-0.2,181.1-1.6,205c-0.2,6.3,9.5,9,8.9,21.1
                              c0.2,2.5-49.6,2.5-51.2,0c-1.7-7.2,13.8-12.4,13.8-21.2c0.2-36.6-1.2-164.8,4-201c4.4-30.4,0-84,3.3-114.5
                              c2.5-23,1.7-136.4-0.9-159.5c-0.5-4.5-9.2,83.3-10.5,93.1c-1.3,17.2-10.5,65.1-16.5,94.9c-0.8,4.5,6.3,6.3,6.4,13.5
                              C2701.3,1547.1,2660.9,1555.3,2659.8,1551.3z"/>
                          <path id="dev-p" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M2577.5,1532.5c0.2,11.4-0.3,32.8-1.6,44.2
                              c-3.7,31.6-5.6,40.2-54,40.9c-0.8,0.5-2.8,78.7-2.7,94c0.1,12.6,1.4,25.2,0.2,37.7c-1.4,14.2,2,54.6-0.2,68.6
                              c-1.7,10.7,12.4,11.8,11.1,21.1c-0.3,2.2-54.9,4.2-55.2,1c-0.6-8,12.9-12.3,13.8-19.9c1.6-14.2,3.2-39.4,5.4-53.5
                              c4.4-27.4,2.8-55.1,4.3-82.5c0.2-3.8-0.9-44.7-0.3-48.5c1-6.6-2.2-59.7-1.1-77c1.4-23.5-0.2-46.8-0.7-70.1
                              c-0.5-21.7-5.2-40.3-7.3-61.8c-2-20.6-6.3-67-11.4-95.1c0,0-11.1,4.8-11.9,1.4c-0.4-1.9-11.3-14-10.6-14.7
                              c11.4-5.4,59.6-17.9,72.5-19.7c9.5-1.3,15.8,2.5,22,10c6.8,8.3,9.9,21.7,13,31.7c3.7,11.9,4.3,45.4,5.4,53.3
                              c2.4,18.4,3.6,33.5,4.6,52C2573.6,1456.7,2576.5,1502.1,2577.5,1532.5z M2521.5,1552.3c0,12.2,0.3,37.6,1.3,37.6
                              c2.8-0.2,11.5,0.8,17.5,0.2c20.8-2.6,18.8-6.8,19.2-25.7c0.5-22.7-1.4-55.5-1.3-75.7c0.1-16.7-9-99.2-11.1-117.5
                              c-1.8-15.3,1.6-26.9-6.9-40c-4.1-6.4-12.2-9.6-17.9-8.5s-19.6,5.1-21.5,5.6c-1.6,0.7,8.6,67.8,9.2,74.7
                              c2.6,28.1,9.3,53.1,10.2,81.4C2520.3,1490.1,2521.6,1535.3,2521.5,1552.3z"/>
                          <path id="dev-o" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M2444.7,1488.8c1.2,29.9,2.7,99.7,3.2,131.6
                              c0.5,31.6-2.3,67.1-4.7,98.5c-2.5,32.5-3.5,64.5-14.7,95c-4,10.7-4.7,32-39.9,32c-14.6-1.7-31.5-12-32.7-24.9
                              c-0.7-7.5-2.7-14.7-2.8-22.5c-0.2-11.1-0.5-22.3-0.2-33.3c0.7-24.9,2.3-49.9,1.2-74.8c-1-21.6-1-47.1-1.3-68.7
                              c-0.2-14-3.4-65.8-3.6-79.6c-0.3-20.7-4-40.9-5.1-61.4c-0.9-16.5-2.9-33-3.6-49.6c-1.3-29.3-4.3-54.5,8.8-80.8
                              c3.6-7.2,12.2-12.5,18.8-16.7c26.1-9.3,44.9-7.7,49.6-0.7c6.1,9.1,9.9,34.4,11.9,41.6c2.8,10,6,20.7,7.5,31.1
                              C2440.8,1432.5,2444.1,1459.5,2444.7,1488.8z M2382.6,1817.3c1.5,5.4,9,6,12.6,1.8c2.5-3,7.8-12.1,8.8-15.8c5.1-14.6,9-53.7,11-71
                              c1.7-14.8,1.3-29.9,2.7-44.6c1.9-19.2,3.2-42.4,4.2-61.6c1.1-21.5-2.1-81.1-1.8-102.6c0.2-12.3,0.8-24.7-0.3-36.8
                              c-2-21.8-5.5-43.4-7-65.2c-0.4-5.2-5.7-44.5-10.3-63.2c-2.7-13.4-16.5-11.7-20.3-11.9c-7.9-0.9-12.1,33-14.1,49.3
                              c-2.1,16.7-0.7,65,0.7,72c3,16,3.5,32.4,5,48.5c2.3,25.8,5.5,89.6,5.5,115.7c0,19.3,1.6,42.5,1.4,61.8c-0.2,18.3-1.6,36.4-2.1,54.6
                              C2378.3,1761.3,2380,1807.8,2382.6,1817.3z"/>
                          <path id="dev-l" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M2319.2,1841.6c-18.2-1.7-75.8,1.1-92.6-0.7
                              c-1.8-1-1.1-12.5,0-23.7c0.3-3.5,15.9-1.5,16-3.6c0.8-12.5,6.9-83.2,7.9-95.8c1.4-16.9,3.2-33.7,6-50.4c3-17.8,3.1-35.6,4.4-53.5
                              c0.9-11.6,1.1-72.7,1.4-91.6c0.4-22.7,2.3-33.9-1.3-56.2c-1.5-9.2-8.4-66.8-7.2-76c0.5-3.8-16.9-8.3-16.9-18.8
                              c0-1.6,53.3-13.6,53.2-12.9c2.6,17.2-8.3,21.8-7.5,26.8c1.7,11.1,4,87.9,3.7,97c-0.3,10.6,2.2,57,1.1,68.5
                              c-2.2,21.8-2.2,55-3.1,76.8c-0.9,21.2-7.1,84.4-8,89.2c-2.8,14.7-2.1,36.1-4.1,50.9c-2.1,15.4-3.9,46.9-2.9,47.1
                              c7.6,1.7,24.5,0.5,32.1,0.1c13.9,0.4,12.9-10.6,18.7-8.9C2324.3,1807.2,2321.5,1842.2,2319.2,1841.6z"/>
                          <path id="dev-e-2" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M2149.6,1691.9c-1,0.1-3.8,47.8-7.4,64.1
                              c-2.9,13.3-5.2,59.7-1.9,59.5c12.2,0.5,14.3,1.8,25.4,1.8c9.7,0,7-13.3,15.5-13.9c2.4,0.8,1.9,37.4-1,37.2
                              c-4.6,0.4-23.2-0.9-27.8-0.6c-12.2,0.7-41.8,0.9-55.4,0.5c0,0-1.1-12.1,1.3-22.8c1.1-5.1,11.9-1.4,12.2-2.9
                              c5.8-39.5,14.6-154.9,17.5-176.4c3.1-22.7,8.1-68.3,7.6-91.8c-0.1-7.3,1.9-111.3,2.2-118.6c-0.1-4.1-15.9,9.7-15.3-2.7
                              c0.3-5.4-4.7-22.8-3.5-24.3c4.8-0.8,50.6-12.6,63.2-14.7c7.6-1,22.9-10.2,25.2-10.2c4.5,0.1,7.4,44.1,5.8,47.3
                              c-0.6,1.2-19-13.2-19-13.2c-11.5,3.3-19.5,5.8-29,8.9c-2.9,1.5-3.2,47.2-3,53.4c0.9,30.2-0.4,58.2-0.5,88.4c0,21.9-2.4,33.7-4,55.5
                              c-1.7,24.6-6.5,45.9-4.9,46c37.5,1.5,22.7-13.1,35.3-11.3c3.5,2,2.8,41.1,1,42.6c-4-0.3-9.6-2.8-15.6-3.3
                              C2165.6,1689.9,2155,1691.6,2149.6,1691.9z"/>
                          <path id="dev-v" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M2016.9,1807.9c5.8,0,11.1-75.8,13.4-87.9
                              c4-20.8,8.4-71.7,9.5-76.4c6.7-28.4,14-120.5,15.6-136c0.1-1,7.4-55.2,5.3-69.6c-0.7-12.8-14.1-6.3-16.5-16.4
                              c-0.9-4.1,49.3-15.5,50.1-14.3c0.1,14.9-12.7,13.9-12.4,23.4c0.3,11.1-6.2,67-4.6,80.1c0.7,5.4-3.9,91.4-11,127.2
                              c-1.8,9-20.5,97.4-25.7,131.3c-1.7,9.8-5.3,39-9,50.7c-0.5,8.5,6,5.8,6.8,21.7c0.2,3.5-44.3,1.8-46.9,1.8
                              c-4.4-11.2,10.3-16.6,8.8-23.8c-1-4.9-3-61.3-3-66.3c0-36.9-2.5-75.2-3.7-112c-1-31.2-3.7-61.6-3.7-92.9c0-15.5-2.9-81.1-3.3-90.3
                              c-0.2-6.4-16.4-3.5-17.9-10.9c-0.8-4.2,50.1-16.6,51.6-15.1c1.4,9.2-13.2,14.1-12.3,20.6c0.7,5.4,5.3,62.5,4.3,79
                              c-1.2,20.6,3,37,3,66.7c0,13.3,1.8,82.2,2.3,106C2018.1,1721.6,2012.4,1807.3,2016.9,1807.9z"/>
                          <path id="dev-e" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M1918.8,1840.6c-11.3,0.8-56.8-1.3-58.8-1.1
                              c-3.4,0-1.3-14.4-0.5-23.2c0.1-1.3,6.7,1.3,7.4-1.1c6.1-23.6,12.4-172.7,12.2-180.2c-0.4-23.6,0-39-3.8-62.4
                              c-2.2-13.4-2.2-70-3.3-77.2c0,0-13.5,4.1-13.4,2.3c0.7-4.4-5.3-17.8-3.7-18.2c2.6,0,38.5-11.2,43.7-12.2c10.9-3.2,32.4-8.4,42-9.3
                              c1.6-0.2,2.1,34.5,0.5,39.4c-0.2,0.6-9.8-10.1-14.8-9.9c-7.4,0.3-22.3,2.7-30.2,3.6c-1.9,0.2-2.2,19.6-1.2,28.5
                              c2.9,25.4,1,51.8,3.8,77.3c4.1,36.7,2.4,61.5,0.6,98.4c-0.4,8.7-1.4,14.7,0.2,15.1c32.3-0.2,28.3-5.5,36-5.2c0,0-0.1,6.2-0.2,11.7
                              c0.1,6.4-0.3,19.5-0.3,19.5c-6.9-0.5-6-5-37.1-4.2c-2.4,0.5-2.3,6.4-4,25.9c-1.8,15.5-5.6,57.6-4,57.7c12,0.4,20.1,1.5,31.1,1.6
                              c12,0.4,12.6-10.5,18.5-10.3c1,3.9,1.8,31.1-0.5,32.8C1937.1,1841.4,1927.7,1840.5,1918.8,1840.6z"/>
                          <path id="dev-d" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M1740.7,1610c-1.2-31-3.1-35.3-3.1-35.8
                              c-0.1-1.6-4.5-32.4-4.8-34c-3.1,0.8-15.9,4.4-16.5,2c-1.2-4.8-2.9-22.6-2-23.1c9-4.4,43-14.5,53.4-16.3c3.7-0.1,26-3.1,29.4-1.6
                              c13.6,6.3,21.4,17.1,22.4,31.3c2.1,30.7,4.8,55.4,5.4,86.1c0.2,13.8,0.3,62.5-0.3,81c-0.9,25.2-2.5,50.2-7.2,75.1
                              c-3.1,16.1-9.1,31-16.8,45.2c-3.5,6.5-13,13.8-24.2,17.3c-12.1,3.8-48.5,2.7-61.6,2.7c-2-0.7,0.9-18.7,1-22.8c10.8-0.5,13-1,13-1
                              c4.9-27.3,11.9-135.7,12-152.7C1741.9,1638,1740.4,1618.2,1740.7,1610z M1802.5,1660.2c0.1-6-2.9-105.3-4.4-118.1
                              c-2.8-23.9-45.2-7.9-45.2-7.9c1.2,11.2,6.8,30.5,7.3,41.9c0.8,16,1.8,16.9,2.9,32.9c0.4,6.6,1.5,20.5,0.3,32.7
                              c-1.5,15.7-1.9,82.7-2,95.5c0,4.8,0,9-0.4,14.1c-1.1,12.4-11.7,63.9-10.8,64c10.7,0.8,26,0.2,31.6-7.9c8.3-12.1,12-38.9,12.8-41.2
                              c1.7-11.5,2.1-24.1,3.5-35.5C1800.7,1710,1803.6,1680.9,1802.5,1660.2z"/>
                          <path id="des-n" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M1172.1,1672.2c-0.7,23.9,0,141.7-1.4,153.1
                              c-0.7,7.7,10,3,11.3,13.8c0.2,1.5-57.2,3.5-57.2-0.2c0-7.6,9.2-5.6,10.6-11.5c5.6-24,7.6-91.9,9.8-101
                              c4.3-17.8-1.5-224.9-1.5-245.3c0-10.5-10.4-12.9-8.5-23.4c0.9-5.1,33.3,7.2,35,11.3c2.2,5.4,8,42.2,13.5,54.2
                              c4.4,9.6,13.8,44.7,15.5,52.4c5.1,23.8,20.3,107.5,24.7,129.9c1.2,5.9,16.5,85.4,17.7,88.7c9.4-2.5-3.1-189-2.8-202.5
                              c0.4-16.9,1.7-80.8,0.7-92.2c-1.6-4.1-9-11.7-8.9-18c0.9-2,51.2,15.3,48.6,19.6c-3.4,8-11,0.7-12.3,13.6c1.7,15.3-4,44.4-4.1,59.9
                              c-0.1,16.3,5.5,98.1,3.5,139.4c0.1,25.1,3.6,82.4-1.2,103.4c-3.8,16.5,9.6,8.7,11.3,20.3c0.3,2-58.4,4-59.8,0.3
                              c-0.9-5.9,11.7-5.3,7.2-20.8c-4.4-15.2-15.6-67.2-17.9-82.8c-2.5-17.7-11.1-34.5-13-52.2c-1.5-13.9-22.6-123.5-24.5-127.3
                              C1165,1554.4,1172.7,1649.5,1172.1,1672.2z"/>
                          <path id="des-g" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M1046.7,1434.9c-0.8,1.3-20,49.6-20.9,47.2
                              c-12.2-12.1,4-8.4-8.2-32.8c-4.4-9.9-36.3-32.3-46.9-25.8c-8.6,5.3-12,19.1-14.1,28.2c-2.6,11.3,4,60.4,4.2,73.5
                              c0,1.5-0.8,90.1-2.2,97.4c-3,16-6.7,111-10.4,137c-2.2,15.1,0.7,22,5.4,36c7,21.2,36.4,29.8,52,14.4c7.3-7.1,5.6-60.8,7.3-78.8
                              s0.3-37-2.9-36c-3.5,0-14.5-2-24.4-0.9c-0.3-9.1-14.3-26.2-7.2-29.7c15.5,0.9,67.6,2.4,82.7,0.4c3.3,1.3,1.4,20.5-2.7,27.7
                              c-0.7,8-15.3,1-16.3,5c-3.1,12.5,2.3,138.3,0.7,143.5c-25.7,1.6-15.1-21.5-25.7-11.4c-11.1,10.4-11,13.8-48.9,13.8
                              c-49.8,0-54.6-69.1-51.1-87.6c3.4-17.8,15.5-231.8,13.3-241.5c-5.1-23.6-8.3-123.4,46.5-115.6c28.4,4.1,43.1,25.7,52.3,30.2
                              C1036.9,1432.7,1050,1425.9,1046.7,1434.9z"/>
                          <path id="des-i" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M817.9,1572.5c0.3,2.4,0.1,22.8,0,27.8
                              c-0.7,25.8-2.3,72.9-2.8,88.9c-0.1,2.3-5.8,67-7.4,79.2c-1.6,12.3,0.2,35.7,0.1,40.2c-0.2,3.7,4,3.9,11.8,5.2
                              c6.5,1.2,7.2-6.5,10.8-4.5c-0.6,4.5,1.9,28.7-1.5,29.7c-24-3.7-65.2,1.1-68.1,0.3c-1.1-1.1-2.3-24.7-1.6-29.5c3.8-4,4.4,3.8,10,3.1
                              c7.5-1.5,9.9,2.6,10.5-3.1c1.7-16.7,1.6-39.9,3-56.7c1.3-15.8,8.2-142.2,8.6-152.5c0.3-8.9-0.8-28.6-0.7-38.5
                              c0.2-17.2-7.2-133.3-7.9-140.6c-1.5-15.6-0.4-48.9-0.6-50.6c0,0-3.6-2.4-7.8-4.3c-8.2-2.5-9.9,6.2-13.7,2.8
                              c1.8-4-1.5-31.6,2.3-32.2c5-0.3,25.8,5.7,40.6,12.4c11.7,5.3,7.9,3.6,20.7,8c2.3,1.1,10.8,5.4,10.2,7.8c-3.1,12-3.9,26.9-5.7,26.1
                              c-4.7-1.5,2.6-2.4-8.7-8.5l-12.4-4.7C807.4,1380.6,816.8,1563.2,817.9,1572.5z"/>
                          <path id="des-s" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M633.7,1593.6c-5-10.8-42.2-23.9-45.7-27
                              c-14.3-12.7-29.1-28.2-32.3-46.9c-1.4-8.5-7.9-85.2-7-105.4c0.1-1.3,1.3-124.5,51.9-125c49.5-0.5,61.9,46.1,63.5,61
                              c1.5,19,8.6,87.4,8.8,106.4c0.1,16.9,6,10.7,6,22.1c-1.2,3.5-38.2,3.4-41.8-0.1c-1.7-11.3,8.9,0,8.9-20.4
                              c0-13.6-2.9-101.1-3.5-114.7c-0.5-11.1-17.7-32-24.8-33.3c-8.3-1.6-26.3-0.1-31.1,19.5c-4.1,11.5-8.4,78.9-8.4,84.3
                              c-0.3,31.9,1.6,97,9.2,111.5c6.7,28.1,48.7,28.7,64.6,52.8c5.9,7.7,15.8,88.4,15.4,114.2c-0.2,6.6,10.8,148.7-51.6,150
                              s-87-72-86.6-96.3c0.3-18.6-2.2-53.2,1.9-76.3c3.9-21.7,29.1-40.7,44-40.7c6.2,0,7.5-5.3,17-12c3.1-2.2,14.3,65.5,10.7,65.2
                              c-12.7-1.2-7.2-7.3-19.4-14.8c-8.7-5.3-17-8.3-20.8-0.1c-6.4,14.1-3.9,36.3-3.9,52.7c0,13.2,23.6,88.9,51.6,88.3
                              s35.4-54.3,34.9-90.8S637,1600.8,633.7,1593.6z"/>
                          <path id="des-e" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M390.9,1837.5c-14.1,0.2-47-1.7-63.2-2.6
                              c0,0-2.3-8.4,1.4-24.7c7.6,2.2,6.5-0.6,6.6-12.9c0.8-11.5,1.1-87.4,2.2-122.6c0.2-5.8,0.6-24.6,0.5-26.3c0-1.3,1.2-60.1,2.8-65.7
                              c1.6-5.7-0.1-91.5-1.9-103.6c-0.6-3.9-5.9-97.2-5.6-105.1c0.4-12.5-1.9-96.5-1.2-108.9c1-29.3,3.4-26.6-8.4-27
                              c-0.9-13.7-1.4-31.6-0.8-31.6c18.3,0,60.6,21.5,76.7,27.6c52.2,15.8,50.4,10.3,59.1,10.9c3.1,0.6-6.7,49.1-9.6,48.6
                              c-6.4-0.7,2.6-13.5-15-21.1c-20.2-8.6-53.9-24.3-73-28.1c-2.5-0.3,1.9,105.3,2.5,116.2c1,21.2,0.3,48.6,2.4,69.8
                              c1.2,12.5,3.6,60.7,4.4,72c1.1,16.5-1.6,70.6-1.2,74.8c1.4,16.9-0.5,72.7,0.6,73.2c4.5,1.2,24.5,0.8,28.8,0.9
                              c28.8,1.2,26.8-7.5,35-7.8c1.1,0.4,1.6,39.9,0.4,39.9c-5.9,0-2.6-8.6-64.7-8.2c-1.8,0-1.2,136.5,2.2,137.5
                              c74.2,3.4,61.5-11.1,70.8-11.2c5.1-0.7,3.4,33.7,2.8,34C436.5,1837.5,404.6,1837.5,390.9,1837.5z"/>
                          <path id="des-d" style="fill-rule:evenodd;clip-rule:evenodd;fill:#FFB400;" d="M126.1,1143.7c14.1,4,43.3,12.5,57.2,17
                              c16.3,5.2,31.2,20.7,38.8,36.3c7.6,15.5,11.1,31.5,14.7,48c3.3,15.3,14.6,210.1,11.9,231.1c-2.2,16.8-6.5,184.4-7.3,203.3
                              c-1.1,27.2-12.2,123.8-42.6,142.9c-32.2,20.2-98.8,6.3-118.7,11.6c0,0-0.7-8.5,0-23.9c0.9-6.9,17.3,11.1,17.4-17.1
                              c0.2-53.8-6.2-295.7-6.2-299.6c0-114,3-286.6,2.9-296.1c1.5-45.9-15.1-24.4-16.5-32.9c-0.5-2.8,0.2-27.7,3.5-27.9
                              C83.3,1136.4,121.2,1142.4,126.1,1143.7z M201.1,1210.7c-5.6-16.7-17.2-23.3-33.5-28.2c-15.9-4.6-51.2-15-50.7-11.2
                              c0.8,6.9,0.7,61.5,0.9,71.1c0.3,16.2,2.1,171.9,2.8,182.6c1.2,18.9,0.4,132.4,1.8,151.4c0.4,5.3-1.5,123.7-1.8,126.3
                              c-1.3,16.4,0.9,102.1,0.9,102.1c21.4,6.4,21.4,6.4,43.7-2.8s37.6-42.3,39.4-55c2.7-19.4,10.7-255.5,10.7-265.3
                              C215.3,1469.3,208.4,1232.4,201.1,1210.7z"/>
                          <path id="bk-n" style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M1650.6,2036.7c-8.8,0.1-8.7-1.3-10-7
                              c-1.5-6.9-8.3-9.3-12.2-14.3c-5.4-6.8-11.8-13-18-19.1c-3.3-3.2-5.2-2.5-6.2,2c-3.4,14.9-10.6,28.4-16.3,42.4c-2,4.9-7,6.8-10.7,5
                              c-4.4-2.1-5.8-6.5-3.5-11.8c6.6-15.1,12.8-30.3,17.3-46.2c1.4-5,2-9.8,2.5-14.8c0.5-5,1.8-9.9,6.8-11.3c5.4-1.5,9.7,1.5,12.7,6.2
                              c8.6,13.5,21.8,22.8,31.3,35.6c4.9,6.7,8,6,11-2.2c4.5-12.2,13.7-21.5,19-33.1c1.7-3.8,0.6-5.4,9.8-3.2c6,2.7,3.2,4.6,3.3,7.7
                              c0,2.9-1.5,5.1-3.2,7.3c-8.6,11.1-13.2,24.2-19,36.8c-1.4,2.9-0.8,5.9-1,8.9C1664.2,2036.1,1664.2,2036.1,1650.6,2036.7z"/>
                          <path id="bk-k" style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M1120.3,1996.2c11.6,13,26.9,20.3,40.8,29.6
                              c9.6,5.8,9.6,5.8,3.6,13.5c-7,9-7,9-14.1,3.7c-0.9-0.4-1.9-0.8-2.6-1.5c-10.4-10.2-24.2-15.8-35.1-25.2c-1.8-1.5-3.7-3-5.8-4.1
                              c-8.3-4.5-11.4-3.2-13.2,6.3c-1.2,6.1-3.5,11.6-6.1,17.1c-3.4,8.2-4.4,10.6-12.8,6.9c-10.9-4.6-8.9-4.9-3.6-14.1
                              c6.6-11.8,9.8-24.9,15.9-36.9c5.5-10.9,13.1-21.3,13.4-34.5c0.1-3.9,1.7-7.1,10.9-3.9c8.8,3.1,7,5.6,5.3,9.3c-1.7,3.7-4,7-5.9,10.6
                              c-1.1,2.1-2,4.8,0.2,6.5c1.7,1.4,4,0.2,5.5-1c8-6.4,17.8-9.6,26.3-15.1c3.4-2.2,6.4-4.5,10,0.1c7.4,8.7,2.1,7.7-0.6,10.2
                              c-3.2,7.1-10.7,8.1-16.2,11.3C1130.6,1988.3,1125.3,1991.7,1120.3,1996.2z"/>
                          <path id="bk-o-2" style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M1001.8,2002c1,8.2-3.7,14.4-8.9,21
                              c-10.5,13.2-24.9,20.4-40.6,18.3c-16-2.1-31.2-8.7-39.7-25.5c-5.6-11.1-4.3-20.8,1-30.6c5.1-9.6,13-16.1,24-18.3
                              c4-0.8,11.6-1.4,14-4.6c0,0,3.8-4.4,8.3-2.8c5.4,1.9,11.1,3,16.5,4.8C992.4,1969.6,1002.8,1985,1001.8,2002z M936,1981
                              c-10.3,4.6-14.5,18.7-7.3,27.4c5,6,11,11.5,19.3,14c9.8,2.9,19,0.7,27.3-3.4c7-3.5,12.6-9.3,12-18.4c-0.7-9.7-7.5-18.3-15.8-21.4
                              C961.4,1973.9,940.9,1978.8,936,1981z"/>
                          <path id="bk-o-1" style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M728.3,2009.1c1.7-16.7,6.8-31.7,20.8-42.5
                              c6.4-4.9,13-7.6,21.3-7.1c11.3,0.7,22.2,1.6,30.8,10.3c6,6,9.4,13,9.6,21.6c0.3,13.4-3.2,25.4-13.1,34.9
                              c-5.6,5.4-9.8,12.6-19.2,13.6c-11,1.2-21-0.4-31.2-4.8c-7.9-3.4-11.9-10.5-18.2-15.4C725.6,2016.7,728.4,2012.7,728.3,2009.1z
                               M744.8,2002.9c-0.9,3.7,1.1,7.3,4.5,10.6c4.7,4.7,10.7,7.6,16.8,8.7c4.7,0.8,10,3.8,14.7,0.9c9.2-5.8,15.6-14.4,15.9-25.4
                              c0.2-8.2-3.6-16.4-11.2-20.2C768.3,1969,746.1,1982.6,744.8,2002.9z"/>
                          <path id="bk-r" style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M627.9,1978.4c1.8,6.7-6,8.4-9.6,12.8
                              c-1,1.2-2.9,1.6-4.4,2.4c-3.1,1.8-9,1.1-9.3,5c-0.3,4.5,5.5,6.5,8.9,8.2c14.3,7,31,18.4,45.1,25.5c3.9,2-4.8,15.9-10,14.7
                              c-4.5-1-9.5-2.3-13.1-4.9c-15-11-32.4-17.8-48.3-27.1c-2.3-1.3-5.1-1.7-7.7-2.4c-7.6-1.8-9.3-1.2-9.6,6.8
                              c-0.3,6.7-4.7,9.2-8.7,12.7c-2.3,2-4.3,0.9-6.5,0c-2.8-1.2-2.9-3.4-2.3-5.9c3.5-14.7,1.2-30.1,4.9-44.8c0.9-3.7,0.9-7-0.7-10.7
                              c-1.8-4.1-2.3-8.5,0.6-12.6c1.5-2,2.9-3.3,5.8-2.7c10.3,1.9,20.5,2.4,30.9,0.4c6.3-1.2,12.7,0.9,19,1.8
                              C620.4,1958.6,627.7,1969,627.9,1978.4z M591.7,1970.9c-3.3,0.4-6.6,0.8-10,1.3c-1.3,0.2-5.8-1.4-7-0.9c-3.8,1.4-2.8,21.5,2.7,19.7
                              c13.4-4.2,7.2-1.9,22.7-7.2c3.5-1.2,8.1-2.8,8.1-6.4c0-4.1-5.1-4.5-8.5-5.5C597.1,1971.2,594.4,1971.2,591.7,1970.9z"/>
                          <path id="bk-b" style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M420.5,1946.9c7.5,2.3,14.3,0.5,21,1.7
                              c11,2,18.7,10.7,19.1,21.6c0.1,3.6-1.9,6.5-3.4,9.4c-4.5,8.6-4.2,8.1,2.2,14.6c10,10.2,13.3,20.8,4.2,34.3
                              c-2.6,3.8-6.5,5.8-10.5,7.6c-12.6,5.6-25.4,10.8-39.2,12.9c-4.9,0.8-11.8,1.7-16.3,4.6c-3.9,2.5-7,2.5-11.7,2.3
                              c-6.8-0.4-8.5-3.3-6.9-9.9c3.4-14.1,8.6-27.5,15.3-40.3c4.9-9.5,5.7-20,9-29.9c1.9-5.7,4.2-10.8,0.5-17.2c-2.2-3.8,2.2-7.5,6-9
                              C413.2,1948,417.3,1947.6,420.5,1946.9z M436.8,2005.3c-5,0.2-24.5,3.4-28.8,5.7c-3.6,1.9-12.3,25-6.7,25c2.7,0,14.3-2.4,16.9-3.2
                              c9.3-2.9,18.6-6.2,27.9-9.2c4.1-1.3,5.8-3.6,5.9-8.1c0.1-5-2.2-7.5-6.2-9.3C443,2004.9,439.9,2005.1,436.8,2005.3z M444.1,1972.4
                              c-1.7-2.6-1.7-6.9-5.8-7.4c-4.7-0.6-8.9-2.8-13.8-2.8c-4,0-6.1,1.5-7.6,5.1c-1.9,4.8-2,9.9-3,14.8c-2.3,11.2-4.7,11.2,6.1,8.7
                              c8.1-1.9,15.7-7.9,21.4-13C443,1976.5,444.2,1974.8,444.1,1972.4z"/>
                          <path id="ny-n" style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M1893.7,1997.2c-3,9.2-11.7,27.1-17.1,35.1
                              c-3,4.4-5.9,8.9-7.1,14.4c-0.6,2.8-1.7,7-6.1,7.1l-11.3-7.1c-1.7-6.7,2.9-14,6.8-18c10-10.3,13.7-23.3,17.3-36.3
                              c2.3-8.2,7.8-14.1,12.4-20.6c4.5-6.3,4.5-6.3,15.2,3.3c2.5,3.1,18.4,33.6,22.1,41.9l9.4-14.6c0,0,12.8-27.5,15.9-36.1
                              c2.9-9.7,2.9-9.7,14.6-4.7c5.8,3.2,5.8,3.2,2.8,9.4c-3.5,7.4-6,15.1-9.8,21.9c-3.8,7-7,14.4-11,21.4c-3.9,6.8-13.8,32.3-14.2,35.7
                              c-2.2,8,0.1,8-9.1,8c-7.9,0.1-6.8-2.8-7.8-6.5C1912.9,2037.1,1894.9,1997.5,1893.7,1997.2z"/>
                          <path id="us-a" style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M2774.7,1972.3c0.7,7.8,1.3,16.8,1.1,26
                              c-0.1,3.4,1.8,4.8,4.7,5.3c10.5,1.7,10.5,2.7,0.7,10.6c-5.2,4.2,0.8,11.3,1.9,16.4c2.8,13.2,8,19.5-6.7,19.5
                              c-10.7,0-9.2-3.6-9.9-9.2c-0.9-6.8-0.6-21.9-2.1-28.4c-11.1-3.6-10.1-3.1-16.2-3.7c-4.4-0.4-11.8-4.3-16.2-4.3
                              c0,0-13.2,14.5-17.5,25.4c-0.9,2.2-0.9,6.9-2.2,8.8c-1.6,2.3-20.9,2.3-23.4-1.5c-2.5-3.9,4.6-15.1,6.3-16.2
                              c3.6-6.6,35.2-41.2,39.2-45c6.2-5.9,13.2-13.3,17.4-20.8c5.4-9.5,4.2-7,10.4-6.8c6.4,0,7.5,0.4,9.8,8.3
                              C2773.9,1961.5,2774.1,1963.2,2774.7,1972.3z M2735.7,1993.9c15.2,2.8,29.7,6.5,29.7,6.5s1.9-20.8-5.8-35.7
                              C2754.6,1968.6,2739.4,1987.9,2735.7,1993.9z"/>
                          <path id="us-s" style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M2537.2,2045.4c-3.7-0.7-9.4-1.5-14.9-2.8
                              c-6.5-1.5-10.1-3-16.6-1.8c-6.3,0.7-17-3.8-16.2-14.9c0.9-12.5,2.2-6.7,6-7.4c9.5,1.7,31.5,13.4,58,5.1c3.6-1.1,3.9-3.6,1.3-6.1
                              c-2.7-2.5-5.5-4.9-8.9-6.5c-3.9-1.9-7.3-4.8-10.9-7c-10.3-6.1-10-14.9-8-24.5c0.9-4.5,11.1-13,17.3-13.9s12.3-2.8,37.7-3.9
                              c1.2,0,4.8,9,3.9,9.7c-4.6,3.5-7.8,5.8-13.8,5.5c-6.7-0.3-18.5,0.7-23.7,5c-2.5,2.1,1.7,10.3,15.7,14.4
                              c10.1,2.3,16.9,16.3,16.3,24.5c-0.6,8-6,17.6-14.4,19.8C2557.1,2042.8,2548.3,2045.1,2537.2,2045.4z"/>
                          <path style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M2325.7,1966.4c3-13.2,3.5-12.6,16.4-8.9
                              c4.1,1.2,4.1,1.2,2.3,7.5c-1.5,4.9-4.4,19.2-6,24c-3.3,9.9-0.3,18.8,4,27.2c4.9,9.3,18.3,8.2,24-1.6c7.1-12.3,10.7-25.9,14.1-39.6
                              c0.5-2,0.9-3.9,1.3-5.9c2.7-11.7,2.7-11.7,13.5-9.2c10.9,2.5,9.6,3,7.7,12.6c-0.6,3.2-1.2,6.8-2.7,9.4
                              c-6.9,12.4-11.5,25.7-16.9,38.7c-5.6,13.5-23.8,24.5-37.4,20.7c-7.7-2.1-13.1-8.8-19-14.3C2312.5,2013.3,2323.6,1976,2325.7,1966.4
                              z"/>
                          <path style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M2032.6,2061c-6.7,0.1-9.2-2.4-6.9-8.4
                              c4.5-11.6,7.2-23.6,11.6-35.2c3.9-10.3,2.6-14.9-6.2-21.7c-6.9-5.3-13.7-10.9-20.2-16.7c-5.8-5.2-5.7-9.9-0.6-14.4
                              c5.3-4.7,11.6-4.5,15.5,1.4c2.8,4.2,6.7,6.6,10.7,8.9c3.6,2,7.3,4,9.6,7.2c3.5,4.9,6.5,3.4,10.1,0.6c5.4-4.2,7.5-11,12.2-15.8
                              c3.7-3.8,7.6-6.7,12.8-7.5c4.4-0.6,8.3,1.2,10,5.3c1.5,3.8-1.5,6.4-4.3,8.3c-11.8,8.4-19.4,20.2-25.7,32.9
                              c-6.2,12.3-10.5,25.1-12.2,38.9C2047.7,2055.3,2041.3,2060.8,2032.6,2061z"/>
                          <path style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M1414.7,1960.6c3,0.1,5.4,1,7.4,3.2
                              c6.5,7.4,15.6,10.2,24.1,14.1c2,0.9,8.5,7.2,9.9,6c6.5-5.3,22.6-16,28.5-22.3c4.5-4.8,8.6-4.9,12.1-1.3c3.1,3.2,2.2,9.6-2.9,13.1
                              c-7.8,5.4-15,11.4-22,17.6c-13.1,11.5-23.8,24.5-25.6,43.1c-0.7,7.1-5.1,13.1-12.5,15.9c-3.1,1.2-6,1.5-8.4-1.1
                              c-2.1-2.3-3.7-4.9-2.6-8.2c0.1-0.3,0.2-0.7,0.3-1c5.4-10.2,7-22.1,13.8-31.9c5.8-8.3,5.5-11.4-3.2-16c-8.4-4.4-15.4-10.8-23.8-15.1
                              c-4.1-2.1-7.6-4.7-5.8-10.3C1405.8,1961.1,1410.6,1961.5,1414.7,1960.6z"/>
                          <path style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M1283.1,1969.3c-4.7,8-3.6,19.3-7.5,29.5
                              c-1.9,5-3.1,10.3-4.3,15.6c-0.7,3.1,0.5,5.6,4,5.7c13.4,0.4,26,7.6,39.8,4.8c1-0.2,2-0.1,3,0c4.5,0.7,9.1,5.1,8.6,8.2
                              c-0.9,5.9-6.7,11.4-11.6,10.8c-20.6-2.6-48.1-5-68.9-5.8c-4.3-0.3-8.9-0.7-0.4-12.7c3-4,10.7-21.9,12.7-26.8
                              c1.8-4.7,4.2-9.2,4.8-14.3c0.4-3.7,1-6.9-0.2-10.8c2.5-14.1,1.5-15.6,14-13.8C1286,1961.3,1283.8,1962.9,1283.1,1969.3z"/>
                          <path style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M1717.4,2048.4c-2.5,11-12.2,22.2-16.2,19.8
                              c-2.6-2.8,3.4-5.5,4.8-14.3c3.2-20.4-9.2-12.8-13.5-15.6c-3.9-2.4-6.2-5.7-4.9-10.4c1.3-4.3,4.6-6.9,8.9-7.8c4.5-1,9,1.2,11.2,5.2
                              C1710.8,2035.1,1719.5,2031.9,1717.4,2048.4z"/>
                          <path style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M2411.4,2040.5c-5.5,2.7-7.9,2.1-9.4,1.4c-3.4-1.5-5.7-4-5.1-8
                              c0.6-3.7,3.1-6.1,6.5-7.3c3.6-1.3,7.5,0,9.7,3.1C2417.9,2036.4,2415,2037.2,2411.4,2040.5z"/>
                          <path style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M2601.6,2031c2,5.4,1.1,7.5,0.2,8.9c-2,3-4.7,4.7-8.5,3.6
                              c-3.5-1-5.6-3.6-6.4-6.9c-0.8-3.4,1-6.9,4.3-8.5C2598.4,2024.5,2598.8,2027.2,2601.6,2031z"/>
                          <path style="fill-rule:evenodd;clip-rule:evenodd;fill:#D8D8D8;" d="M2807.2,2031.4c3.7,2.6,4,4.4,3.9,5.7
                              c-0.2,2.8-1.5,5.2-4.8,6.3c-3.1,1-5.7,0.2-7.7-1.6c-2-1.9-2.2-5-0.4-7.5C2802,2028.6,2803.5,2030.2,2807.2,2031.4z"/>
                          <g id="ampersand">
                              <path style="fill-rule:evenodd;clip-rule:evenodd;fill:#F9F9F9;" d="M1445.7,1604.9c0,0-21.7-10.2-24-37.3
                                  c-2.7-31.7,23.3-58.3,62-58.3c20.6,0,39.1,15.8,41.3,26.3c2,9.7-1.2,19.5-2.7,26.3c-12,4.8-32.6,0.5-34-5.3
                                  c-1.1-4.3,19.7-11.1,16-22.3c-2.5-7.8-19-11.3-33.3-9.3c-15.5,2.2-43.3,57.9,1.6,70.6c13.4,3.8,19.4,3.7,19,13
                                  c-0.3,8.4-6.4,10.4-18.6,10.4s-56.9,26.9-66,44c-32,60.3-27,149.2,96.2,146.4c85-2,69.9-151.8,69.1-155.4
                                  c-0.8-1.9-41-0.7-65.6,10.3c-6.1,2.7-24.4,23.7-13.7,39.7c6.7,10,21.9,8.2,25.7,7c14.3-4.3,2.9-21.7,9.3-22c2.3-0.1,22.3,14,6,29
                                  c-9.1,8.4-36.2,9.5-45.5,0.8c-26.2-24.3-6.2-85.5,54.4-86.4c7.8-0.1,31.8-2.6,39.4-1.7c15.6,1.8,58,3.3,64.3-24
                                  s-6.8-42.9-15.7-46.7c-12.8-6.3-28.4-4.6-37,0.3c-25.1,14.4-15.4,44,9.7,34.7c5.7-24.7,39,9.7-6.3,16c-8,1.1-22-2.3-26.8-9.8
                                  c-5.5-6.3-8.3-17.6-6.7-24.5c3.7-16.2,15.6-24.2,32.4-29.4c16.4-5.1,32.9-4.3,46.1,3.9c32.6,19.5,30.7,76.9,2.6,92.5
                                  c-15.7,8.7-20.1,13.6-45.7,12.3c-2,1,1.6,25,2,55.3c0.2,16.3,16.6,99.1-88.1,118.9c-48.7,9.2-111.2,5.3-135.3-44.7
                                  C1312.4,1649.4,1445.7,1604.9,1445.7,1604.9z"/>
                          </g>
                      </g>
                      </svg>
                      <figcaption>
                          An embedded SVG version of the final(ish) logo. SVG is amazing
                      </figcation>
                  </figure>
                  <p>
                      I'm looking forward to working with the logo more. Keeping
                      it vector was important for me because I want to display it using SVG.
                      I also think there will be some interesting things to do with it using
                      CSS transitions and animations further down the road.
                  </p>
                  <p>
                      <i>Thanks for reading</i>
                  </p>
              ]]>
          </description>
          <link>https://tylergaw.com/articles/a-logo-design-process</link>
          <guid>https://tylergaw.com/articles/a-logo-design-process</guid>
          <pubDate>Thu, 17 Jul 2014 18:30:00 EST</pubDate>
      </item>
      <item>
          <title>How I Start</title>
          <dc:creator>Tyler Gaw</dc:creator>
          <description>
              <![CDATA[
              <p class="entry-intro">
                  I'm beginning a design iteration of this site. I've started
                  freelancing full time and I want the site to display more of my work
                  and explain it in finer detail to give potential clients and collaborators
                  a better idea of what I do and how I go about it.
                  I love this early part of the process where everything is on the table,
                  and up in the air, and I can get arty-farty and a little weird. Hold
                  on tight, it's a bumpy ride.
              </p>
              <h2>Nonsense</h2>
              <p>
                  This part of the process has no structure. I just like to
                  make things as messy as possible. I immerse myself in the types of
                  ideas I want to convey and the type of designer I want to be. I open
                  100 browser tabs across 10 different windows. I keep multiple sketch books in front
                  of me, a No. 2 pencil and busted, cheap sharpener. I drag out every
                  micron pen I have, even though I'll probably not use any of them.
                  I haven't yet, but I'll probably get out the paint and brushes to
                  make a really big mess.
              </p>
              <figure>
                  <img src="https://tylergaw.com/articles/assets/post-image-starting-sketches.jpg" alt="">
                  <figcaption>
                      Bad handwriting. Worse block lettering.
                  </figcation>
              </figure>
              <p>
                  In my sketchbooks I don't really sketch interface wireframes, but I do write lots of words
                  and phrases. Some of them are ideas I want to convey. Some of them
                  are just nonsense that I think is funny. "Hollerin'" "Sweatin'" "Braggin'"
                  "BLACK" "MAKE IT EXTRA LARGE"
                  "NO STRAIGHT LINES" "ALL RIGHT ANGLES" "Don't limit" "Do limit"
                  and plenty more non-sequiturs, contradictions, and fibs
                  that will have me scatching my head when I look back at them at some
                  point in the future. I also draw plenty of skulls on fire and anarchy signs
                  and all other types of bad drawings that look awesome on the back
                  of 8th grader's notebooks.
              </p>
              <h2>Folks</h2>
              <p>
                  I listen to artists and designers talk about making cool art and design.
                  I have seen all of these multiple times, they never get old. I love them and
                  they get me excited to design:
              </p>
              <ul>
                  <li><a href="https://vimeo.com/39441590">Portland/CreativeMornings - Draplin</a></li>
                  <li><a href="http://www.aiga.org/video-HHH-2013-draplin/">More Draplin - Tall Tales from a Large man</a></li>
                  <li><a href="https://vimeo.com/20456428">Even more Draplin</a></li>
                  <li><a href="https://vimeo.com/20777627">Draplin, shit! He's the best</a></li>
                  <li><a href="http://hillmancurtis.com/artist-series/lawrence-weiner/">Design Matters: Lawrence Weiner</a></li>
                  <li><a href="http://buy.beautyisembarrassing.com/">Beauty is Embarrassing - Wayne White</a></li>
                  <li><a href="https://www.youtube.com/watch?v=JyRAHKTy6hI">Beautiful Losers - Buncha great folks</a></li>
              </ul>
              <figure>
                  <img src="https://tylergaw.com/articles/assets/post-image-starting-art.png" alt="">
                  <figcaption>
                      Art, beards, grumpy men
                  </figcation>
              </figure>
              <p>
                  With all of those it's not even really about the crud they make. I
                  don't sit and stare at the work they produce. I get inspired and excited
                  hearing them talk about how much they love what they do, what it
                  means to them, and what they think it means to other people.
              </p>
              <h2>Letters and Such</h2>
              <p>
                  This time around I wrote names of different fonts that
                  I was interested in using or just like the name; "Franklin Gothic Condensed",
                  "Futura", "Roboto Slab", "Neue Helvetica 55 Roman".
              </p>
              <p>
                  I hopped into ole sparky (PS) and threw together some of the type
                  to see how it would look in the colors I have in mind. Again, no structure,
                  just bashing on the keyboard and tweaking some settings here and there to
                  see how things might work later.
              </p>
              <figure>
                  <img src="https://tylergaw.com/articles/assets/post-image-starting-type.png" alt="">
                  <figcaption>
                      Playing around with some type treatments
                  </figcation>
              </figure>
              <h2>Music</h2>
              <p>
                  Music is always a huge part of my process. A <a href="http://www.rollingstone.com/music/videos/interpol-comes-out-of-the-black-in-all-the-rage-back-home-video-20140709">new Interpol song</a> was
                  released yesterday. It's awesome. I've listened to it about 999 times so far.
                  It's having an impact on my brain and designs.
              </p>
              <iframe width="100%" height="325" src="//www.youtube.com/embed/-u6DvRyyKGU" frameborder="0" allowfullscreen></iframe>
              <p>
                  Here's some other crud I have on repeat that will also have an impact on the design:
              </p>
              <ul>
                  <li><a href="http://www.rdio.com/artist/Interpol/album/Our_Love_To_Admire/">Our Love to Admire - Interpol</a></li>
                  <li><a href="https://twitter.com/ANTEMASQUE">ANTEMASQUE - ANTEMASQUE</a></li>
                  <li><a href="http://www.rdio.com/artist/AFI/album/Burials_(Digital_Booklet_Version)/">Burials - AFI</a></li>
                  <li><a href="http://www.rdio.com/artist/Every_Time_I_Die/album/From_Parts_Unknown/">From Parts Unknown - Every Time I Die</a></li>
                  <li><a href="http://www.rdio.com/artist/Lana_Del_Rey/album/Ultraviolence_(Deluxe)/">Ultraviolence - Lana Del Rey</a></li>
                  <li><a href="http://nadiesound.com/album/cry-is-for-the-flies">Cry Is For The Flies - Le Butcherettes</a></li>
                  <li><a href="http://www.rdio.com/artist/Mastodon/album/Once_More_%27Round_The_Sun_1/">Once More 'Round The Sun - Mastodon</a></li>
              </ul>
              <p>
                  That's pretty heavy list. This design is most likely going
                  to be dark, loud, bold, and brooding. Simple and clean can take a
                  long walk off a short pier.
              </p>

              <h2>6th Grade Art Class</h2>
              <p>
                  Here's something I haven't done for designs in the past, but felt
                  compelled to for this one. I pulled out a stack of old magazines and
                  have been tearing through them cutting things out to make crap little
                  collages. I'm not putting much thought into the pieces. These aren't
                  "mood boards" or whatever. Just flipping the pages and if something
                  catches my eye I hack it out and throw it on some paper. A piece of
                  text that's bright. Some dumb phrase. Creepy dude eyes. Anything that's
                  interesting.
              </p>
              <figure>
                  <img src="https://tylergaw.com/articles/assets/post-image-starting-collage.png" alt="">
                  <figcaption>
                      Cuttin’ out Hetfield's eyeballs for shits and giggles
                  </figcation>
              </figure>
              <p>
                  These collages aren't going to be part of the design, they're
                  just a way for me to get moving. Computers are too damn slow. I
                  need to use my hands to move fast and toss shit around and scribble and just
                  be a wreck in general. It helps me get my entire self into it.
              </p>
              <h2>These Silly Paragraphs</h2>
              <p>
                  This post is part of it too. This isn't meant to be some how-to or
                  "10 Clever Ways to Inspire Your Next Responsive E-Commerce Design" post.
                  Like the rest of the process, it's messy. It's unedited and maybe unfinished.
                  It's also a chance to stop and think about what I'm doing. Or to put
                  it another way; procrastinate.
              </p>
              <p>
                  What about you, how do you start?
              </p>
              <p>
                  <i>Thanks for reading</i>
              </p>
              ]]>
          </description>
          <link>https://tylergaw.com/articles/how-i-start</link>
          <guid>https://tylergaw.com/articles/how-i-start</guid>
          <pubDate>Thu, 10 Jul 2014 17:30:00 EST</pubDate>
      </item>
    <item>
        <title>CSS True Titles</title>
        <dc:creator>Tyler Gaw</dc:creator>
        <description>
            <![CDATA[
            <figure>
            <a href="http://lab.tylergaw.com/css-true-titles/">
                <img src="https://tylergaw.com/articles/assets/post-image-true-titles-intro.jpg"
                    alt="Image of the title cards from the True Titles demo">
            </a>
        </figure>
        <p class="entry-intro">
            The <a href="http://youtu.be/ZRPpCqXYoos">opening titles</a> to the
            show True Detective are incredible. CSS contains the building
            blocks necessary to create a similar style title sequence. I tried my
            hand it and came up with fun results. Here's the
            <a href="http://lab.tylergaw.com/css-true-titles/">demo</a> and the
            <a href="https://github.com/tylergaw/css-true-titles">source code</a>.
        </p>
        <p>
            The first thing I have to do is issue the disclaimer. This is an
            experimental project. Things will most likely go wrong when you view
            it. I'm pushing on the browser pretty hard to get it to do things it
            maybe wasn't intended to. Also, due to limited suppport
            of <a href="http://www.w3.org/TR/css-masking-1">CSS masking</a>
            you'll only see the full effect in Chrome, Opera, and possibly Mobile Safari.
            The animations will work in other browsers, but things will look strange.
        </p>
        <h2>The Masking</h2>
        <p>
            There's a whole lot going on in each shot of the TD titles. You should
            read the write up about it on <a href="http://www.artofthetitle.com/title/true-detective/">Art of the Title</a>.
            The main effect that I wanted to recreate was the use of human silhouettes
            to mask shots. They use video clips for the masks and the content being
            masked, but I stuck with still images to keep things a little easier.
        </p>
        <figure>
            <img src="https://tylergaw.com/articles/assets/post-image-true-titles-mask-example.jpg"
                alt="">
            <figcaption>
                An example of using a human to mask footage in the True Detective titles
            </figcaption>
        </figure>
        <p>
            I'm accomplishing masking using the CSS <code>mask</code>
            property. As I mentioned, browser support for it is currently
            limited. I'm going to break down the first title
            card to explain how the masking is
            implemented.
        </p>
        <figure>
            <img src="https://tylergaw.com/articles/assets/post-image-true-titles-opener-card.jpg"
                alt="">
            <figcaption>
                A still of the first title card.
            </figcaption>
        </figure>
        <p>
            Each title card is an <code>li</code> element. They contain base
            styles like width, height, position, etc. Within each <code>li</code>
            is markup specific to that card. Below is the markup for the elements
            that make up the opening card.
        </p>
        <pre><code>&lt;div class="composition animated"&gt;
  &lt;div class="town animated">&lt;/div&gt;
  &lt;div class="human">&lt;/div&gt;
&lt;/div&gt;
&lt;h1 class="copy animated"&gt;
  &lt;b class="role">This is a&lt;/b&gt; CSS Experiment
&lt;/h1&gt;</code></pre>
        <p>
            The containing <code>div</code> element with the class of "composition"
            houses all the images. I'm using a containing element here so I can move
            and scale the "human" and the "town" together, while still being able
            to move and scale each independently. I'm also applying the mask
            to the composition element. The "human" and "town" elements have background
            images applied to them.
        </p>
        <figure>
            <img src="http://lab.tylergaw.com/css-true-titles/img/opener-human.png"
                alt="">
            <figcaption>
                The partially transparent human image, which doubles as the mask
                for the opening card.
            </figcaption>
        </figure>
        <p>
            All of the images are from the <a href="http://flickr.com/commons">Flickr Commons</a>.
            I cropped, clipped, and pushed their pixels around in Photoshop until
            they felt right. Some of them have kind of ratty clipping paths, but
            you can't really seen them with everything moving. I was able to bring
            the file size down some because of that.
        </p>
        <figure>
            <img src="http://lab.tylergaw.com/css-true-titles/img/opener-town.png"
                alt="">
            <figcaption>
                The background image for the town element.
            </figcaption>
        </figure>
        <p>
            I want to be able to move the "town" element from right to left and
            left to right. If I applied the mask to that element, the mask would
            move with the town causing it to not line up properly with the human image.
            The mask needs to be applied to an element above it, the composition element.
            Since CSS masks are only concerned with the opaque and transparent
            portions of an image, I can reuse the human image as the mask for the
            "composition" element using the CSS below. Areas appearing under
            opaque portions will be visible, those under transparent portions will
            be invisible.
        </p>
        <pre><code>.opener .composition {
  ...
  -webkit-mask: url("opener-human.png") top left/cover no-repeat;
  mask: url("opener-human.png") top left/cover no-repeat;
}</code></pre>
        <p>
            The shorthand <code>mask</code> has similar properties as <code>background</code>.
            URL, position, size, repeat. It was important that all of the images and mask
            had the same <code>background-size</code> setting. In this case they
            are all set to <code>cover</code>. That prevents the images from becoming
            too short or too narrow so that you see the edges of them. I could
            have set a fixed width and height on the cards to avoid this
            altogether, however it was more fun to embrace the unstable
            environment that is a Web browser by letting everything remain fluid.
        </p>
        <p>
            The rest of the title cards follow a similar pattern with adjustments
            based on the the number of images that were needed and the animations
            that were put into place. You can see the source scss for each
            card <a href="https://github.com/tylergaw/css-true-titles/tree/master/css/scss/partials">here</a>.
        </p>
        <h2>The Animations</h2>
        <p>
            There are a few different animations happening during the sequence.
            The main animation–or timeline–is responsible for cutting from one card to the next
            and looping back to the first after the last. Within each card there
            are one or more animations that control composition elements and individual
            elements.
        </p>
        <figure>
            <img src="https://tylergaw.com/articles/assets/post-image-true-titles-ani-diagram.jpg"
                alt="Sketch showing the length of the main timelime is seconds and the length of each sub timeline as a percentage.">
            <figcaption>
                A diagram showing the main and sub timelines of the sequence.
            </figcaption>
        </figure>
        <h3>The main timeline</h3>
        <p>
            Once I came up with all the cards I wanted, I tweaked
            the total length until the pacing felt about right at 36 seconds. The .54 seconds was
            added the to make the sequence length match the length of the audio track.
            The track ended up an odd length because I edited it with <a href="http://audacity.sourceforge.net/">Audacity</a>
            and I've never been able to tame that beast. Has anyone?
        </p>
        <p>
            As the diagram shows, the length of each title card is a percentage
            of the total sequence length. That length is how long that card will
            be visible. Figuring out the length for each card took quite a bit
            of trial and error to get right. If each card was the same length, coming
            up with a percentage for each would be easy: <code>100% / number of cards = length</code>.
            That was not the case with this sequence.
            There are four title cards and one "bump" card.
            The bump card contains no text, it's there as a break and
            extra visual information (AKA, it looks cool). Because of that, the
            bump needed to be shorter than the title cards. The photography card
            doesn't contain as much motion as the other cards and that was making it feel too long.
            I stole time from it to give to the last card. I wanted the last card with my Twitter info
            to be longer than the others.
        </p>
        <p>
            Each card needs keyframes at multiple positions. I'm going to show
            the second title card–the director card–as an example, because they get a little strange.
        </p>
        <pre><code>@keyframes director {
  0%, 22.9%, 46%, 100% {
    opacity: 0;
    transform: translateX(-100%);
  }

  23%, 45.9% {
    opacity: 1;
    transform: translateX(0);
  }
}</code></pre>
        <p>
            I'm going to explain this backwards. Starting at 23% of the main
            timeline length I want the card to be visible and on the screen. It should stay
            visible until 45.9% of the main timeline length.
        </p>
        <p>
            The first set of frames says that the card should be invisible and
            off screen from 0% until 22.9% and again from 46% to 100%.
        </p>
        <p>
            I'm using the 0.1% intervals to make sure there is no visible interpolation
            of the opacity or transform. I want the transition from one card to
            the next to be a quick cut. The opacity is still interpolating from
            0 to 1 and transform from -100% to 0, but it's too fast to be seen.
        </p>
        <p>
            I'm using <code>translateX</code> to move the invisible cards
            out of the viewport so they don't obsure any elements of the current
            visible card. That was happening when I was only using <code>opacity</code>.
        </p>
        <p>
            Again, getting the timing correct on each card was a lot of trial
            and error. Making changes a percentage at a time. You can see
            the full scss for the main timeline animation
            <a href="https://github.com/tylergaw/css-true-titles/blob/master/css/scss/partials/_general.scss#L115">here</a>.
        </p>
        <h3>A sub timeline example</h3>
        <figure>
            <img src="https://tylergaw.com/articles/assets/post-image-true-titles-director-card.jpg"
                alt="">
            <figcaption>
                A still of the director title card.
            </figcaption>
        </figure>
        <p>
            The director card is the most complex card with five separate animations happening
            simultaneously. Here's the markup for the card.
        </p>
        <pre><code>&lt;div class="composition animated"&gt;
  &lt;div class="street animated">&lt;/div&gt;
  &lt;div class="trees animated">&lt;/div&gt;
  &lt;div class="human">&lt;/div&gt;
&lt;/div&gt;
&lt;h2 class="copy animated"&gt;
  &lt;b class="role">Directed by&lt;/b&gt;
  &lt;a href="https://tylergaw.com">Tyler Gaw&lt;/a&gt;
&lt;/h2&gt;</code></pre>
        <p>
            Anything with the class of "animated"–you guessed it–has an animation
            applied to it. The fifth animation is on a pseudo-element. Below is
            list of each animated element and description of what is animated.
        </p>
        <ul>
            <li>
                <b>composition</b>: Houses all the images. Over the span of 17 seconds
                it is slightly scaled, skewed along the X axis, and translated on the X axis.
            </li>
            <li>
                <b>street</b>: A <a href="http://lab.tylergaw.com/css-true-titles/img/director-street.png">masked image</a>.
                Its X and Y positions are changed over 18 seconds using <code>translate</code>.
            </li>
            <li><b>trees</b>: A <a href="http://lab.tylergaw.com/css-true-titles/img/director-trees.png">masked image</a>.
                Its X and Y positions are changed over 18 seconds using <code>translate</code>.
            </li>
            <li>
                <b>copy</b>: The card info is slightly scaled and translated
                on the X and Y axes over 17 seconds. The copy moves somewhat in
                tandem with the composition.
            </li>
            <li>
                <b>pseudo-element</b>: I'm using the <code>::before</code> selector
                on the trees element to add an additional subtle detail I called a "blip". It is a
                <a href="http://lab.tylergaw.com/css-true-titles/img/director-blip.png">image of fire</a>
                that is displayed shortly to give a little more detail to animation.
            </li>
        </ul>
        <p>
            A quick note on using <code>translate</code>. Always use it to animate
            elements on X and Y axes instead of using positioning properties like
            left, right, top, bottom. Also try to use it instead of animating
            the <code>background-position</code> property. Using <code>translate</code>
            gives much smoother animations and uses less CPU. <a href="http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/">Paul</a>
            explains it better than I can.
        </p>
        <p>
            All of the animations have their <code>animation-direction</code> set
            to <code>alternate</code>. I did this so there would be no visible jumps
            back to the starting position of the properties. If you watch the
            sequence multiple times, you're likely to see the animations running
            in different directions.
        </p>
        <p>
            You can see the full source scss for the director card
            <a href="https://github.com/tylergaw/css-true-titles/blob/master/css/scss/partials/_card-director.scss">here</a>.
        </p>
        <h3>Pausing the animations</h3>
        <p>
            In the markup there is a class of "animated" applied to any animated
            element. I'm using that as way to pause and unpause all of the
            animations using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/animation-play-state"><code>animation-play-state</code></a>.
        </p>
        <pre><code>.paused .animated,
.paused .title-card .animated {
  -webkit-animation-play-state: paused;
  animation-play-state: paused;
}</code></pre>
        <p>
            There is some <a href="https://github.com/tylergaw/css-true-titles/blob/master/js/controls.js">light JavaScript</a> to allow the pause/play
            button to add and remove the class of "paused" from the
            body element, the CSS above takes care of the rest.
        </p>
        <h2>This was tough!</h2>
        <p>
            When I was working on this project, I realized there was a lot going
            on to make it happen, however I didn't realize how much I was on
            autopilot during the process. Sitting down and trying to explain—in
            words—what I did was difficult. If you read this far I hope there's a
            useful technique or trick that you picked up along the way. If not,
            I hope it was an interesting look into what goes on in my brain when
            building this type of thing.
        </p>
        <p>
            <i>Thanks for reading</i>
        </p>
            ]]>
        </description>
        <link>https://tylergaw.com/articles/css-true-titles</link>
        <guid>https://tylergaw.com/articles/css-true-titles</guid>
        <pubDate>Tue, 25 Feb 2014 19:30:00 EST</pubDate>
    </item>
    <item>
        <title>2000 Days</title>
        <dc:creator>Tyler Gaw</dc:creator>
        <description>
            <![CDATA[
            <p class="entry-intro">
            That’s roughly the number of days I was a member of the team at
            <a href="http://arc90.com">Arc90</a>. Five years, five months, and a couple
            weeks. Feb. 14th 2014 was my last day. Leaving was a difficult and
            emotional decision, but after months of mulling it over I decided it
            was the right one to make.
        </p>

        <p>
            Summer 2008. I was living in St. Louis, MO and working as a freelance
            designer/developer. For months I’d had a nagging feeling that I couldn’t
            articulate. The best I can describe it was a “pull” that was
            originating from my gut. The feeling was telling me there was
            something out there and I needed to go. The feeling didn’t bother to
            let on what was out there, or where “there” was. It wanted somewhere
            big though. Somewhere completely different from where I was then or
            where I had been before. Los Angeles? Nah, too glossy. New York City?
            Rough, the opposite of glossy. I’d only been to New York once before
            for a few days years earlier. At the time I described it as “a cool
            place to visit, but I couldn’t imagine living there.” The fact that
            I didn’t see New York as a place for me became a reason to go. This
            move wasn’t about comfort, it was about changing my life. Once I
            knew where “there” was I decided quickly, almost cavalierly. When my
            lease was up, I’d pack everything I owned into my Dodge Durango and
            move to a new city half way across the country.
        </p>

        <p>
            The freedom that comes with freelancing and working from home was
            great, but I needed something new. I needed to be around other
            designers and developers. I wanted to share the work I was doing
            every day and learn from people that loved the Web like I did.
        </p>
        <p>
            In my job search for “NYC Web designer”, “Front-end Web NYC”,
            “NYC Web PHP”, etc I
            came across this
            <a href="http://blog.arc90.com/2008/07/18/were-looking-for-the-lone-wolf-mcquade-of-php-development/">job post</a>.
            PHP was my language of choice in ‘08 and wow, what a great post. No
            long list of buzz word requirements, no arbitrary years of experience
            necessary. Just three lighthearted, pointed paragraphs. Who was this company?
            Who was behind it?
        </p>
        <p>
            Time has claimed some of the styles, but the Arc90 jobs page from
            Jul ‘08 is still available via the
            <a href="https://web.archive.org/web/20080715112955/http://arc90.com/jobs.php">Wayback Machine</a>.
            Here's a small portion:
        </p>
        <blockquote>
            <p>
                “...if you appreciate (and violently defend) standards-based
                technologies like <strong>XHTML</strong> and <strong>CSS</strong>...”
            </p>
        </blockquote>
        <p>
            It's easy to take for granted in 2014, but in 2008 an agency
            saying they wanted people that cared about Web Standards was
            not the norm. That description was speaking to me.
            It was saying all the things I wanted
            a company to say. That sealed the deal. Arc90 was where I needed to be. I
            hadn’t applied to anywhere else, and I wouldn’t. I wasn’t sure
            that my PHP skills were good enough to get hired for the developer
            position, but I was going for it anyway. I wanted to work for this company.
            I <em>had</em> to work for this company.
        </p>
        <p>
            When I got to New York on August 27th 2008 I sprinted to the Arc90
            office. I didn’t start work for a few more days, but I couldn’t wait
            to see the place and meet the people I’d only seen in
            <a href="http://www.flickr.com/search/?q=arc90">photos</a> up to
            that point. I was here. This place was real. I was a part of it.
        </p>
        <p>
            Five years didn’t just happen. Working for a company is a
            relationship. Relationships are hard and require work to maintain.
            Over the years there’ve been ups and downs, too much work and too
            little, I saw good people leave and great people join. There were
            times I thought I was ready to throw in the towel, but didn’t. I
            knew how special my relationship with Arc90 and the humans there was
            so I stuck with it.
        </p>
        <p>
            I wore a lot of hats. I designed a boatload of interfaces for
            insurance software. Designed and built sites and an app for a
            <a href="http://en.wikipedia.org/wiki/GradeGuru">failed education startup</a>.
            Designed and helped build <a href="http://donahueapp.com">an app</a>
            to experience conference talks in a novel way. I also made a couple
            brief design visits to <a href="http://www.kindlingapp.com">an app</a>
            that I saw grow from weekend project to successful business.
        </p>
        <p>
            The best work I’ve ever done I did for
            <a href="http://readability.com">Readability</a>. On Readability I
            was able to stretch my abilities in ways that I never imagined I
            would. My design and development skills were challenged and
            strengthened like never before. As the Product Lead I had the
            opportunity to shape the vision and direction of a product that I
            used and continue to use every day. I was able to try my hand at
            leading a team of incredible people to build incredible software.
            Working to make the lives of the people I worked with more enjoyable
            has been by far the most challenging thing I’ve done in my career.
        </p>
        <p>
            I’m forever grateful for Arc90, the people there today, and all the
            people that have come and gone. The opportunities it’s given me have altered
            my life for the better. At Arc I was able to be a designer, developer,
            writer, customer support, marketer, advocate, lobbyist, conflict
            mediator, and–when needed–ad hoc therapist.
        </p>
        <p>
            So why am I leaving now? It’s back. That feeling. That pull from my
            gut that I felt back in St. Louis in ‘08. It’s telling me it’s time
            to go again. Telling me something is out there. Telling me I need to
            go find it. Like before, it’s not doing me any favors of being
            specific about what “it” is. It’s not looking for a new “there” this
            time. It’s just looking for a “what”. It wants change. It didn’t
            let me down last time, so I’m not inclined to ignore it this time around.
        </p>
        <p>
            I’m not sure what’s next, and that feels OK. I don’t have a new job
            lined up. I don’t have any interviews or prospects. I’m on the hunt.
            I’m going to spend my next handful of a days looking for it.
            Meeting and talking with passionate people about what they’re working
            on and what’s out there. I’m going to spend my next few days searching
            for how I’ll spend my next 2000.
        </p>
        <p>
            The world is bigger than ever right now. I can't wait to see what I find.
        </p>
        <p>
            <i>Thanks for reading</i>
        </p>
            ]]>
        </description>
        <link>https://tylergaw.com/articles/2000-days</link>
        <guid>https://tylergaw.com/articles/2000-days</guid>
        <pubDate>Mon, 17 Feb 2014 17:00:00 EST</pubDate>
    </item>
    <item>
        <title>Papersaver</title>
        <dc:creator>Tyler Gaw</dc:creator>
        <description>
            <![CDATA[
            <figure>
            <a href="http://lab.tylergaw.com/papers">
                <img src="https://tylergaw.com/articles/assets/post-image-papersaver-intro.png"
                    alt="Papersaver text on a crappy Photoshopped background">
            </a>
        </figure>
        <p>
            <a href="http://www.fiftythree.com/paper">Paper</a> is an excellent
            product. Like all great products, it not only does a thing well, it
            also makes you want to do that thing more. Paper makes me want to
            draw more. After drawing with Paper for a few weeks, I wanted a
            quick way to share my drawings from Paper to the Web. I built
            <a href="https://github.com/tylergaw/papersaver">Papersaver</a> so I
            could get that done.
        </p>

        <h2>In Dependence</h2>
        <p>
            <a href="http://madewithpaper.fiftythree.com/">Made with Paper</a>
            is a showcase of what people are drawing with Paper. I went to
            submit one of my silly sketches to it and saw that they ask for an
            attribution link. They suggest–and most submissions are hosted
            on–Tumblr. Twitter and Facebook are also mentioned as hosting choices.
            I don’t care for that. I want the canonical location of my brain
            dumps to be on a server that I control. This is my natural tendency,
            but I was also inspired by <a href="http://adactio.com/journal/6620/">Jeremy</a>
            and <a href="http://frankchimero.com/blog/2013/12/homesteading-2014/">Frank’s</a>
            articles on the topic of pushing ourselves to take more control over
            our digital artifacts by self-hosting.
        </p>
        <figure>
            <img src="https://tylergaw.com/articles/assets/post-image-papersaver-attribution.jpg" alt="A portion of the Made with Paper submission form">
            <figcaption>
                Host my masterpieces on a Tumblr? Nope.
            </figcaption>
        </figure>
        <p>
            At this point it was clear that I had a fun side project in front of
            me. With a few days off work for the holidays, I started letting my
            brain run wild with possibilities. The plan was to build some type
            of site where I could publish my drawings while digging into a
            technology or two that was new to me.
        </p>

        <h2>Planning</h2>
        <p>
            I generally start a new project with a few bullet points for
            what I want, what I don’t, technologies I’ll use, etc. These points
            don’t always make it into the final project and some are just
            questions to think about during the process. Here’s a few I jotted
            down for Papersaver:
        </p>

        <h3>Project goals</h3>
        <ul>
            <li>Publish images from Paper to my personal Web site</li>
            <li>Do not rely on third-party platforms for storage</li>
            <li>“In Dependence”</li>
        </ul>
        <figure>
            <img src="https://tylergaw.com/articles/assets/post-image-papersaver-notes01.jpg" alt="Papersaver notes taken in the Paper App">
            <figcaption>
                I used Paper to take notes.
            </figcaption>
        </figure>
        <h3>What does it do?</h3>
        <ul>
            <li>Static HTML site</li>
            <li>Displays a list of images (maybe thumbnails + click to enlarge)</li>
            <li>Git repo backed up to Github</li>
            <li>Different input methods: email, Twitter, direct to repo</li>
            <li>generate(): image creation, HTML generation, Git commit, publish</li>
            <li>How can others use this?</li>
            <li>Permalinks to each item</li>
        </ul>
        <p>
            At this point I’ll also jot down some pseudo-code to start thinking
            about core functions of what I’ll be building. In this case I wanted
            to visualize what the main method of saving a Paper would look like:
        </p>
        <pre><code>newPaper() // takes img
// contents/papers/Date.getTime()
// &quot; &quot;/index.md
// date: D.getTime()
// template: paper.html
// image_path: tylergaw-paper-{date}.jpg
// save img: image_path.jpg</code></pre>
        <h2>Getting rolling</h2>
        <p>
            After a few hours of writing down goals and researching similar
            projects I had a good idea of what I would build and the tools I
            would use to build it. I needed a way to get my drawings out of Paper.
            The App allows drawings to be shared to Tumblr, Facebook, Twitter,
            Camera Roll, and Email. The first three were out
            since they’re third-parties. I entertained the idea of using a
            combination of export to cameral roll and Dropbox to publish, but
            went away from it because it didn’t feel flexible enough.
            I’d be going the route of publishing via email.
        </p>

        <h2>How I use Papersaver</h2>
        <ul>
            <li>
                Draw in Paper
            </li>
            <li>
                Email each drawing I want published to a dedicated email address
            </li>
            <li>
                A Node.js <a href="https://github.com/tylergaw/papersaver/blob/master/papersaver-mail-listener.js">script</a>
                listens for new email at the dedicated email address
            </li>
            <li>
                When a new email arrives, the script grabs the attached
                image and hands it off to
                <a href="https://github.com/tylergaw/papersaver/blob/master/papersaver.js">Papersaver.js</a>
            </li>
            <li>
                Papersaver.js creates the necessary directories and files
                for Node.js static site generator, <a href="https://github.com/jnordberg/wintersmith">Wintersmith</a>
            </li>
            <li>
                Wintersmith regenerates the site with the new drawings
            </li>
            <li>
                The newly added and created files are commited to the Git
                repo and those commits are pushed to the remote repo at Github
            </li>
            <li>
                The drawings live at <a href="http://lab.tylergaw.com/papers">http://lab.tylergaw.com/papers</a>
            </li>
        </ul>
        <h2>The pieces</h2>
        <p>
            Papersaver is a fairly simple hack, but there are a few interesting
            parts worth talking about in more detail.
        </p>
        <h3>Good enough email security</h3>
        <p>
            I'm using a <a href="https://github.com/chirag04/mail-listener2">node package</a>
            to access Papersaver emails over IMAP. I don't want to leave the
            security of my primary email address in the hands of my programming
            skills so I use a dedicated email address–tylergaw.papersaver@gmail.com–for
            Papersaver.
        </p>
        <p>
            I just gave you the email address. So what's to keep you from
            flooding my Papersaver with all the images you please? (aside from your trustworthy nature)
            I use a settings file with to and from whitelists to only allow emails
            to/from certain addresses. Nothing good would come from me adding
            my actual settings to the public Github repo, but I do include a
            <a href="https://github.com/tylergaw/papersaver/blob/master/mail-settings.sample.json">sample settings file</a>.
            The from address is really easy to fake, but the to address is a pretty
            decent way to lock things down.
        </p>
        <p>
            I use a "+slug" on the email address that I don't publish anywhere.
            Fake example; tylergaw.papersaver+71ksk27817@gmail.com. I put that address
            in my <code>toWhitelist</code> settings. The <a href="https://github.com/tylergaw/papersaver/blob/master/papersaver-mail-listener.js#L69">mail listener script</a>
            checks to make sure any emails received are to an address in the whitelist,
            if not, it bails. Fort Knox an email slug don't make, but for this
            little project it gets the job done.
        </p>
        <h3>Papersaver Command-line interface</h3>
        <p>
            While I was working on Papersaver, I didn't want to have to send an
            email each time I needed to test that the saving code was working.
            To make things faster I wrote a <a href="https://github.com/tylergaw/papersaver/blob/master/papersaver-cli.js">simple CLI</a>
            that allows me to create new Papers from images on my machine. I used
            the super-cool node package <a href="https://github.com/substack/node-optimist">Optimist</a>
            to handle the arguments I wanted to provide. The Papersaver CLI isn't
            any type of technological achievement, but it was the first time I've
            written something like it, so it gets a mention here.
        </p>
        <h3>Static site generator</h3>
        <p>
            Since I decided to go the Node.js route for this project, I wanted
            all the pieces to be written in Node.js. <a href="http://wintersmith.io/">Wintersmith</a>
            is a static site generator written in Node.js. These types of static generators
            have been <a href="http://davidtucker.net/articles/introduction-to-wintersmith/">written about at length</a>
            so I don't have anything more to add.
            I'll just say that Wintersmith gets the job done and if I need a
            generator written in Node.js, it will be my go-to.
        </p>
        <h2>Steal this project!</h2>
        <p>
            I didn't build this project just for me. My hope is that other folks
            that use Paper will find it interesting and start self-hosting the
            notes, drawings, and sketches they create. Papersaver isn't yet a
            turnkey project. I had that in mind from the start, but it will require
            more work to get it there. In the meantime, I'd love to see forks of
            it that remedy some of the issues that are currently making it not
            ready for quick re-use. I'm also 100% open to pull requests, so
            <a href="https://github.com/tylergaw/papersaver">fork it!</a>
        </p>
        <p>
            <i>Thanks for reading</i>
        </p>
            ]]>
        </description>
        <link>https://tylergaw.com/articles/papersaver</link>
        <guid>https://tylergaw.com/articles/papersaver</guid>
        <pubDate>Mon, 03 Feb 2014 22:00:00 EST</pubDate>
    </item>
    <item>
        <title>Slenderman</title>
        <dc:creator>Tyler Gaw</dc:creator>
        <description>
            <![CDATA[
                <figure>
                    <a href="http://lab.tylergaw.com/slenderman">
                        <img src="https://tylergaw.com/articles/assets/post-image-slenderman-intro.png" alt="Slenderman in use">
                    </a>
                </figure>
                <p class="entry-intro">
                    Fonts have always felt like a black box to me. The thought of creating
                    a new one seemed like a task reserved for a select few with decades
                    of experience, a deep knowledge of typography, and cumbersome,
                    expensive software. Not me, I thought. I proved myself wrong by
                    making my first font,
                    <a href="http://lab.tylergaw.com/slenderman">Slenderman</a>.
                </p>
                <h2>Made to Scratch an Itch</h2>
                <p>
                    I’m working on a small design project that felt like it needed hand drawn type.
                    I could have gone the route of using images, but
                    that just doesn’t feel right on the Web these days.
                    I want my web type to be scalable and maintainable. Using images for
                    type is neither of those. It was clear that I’d need to either find an
                    existing font that fit the style I wanted, or create my own.
                </p>
                <p>
                    I didn’t look around for existing fonts that fit. I jumped straight
                    to creating my own. The project is a personal one so there’s no
                    time limit. When I have that choice I love to let projects expand to
                    as many different areas as they need to. This same project also led
                    me down the path of building <a href="https://tylergaw.com/articles/webvtt-cuemarker">another small tool</a>
                    for working with HTML video subtitles.
                </p>
                <h2>The Process</h2>
                <p>
                    I knew how I wanted the capital letters to look from the outset. I
                    started by just drawing them out on paper. My first thought for the
                    lowercase letters was to make them not match the style of the caps,
                    but to be a complement to them. I though a cursive handwriting style
                    would be a nice contrast to the tall, skinny caps.
                </p>
                <figure>
                    <img src="https://tylergaw.com/articles/assets/post-image-slenderman-sketch01.jpg" alt="Initial pen and paper sketches of Slenderman">
                    <figcaption>Capital and unused lowercase letter sketches</figcaption>
                </figure>
                <p>
                    After drawing the letters, I scanned them and used Illustrator’s
                    Live Trace to convert them to vectors. The cursive lowercase letters didn’t
                    convert well. They lost a lot of detail, and just kind of turned
                    into muck. It was clear they weren’t going to work out so I ditched them.
                </p>
                <p>
                    I went back to the pen and paper and drew a new set of lowercase
                    letters as well as the numbers, punctuation, and assorted symbols.
                    You can see in the drawing that the “@” and “&” are tough to decide on.
                    I followed the same scan then Live Trace with these.
                </p>
                <figure>
                    <img src="https://tylergaw.com/articles/assets/post-image-slenderman-sketch02.jpg" alt="Initial pen and paper sketches of Slenderman">
                    <figcaption>Numbers, punctuation, symbols, and two sets of lowercase letter sketches</figcaption>
                </figure>
                <p>
                    With all the characters drawn, scanned, and converted to vector the next
                    step was to start refining them. All the charaters needed to have similar
                    stroke weights, similar widths and heights, and a handful of other
                    defining characteristics to make them feel like a family.
                </p>
                <p>
                    I started with the capital letters. They converted to vector really well.
                    For them I mostly just needed to make sure each letter was the
                    correct width and height. I also needed to remove extra, uneeded
                    points that Live Trace had added during conversion.
                </p>
                <figure>
                    <img src="https://tylergaw.com/articles/assets/post-image-slenderman-vector-caps.jpg" alt="A few Slenderman capital letters in Illustrator">
                    <figcaption>Working with the capital letters in Illustrator</figcaption>
                </figure>
                <p>
                    While I was refining each capital letter, I was bringing each into
                    <a href="http://glyphsapp.com/glyphs-mini/">Glyphs Mini</a>, my font
                    editor of choice. It allows vectors to be pasted in from Illustrator.
                    Once the vector is in Glyphs I could set the position and spacing.
                    Glyphs also allows characters to be typed out in
                    the edit tab to get a real feel for how each one interacts
                    with the others. This allows for creating kerning pairs of characters.
                </p>
                <figure>
                    <img src="https://tylergaw.com/articles/assets/post-image-slenderman-glyphs01.jpg" alt="Editing Slenderman glyphs using Glyphs Mini">
                    <figcaption>Glyphs Mini edit tab</figcaption>
                </figure>
                <p>
                    The refining process for the lowercase letters took a lot more work
                    than the capitals. For each of the 26 letters, I needed to make the strokes narrower,
                    and for a lot of them completely change the shape from what I had
                    drawn. This involved manually moving the points that made up each
                    letter. It felt a lot like the original vectors were chunks of
                    rough granite that I needed to chisel down into their final forms.
                    As you can imagine, it’s a long, slow process.
                </p>
                <p>
                    I followed the same process for the punctuation, numbers, and symbols.
                    While I was refining the numbers, I did get a little overzealous. I
                    ended up chipping away a bit too much off each one, leaving them very
                    skinny. When they were next to other characters it looked like they
                    were from a different family. This required me to go back and make
                    each number a bit heavier. Lesson learned; compare each character to
                    as many others as you can while you’re tweaking the vectors.
                </p>
                <figure>
                    <img src="https://tylergaw.com/articles/assets/post-image-slenderman-glyphs02.jpg" alt="A view of most of the glyphs of Slenderman in Glyphs Mini">
                    <figcaption>A bird’s-eye view of Slenderman</figcaption>
                </figure>
                <p>
                    Glyphs Mini allows for exporting to the OpenType format. While I was adding characters
                    I wanted to get a better feel for how they were working. I would export
                    the font, install it and then open up a test Photoshop document. Seeing
                    full sentences and different combinations of letters, numbers, and symbols
                    is really key to determining if things are working. I could better
                    see any issues with spacing and sizing and pick out pairs of
                    characters that could use attention to kerning. Also, it’s a really
                    great feeling to be able to use a font after spending so much time
                    working with it.
                </p>
                <figure>
                    <img src="https://tylergaw.com/articles/assets/post-image-slenderman-tester-psd.jpg" alt="Testing Slenderman in Photoshop">
                    <figcaption>Testing Slenderman in Photoshop</figcaption>
                </figure>

                <h2>Detail of a Gotcha</h2>
                <p>
                    This is very specific to users of Glyphs Mini, but I spent a few hours
                    banging my head against the desk before I figured it out
                    so I’m going to write about it in the hopes that I’ll save some
                    other poor soul’s forehead.
                </p>
                <p>
                    Glyphs has components. A component is a Glyph that is made
                    up of a base glyph. Example components are right and left double quotes
                    and the ellipsis; made up of right and left single quotes and the period,
                    respectively. When you open a component to edit, the base
                    glyph is grayed out, it seems like it is uneditable. It also only shows
                    the base glyph. In the case of double quotes only a single right
                    or left quote is shown. For the ellipsis only a single period. For some reason
                    Glyphs stacks the base glyphs on top of each other. The base
                    glyph needs to be clicked and dragged to reveal the other base glyph(s)
                    under it. I found this answer on the
                    <a href="http://glyphsapp.com/forums/index.php?action=vthread&forum=1&topic=282">Glyphs forum</a>.
                </p>
                <p>
                    Part two of the gotcha took longer to figure out. After I’d separated
                    the base glyphs of the compenents, I spaced them, tested them with
                    other characters in the edit tab, and exported like normal.
                    Then I tried to use right and left double quotes and the ellipsis. Nothing
                    would show up. I could tell the glyph exported because I didn’t get
                    the usual missing glyph character and neither Photoshop nor TextEdit
                    tried to replace it with another font. This seemed like one of those
                    what in the hell?!/computer voodoo moments.
                </p>
                <p>
                    <b>Cmd + Shift + D</b> was the magic key combo that ended my confusion.
                    Components need to be Decomposed before they can be exported properly.
                    The option is available under the Glyph menu > Decompose Components.
                    Once a component is decomposed it is no longer tied to a base glyph
                    so it can be edited like any other glyph and it exports normally.
                </p>

                <h2>Slenderman is Open Source</h2>
                <p>
                    I’ve always felt like when I download a font that’s that. The font is
                    done and nothing can be added or modified. This might not be the
                    reality, but without a knowledge of how fonts are
                    made, that’s how I’ve seen things. With Slenderman, I’m not aiming for
                    it being “done”. I want to keep iterating and improving on it.
                    All of the source files are available on
                    <a href="https://github.com/tylergaw/slenderman">GitHub</a>. That means
                    anybody can fork the repo and make any changes or additions needed.
                    They can make new versions of it that fit their needs, or open Pull
                    Requests to have their changes added into the main fork of Slenderman.
                    I haven’t seen that before and I’m interested in seeing if anyone
                    is interested in working on it.
                </p>

                <h2>What Did I Learn?</h2>
                <p>
                    Slenderman does not make me a typographer by any means. It did open up
                    the possibilty though. Working on it has given me a greater
                    appreciation for the skill and patience required to create the
                    beautiful type that we use and often take for granted.
                </p>
                <p>
                    <i>Thanks for reading</i>
                </p>
            ]]>
        </description>
        <link>https://tylergaw.com/articles/slenderman</link>
        <guid>https://tylergaw.com/articles/slenderman</guid>
        <pubDate>Wed, 03 Jul 2013 12:00:00 EST</pubDate>
    </item>
    <item>
        <title>WebVTT Cuemarker</title>
        <dc:creator>Tyler Gaw</dc:creator>
        <description>
            <![CDATA[
            <p>
            <a href="http://dev.w3.org/html5/webvtt/">WebVTT</a> is a text format
            that can be used to provide captions and subtitles for HTML video.
            While working on a project that uses it I found myself needing a
            way to find and mark precise cue in/out times.
            <a href="https://github.com/tylergaw/webvtt-cuemarker">webvtt-cuemarker</a>
            is a small tool I whipped up in JavaScript to do just that.
        </p>
        <h2>A WebVTT Primer</h2>
        <p>
            The format is still fairly new and not yet widely supported, so I'll give
            a quick example of how it's used.
        </p>
        <pre><code>WEBVTT FILE

00:00:01.265 --> 00:00:06.210
This is text that will appear over the video at during the in/out points.

00:00:05.500 --> 00:00:10.250
You can use <b>HTML</b> within WebVTT, pretty cool.
</code></pre>
        <p>
            That's an example of a very simple WebVTT file with two cues. We'll
            refer to it as <code>captions.vtt</code>
        </p>
        <p>
            The next thing to do is to let your HTML video know about the WebVTT file.
            That's done using the <code>track</code> element.
        </p>
        <pre><code>&lt;video controls&gt;
    &lt;source src="/path/to/video.mp4" type="video/mp4"&gt;
    &lt;source src="/path/to/video.webm" type="video/webm"&gt;
    &lt;track kind="captions" label="English captions"
        src="/path/to/captions.vtt" srclang="en" default>&lt;/track&gt;
&lt;/video&gt;
</code></pre>
        <p>
            That's all there is to it. If we played the fictional video above
            we would see the two captions appear at the times provided in <code>captions.vtt</code>.
            For a more in-depth look at the <code>track</code> element I suggest
            <a href="http://www.html5rocks.com/en/tutorials/track/basics/">Getting Started With the Track Element</a>
            on HTML5 Rocks. That's where I first learned about all this goodness.
        </p>
        <h2>Why Is Cuemarker Needed?</h2>
        <p>
            WebVTT is very specific about the time format for cues. Cue times must
            be represented exactly as shown in <code>captions.vtt</code>. It's
            easy to type out the times in that way, but how do you find
            precise times using HTML video? HTML video controls display the time
            of the video, but only down to the second. And not in the format needed
            for WebVTT. This is why I wrote Cuemarker.
        </p>
        <p>
            Cuemarker provides a keyboard shortcut–the period key–for setting in/out
            points and outputting the cue time in the required format.
        </p>
        <p>
            Cuemarker also provides shortcuts to interact with HTML
            video. You can play/pause using the space bar and
            seek forward/backward using the right/left arrow keys. Those controls
            allow you to arrive at more precise moments in video than you can
            using the default video scrubber provided by the browser.
        </p>
        <h2>How Do I Use It?</h2>
        <p>
            I'll go back to the HTML in the previous example, but I'm going to
            remove the <code>track</code> element. For marking cue times you only
            need an HTML video.
        </p>
        <pre><code>&lt;video controls&gt;
    &lt;source src="/path/to/video.mp4" type="video/mp4"&gt;
    &lt;source src="/path/to/video.webm" type="video/webm"&gt;
&lt;/video&gt;
</code></pre>
        <p>
            Once your HTML is in place, you'll need a bit of JavaScript.
            Cuemarker creates a variable–<code>cuemarker</code>–in the global scope.
            <code>cuemarker</code> is a function that takes one required parameter;
            the video element. It also takes a second, optional parameter to
            specify a seek interval and output function.
        </p>
        <pre><code>cuemarker(document.querySelector('video'));
</code></pre>
        <p>
            That's a bare-bones example and may be all that you'll need. The cue
            times will be output using <code>console.log</code>.
        </p>
        <p>
            Here's another example using both the available options.
        </p>
        <pre><code>cuemarker(document.querySelector('video'), {
  seekInterval: 0.5 //default is 0.03,
  output: function (cuetime) {
    var times = document.getElementById('cuetimes'),
        li = document.createElement('li');
      li.innerHTML = cuetime;
      times.appendChild(li);
    }
});
</code></pre>
        <p>
            In that example, the seek interval is less precise than the default
            and the cue times will be injected into the page as <code>li</code>
            elements that are children of the <code>ul</code> with an id of "cuetimes".
        </p>
        <figure>
            <img src='https://tylergaw.com/articles/assets/post-image-cuemarker-output.jpg' alt='Screenshot of Cuemarker output'>
            <figcaption>Cuemarker output</figcaption>
        </figure>
        <p>
            Cuemarker doesn't try to get cute by attempting to transfer cue times to
            a WebVTT file. All it's interested in doing is outputting the
            in/out times. Once you've marked in/out times you
            can copy them over to your WebVTT files. That's how I've been using
            it and it's working really well for me.
        </p>
        <p>
            A working demo of the latter example is located
            <a href="http://tylergaw.github.io/webvtt-cuemarker/demo.html">here</a>.
        </p>
        <h2>That's It?</h2>
        <p>
            Yep, that's what I got for now. Again, the <code>track</code> element
            and WebVTT are not yet widely supported. As of this writing only the
            latest Chrome and Opera are cool enough to do so. The Web moves fast though and
            I know it won't be long before Firefox and Safari follow suit.
        </p>
        <p>
            Limited support for the underlying technology means that this is a
            tool you may not need today, but you might tomorrow.
            <a href="https://github.com/tylergaw/webvtt-cuemarker">Fork it!</a>
        </p>
        <p>
            <i>Thanks for reading</i>
        </p>
            ]]>
        </description>
        <link>https://tylergaw.com/articles/webvtt-cuemarker</link>
        <guid>https://tylergaw.com/articles/webvtt-cuemarker</guid>
        <pubDate>Sat, 08 Jun 2013 20:00:00 EST</pubDate>
    </item>
    <item>
        <title>Get Together. Get Excited. Get to Work.</title>
        <dc:creator>Tyler Gaw</dc:creator>
        <description>
            <![CDATA[
            <figure>
                <a href="http://brooklynalpha.org/2012">
                    <img src='https://tylergaw.com/articles/assets/post-image-brooklyn-alpha-2012.png' alt='Brooklyn Alpha 2012'>
                </a>
            </figure>
            <p>
                <b>Brass tacks:</b> <em>
                    <a href="http://brooklynalpha.org/2012">
                        Come to my build-a-thon, make cool shit!
                    </a>.
                </em>
            </p>
            <p class="entry-intro">
                If you haven't heard, Brooklyn is <b>the</b> place to be the
                second week of October 2012. That's the week that the Internet
                is getting together for a week of conferences, meet ups, drink-ups,
                shinanagans, and the friendliest little web build-a-thon that could.
                It just so happens that that build-a-thon is being organized and
                hosted by myself and <a href="https://twitter.com/xiian">Tom Sartain</a>.
                We'd love for you to join us!
            </p>
            <h2>What's this &#8220;build-a-thon&#8221;?</h2>
            <p>
                Alright, here's the deal. If you're a designer or a developer I
                know you have at least one project in your mind right now
                that you want to work on. And there's a good chance you've
                been thinking about working on this project for a while and for
                one reason or another haven't been able to dive in and get it done.
            </p>
            <p>
                Brooklyn Alpha is a perfect excuse to give yourself three days
                to put your idea into motion. Not only will you have three days, you'll
                have access to other motivated folks who may have complementary
                skills. Maybe you're a designer who needs a Python developer. Or
                you're Mr. Django himself and you need a designer to help
                make your project easy to use and easy on the eyes. Or maybe you're someone
                who has a head full of great ideas and you're looking for
                designers and developers to lend a hand in materializing all that
                greatness coming from your grey matter. Whatever your situation
                we'll have you covered.
            </p>
            <h2>&#8220;I don't have any ideas&#8221;</h2>
            <p>
                OK, OK. The brain well done gone dry. It happens to the best of us.
                Can you sling hot pixels or code? Of course you can. Then you should
                come to Brooklyn Alpha. Here's why. Last year when we opened up
                the floor to anyone to get up and pitch ideas, nearly every
                single one of our incredible attendees got up and did just that.
                And more than a few got up a second or third time. There will be
                no shortage of excellent ideas at Alpha and many of them will need
                your brain to get them moving.
            </p>
            <h2>OK, cool. What else?</h2>
            <p>
                I'll tell you what else. You get to be a part of something that's
                going to be huge. Brooklyn Alpha is sharing the week with the
                legendary <a href="https://brooklynbeta.org/">Brooklyn Beta</a>
                and we're an official part of <a href="https://brooklynbeta.org/beyond">Beyond Beta</a>.
                If you haven't heard of those stop reading this now and go find out
                about them (then hurry back and pick up your ticket to Alpha).
            </p>
            <p>
                Brooklyn Alpha will be taking place during the day each of its
                three days. That leaves the evenings free for maximum mingling
                with a whole host of excellent web folks that will for sure be
                inhabiting our incredible Borough of Brooklyn during the week.
            </p>
            <h2>Hope to see you there!</h2>
            <p>
                This is our second year hosting Brooklyn Alpha. It's a challenging,
                fun, and rewarding task to take on. We're doing this because
                we love the work we do, we love the city we live in, and we love
                the web community that makes events like this possible. We're
                honored to be able to surround ourselves with so many people that
                share our passions.
            </p>
            <figure>
                <a href="http://www.flickr.com/photos/brooklynalpha/6251904083/in/photostream">
                    <img src='http://farm7.staticflickr.com/6042/6251904083_1347d7870f_b.jpg' alt='Brooklyn Alpha 2011'>
                </a>
                <figcaption>Brooklyn Alpha 2011 Group Photo</figcaption>
            </figure>
            <p>
                <i>Thanks for reading</i>
            </p>
            ]]>
        </description>
        <link>https://tylergaw.com/articles/get-together-get-excited-get-to-work</link>
        <guid>https://tylergaw.com/articles/get-together-get-excited-get-to-work</guid>
        <pubDate>Mon, 24 Sep 2012 11:30:00 EST</pubDate>
    </item>
  <item>
    <title>Reacting to Media Queries in Javascript</title>
    <dc:creator>Tyler Gaw</dc:creator>
    <description>
        <![CDATA[
            <p>
                <b>Brass tacks:</b> <em><a href="http://tylergaw.github.com/media-query-events/">A demo</a> and <a href="https://github.com/tylergaw/media-query-events">some code</a>.</em>
            </p>
            <p>
                <code>window.matchMedia</code> provides a way for Javascript to react when a media query condition is met or unmet. While the functionality it allows is great, the necessary code duplication required to use it leaves a bit to be desired. I'm going to walk through a work-in-progress approach to getting around that duplication.
            </p>
            <h2>window.matchMedia</h2>
            <p>
                The method is simple enough to use and works the way you'd expect it. You give it a media query string it gives you back a <a href="https://developer.mozilla.org/en/DOM/MediaQueryList">MediaQueryList</a> object.
            </p>
            <pre><code>var mql = window.matchMedia("(min-width: 480px)");</code></pre>
            <p>
                That sets the value of <code>mql</code> to a MediaQueryList object with two members, something like:
            </p>
            <pre><code>MediaQueryList: {
  matches: true,
  media: "(min-width: 480px)"
}</code></pre>
            <p>
                The boolean value of the <code>matches</code> member will be determined by the width of your browser window at the time.
            </p>
            <p>
                You can add event listeners to MediaQueryList objects. An event will fire each time the condition is triggered. This allows you to be updated on the status of the media query without having to resort to polling or a <code>window.resize</code> event. Using <code>mql</code> from above we can set up a listener and handler like so:
            </p>
            <pre><code>mql.addListener(handleMediaChange);
handleMediaChange(mql);

var handleMediaChange = function (mediaQueryList) {
  if (mediaQueryList.matches) {
    // The browser window is at least 480px wide
  }
  else {
    // The browser window is less than 480px wide
  }
}</code></pre>
            <p>
                You can find similar code examples and further explanation of MatchMedia on the <a href="https://developer.mozilla.org/en/DOM/window.matchMedia">Mozilla Developer Network</a>
            </p>
            <p>
                MatchMedia works pretty well. As usual there are some caveats that I'll mention later. My issue is with needing to specify the media query when you create the MediaQueryList. If you wanted an event to fire for every media query you might have, you'd have manually copy each from your stylesheets to you JS. Every time you update a media query in CSS, you'd have to do the same in JS. What I want is a script to look at a page's stylesheets, pick out all the media queries and create a MediaQueryList for each one.
            </p>
            <h2>
                Getting Media Queries from CSS to JS
            </h2>
            <p>
                When I first started looking into this I thought I was in for some really hairy stuff. I imagined myself having to make ajax requests to fetch stylesheets, use weird regexes to find the <code>@media</code> rules, and employ other types of not-so-fun things. Luckily, this did not turn out to be the case.
            </p>
            <p>
                I've written small script that accomplishes the tasks I'm after, <a href="https://github.com/tylergaw/media-query-events/blob/master/js/mq-events.js">mqEvents.js is on Github</a> and a working example is at <a href="http://tylergaw.github.com/media-query-events/">tylergaw.github.com/media-query-events</a>.
            </p>
            <pre><code>(function () {
  var mqEvents = function (mediaChangeHandler) {
    var sheets = document.styleSheets,
        numSheets = sheets.length,
        mqls = {},
        mediaChange = function (mql) {
            console.log(mql);
        }

    if (mediaChangeHandler) {
      mediaChange = mediaChangeHandler;
    }

    for (var i = 0; i < numSheets; i += 1) {
      var rules = sheets[i].cssRules,
          numRules = rules.length;

      for (var j = 0; j < numRules; j += 1) {
        if (rules[j].constructor === CSSMediaRule) {
          mqls['mql' + j] = window.matchMedia(rules[j].media.mediaText);
          mqls['mql' + j].addListener(mediaChange);
          mediaChange(mqls['mql' + j]);
        }
      }
    }
  }

  window.mqEvents = mqEvents;
}());</code></pre>
            <p>
                I'm going to go through the code here and explain what's happening each step of the way.
            </p>
            <pre><code>var mqEvents = function (mediaChangeHandler)</code></pre>
            <p>
                The <code>mqEvents</code> function takes a single parameter, a function that will be called each time a media query is triggered.
            </p>
            <pre><code>var sheets = document.styleSheets,
    numSheets = sheets.length</code></pre>
            <p>
                The document contains an object of all loaded stylesheets. Our <code>sheets</code> variable is a list of <a href="https://developer.mozilla.org/en/DOM/stylesheet">StyleSheet</a> objects. <code>numSheets</code> is stored for convenience for when we loop over the list of stylesheets.
            </p>
            <pre><code>mediaChange = function (mql) {
  console.log(mql);
}

if (mediaChangeHandler) {
  mediaChange = mediaChangeHandler;
}
</code></pre>
            <p>
                If the <code>mediaChangeHandler</code> argument is not passed to <code>mqEvents</code>, a default function, <code>mediaChange</code> will handle each media query event. The default doesn't do much of anything. For the purpose of this script we just want to have something there.
            </p>
            <pre><code>for (var i = 0; i < numSheets; i += 1) {
  var rules = sheets[i].cssRules,
      numRules = rules.length;</code></pre>
            <p>
                Here we're looping over our <code>sheets</code> list to look at each loaded stylesheet. The <code>rules</code> variable is list of all the rules of the current stylesheet represented as <a href="https://developer.mozilla.org/en/DOM/cssRule">CSSRule</a> objects. <br><em>This is where things take a turn for the awesome.</em>
            </p>
            <p>
                At the start of this I was aware that all a document's stylesheets could be accessed and that all the rules of the stylesheets were represented by CSSRule objects, but what I didn't know was that CSSRule objects that contain a media query have a unique name. Take a look at this screen shot of the console when logging out each CSSRule object:
            </p>
            <figure>
              <img src='https://tylergaw.com/articles/assets/mqevents-cssrules-in-console.jpg' alt='Screenshot of the Chrome developer console showing a number of CSSRule objects being logged.'>
              <figcaption>Logging out each CSSRule object reveals a unique name for media query rules. Badass.</figcaption>
            </figure>
            <p>
                Regular CSS rules have the name "CSSStyleRule" while media queries have the name "CSSMediaRule". This is great because it gives us an easy way to pluck out only the media queries from our stylesheets without needing to resort to string parsing, which can get ugly quickly.
            </p>
            <p>
                (It looks like other types of rules like font-face and keyframes have <a href="https://developer.mozilla.org/en/DOM/cssRule#section_2">unique names too</a>.)
            </p>
            <pre><code>for (var j = 0; j < numRules; j += 1) {
  if (rules[j].constructor === CSSMediaRule)</code></pre>
            <p>
                We can now loop over each rule in the stylesheet and check to see if it is a media query. The condition here, checking to see if the constructor matches the name "CSSMediaRule", was also new to me. I found that approach in a thorough <a href="http://stackoverflow.com/a/332429/368634">Stack Overflow answer</a> on the topic.
            </p>
            <pre><code>mqls['mql' + j] = window.matchMedia(rules[j].media.mediaText);
mqls['mql' + j].addListener(mediaChange);
mediaChange(mqls['mql' + j]);</code></pre>
            <p>
                Now that we know we're only dealing with media queries, we're free to use them with matchMedia to create MediaQueryList objects, bind events to those and handle them with a given handler function. This bit of code is nearly the same as the matchMedia example. The noticeable exception here is that each MediaQueryList object is being added to a hash, <code>mqls</code>, that we created earlier. We could accomplish the same thing without putting each object in the hash, this was more of a forward-thinking thing. I have a feeling that there could be a use for holding on to all of the objects to access them later.
            </p>
            <h2>Usage</h2>
            <p>
                So why would you use this and what happens when you do? In the demo I linked to, this is the implementation:
            </p>
            <pre><code>var msg = document.getElementById('condition'),
    handleMediaChange = function (mql) {

  // For some reason Firefox has trouble always running this code.
  // The console.log seems to help it.
  // TODO: Figure out what the hell that's all about
  console.log();

  if (mql.matches) {
    msg.setAttribute('class', 'met');
    msg.innerHTML = 'The condition "' + mql.media + '" was met.';
  }
  else {
    msg.setAttribute('class', 'unmet');
    msg.innerHTML = 'The condition "' + mql.media + '" was not met.';
  }
};

mqEvents(handleMediaChange);</code></pre>
            <p>
                Notice the big comment about Firefox there, I still don't know what that is. Like I said, work-in-progress here.
            </p>
            <p>
                What this does is update a the DOM element, represented by <code>msg</code>, each time a media query is triggered. <code>mqEvents</code> doesn't try to react differently to specific media queries. It calls the same handler each time one is triggered. The handler function, however, does receive information about the media query. It knows what the media query is–which I'm placing in the DOM with <code>mql.media</code>–and if the condition was met by the window–which I'm checking with <code>mql.matches</code>.
            </p>
            <h2>What Else Can This Be Used For?</h2>
            <p>
                Maybe parts of the page layout are done with Javascript and need to be updated each time a media query is triggered. Maybe it could be used to do conditional loading of images, scripts, fonts, or the like. This type of conditional loading of assets could be used to help reduce the bandwidth usage on mobile devices.
            </p>
            <h2>Concerns and Caveats</h2>
            <p>
                If a page has a lot of media queries, <code>mqEvents</code> is going to add a lot of event listeners and it's going to be calling the handling function a lot of times. I haven't run into anything problematic with my small demo page, but I'd be curious to see the impacts on performance on a page with a substantial number of media queries.
            </p>
            <p>
                With all these fancy new things browser support is a big question. <code>matchMedia</code> is not supported very well yet. According to <a href="http://caniuse.com/#search=matchMedia">caniuse.com</a> we're looking at Chrome 17+, Firefox 9+, Safari 5.1+, iOS Safari 5.0+, IE 10+ (with an ms prefix), and no support yet for Opera. For unsupported browsers a polyfill could be employed. Here's a good one right <a href="https://github.com/paulirish/matchMedia.js/">here</a>.
            </p>
            <h2>Other Cool Stuff</h2>
            <p>
                As I was working on this I found out about a related project named <a href="http://harvesthq.github.com/harvey">Harvey</a>. I haven't used it yet, but it looks really good. It looks like it still has that duplication of media queries issue, but if you're looking for targeted reactions to specific media queries it might be the way to go.
            </p>
            <p>
              <i>Thanks for reading</i>
            </p>
        ]]>
    </description>
    <link>https://tylergaw.com/articles/reacting-to-media-queries-in-javascript</link>
    <guid>https://tylergaw.com/articles/reacting-to-media-queries-in-javascript</guid>
    <pubDate>Thu, 17 May 2012 07:15:35 EST</pubDate>
  </item>
  <item>
    <title>Fun with HTML Form Validation Styles</title>
    <dc:creator>Tyler Gaw</dc:creator>
    <description>
      <![CDATA[
        <p>
        	<b>Brass tacks:</b> <em>Demos; <a href="http://lab.tylergaw.com/html5forms/errorStyles/01/">Style 01</a>, <a href="http://lab.tylergaw.com/html5forms/errorStyles/02/">Style 02</a>, <a href="http://lab.tylergaw.com/html5forms/errorStyles/03/">Style 03</a> and <a href="https://github.com/tylergaw/html5forms/tree/master/errorStyles">the code</a>.</em>
        </p>
        <p class="entry-intro">
        	If you haven't heard already, the current HTML Living Standard or HTML5 spec has in it some super cool form validation. This is client-side form validation that is meant to remove some of the burden of writing validation with Javascript. If you've ever written form validation with Javascript I think you'll agree that this is a very good thing.
        </p>
        <p>
          Using this new feature of html can be as easy as creating a form and within it creating an input with a <code>required</code> attribute. When the form is submitted, browsers that support validation will take the reigns and make sure that the required input has a value. If it does not, the browser will display a validation error message.
        </p>
        <p>
          In addition to validating required fields, the form validation will also check to make sure inputted values match given input types. New in the Living Standard are quite a few new input types such as; "email", "url", "number" and so on. Check out the full list located at <a href="http://developers.whatwg.org/the-input-element.html#the-input-element">http://developers.whatwg.org/the-input-element.html#the-input-element</a>.
        </p>
        <h2>Validation error messages</h2>
        <p>
          As of this writing, there are three browsers that both support html form validation and display error messages when needed. Browser support includes; Chrome 10+, Firefox 3.6+, and Opera 11+. Safari doesn't care about validating your forms and I haven't checked any versions of IE, maybe 9 or 10 support this? Each browser handles the styling of validation messages differently.
        </p>
        <figure>
          <img src='https://tylergaw.com/articles/assets/post_image_htmlFormErrors_defaultChrome.jpg' alt='Chrome 12 default error messages'>
          <figcaption>Chrome 13 default error message</figcaption>
        </figure>
        <figure>
          <img src='https://tylergaw.com/articles/assets/post_image_htmlFormErrors_defaultFirefox.jpg' alt='Firefox 5.0 default error messages'>
          <figcaption>Firefox 6.0 default error message</figcaption>
        </figure>
        <figure>
          <img src='https://tylergaw.com/articles/assets/post_image_htmlFormErrors_defaultOpera.jpg' alt='Opera 11.5 default error messages'>
          <figcaption>Opera 11.5 default error message</figcaption>
        </figure>
        <p>
          Of the three, only Chrome currently offers a way to apply custom styles to the message. Through non-standard, Webkit-specific pseudo classes you are able to style the messages in any way that floats your boat. That's what I'm focusing on with this article.
        </p>
        <h2>What about the other browsers?</h2>
        <p>
          That's a good question. I haven't heard anything about Opera, but it seems that the style and even handling of the error messages is an open discussion with Firefox. I did quite a bit of digging around in <a href="http://www.bugzilla.org/">Bugzilla</a> and in the FF 6 source code, but couldn't find any solid information on what the plan is. My hope is that a standard will emerge for styling the messages, but that could be far off.
        </p>
        <h2>The pseudo classes and markup</h2>
        <p>
          When I first started looking into if/how the error messages could be styled I quickly came across a "Rosetta Stone" in an article by Peter Gasston, <a href="http://www.broken-links.com/2011/06/16/styling-html5-form-validation-errors/">http://www.broken-links.com/2011/06/16/styling-html5-form-validation-errors</a>. In the post Peter explains the pseudo classes that webkit makes available to target the error message elements. He's done the leg work of digging through the Webkit source code to find this stuff, thanks Peter! To reiterate some of what is in Peter's article, the following are the classes that are available to hook into:
        </p>
        <pre><code>::-webkit-validation-bubble {}
      ::-webkit-validation-bubble-message {}
      ::-webkit-validation-bubble-arrow {}
      ::-webkit-validation-bubble-arrow-clipper {}</code></pre>
        <p>
          Each of those target a <code>&lt;div&gt;</code> element that is inserted into the DOM when a validation error is triggered. The markup looks like this:
        </p>
        <pre><code>&lt;div -webkit-validation-bubble&gt;
      &lt;div -webkit-validation-bubble-arrow&gt;&lt;/div&gt;
      &lt;div -webkit-validation-bubble-arrow-clipper&gt;&lt;/div&gt;
      &lt;div -webkit-validation-bubble-message&gt;Error Message&lt;/div&gt;
      &lt;/div&gt;</code></pre>
        <p>
          There are four more pseudo classes that were added as I was writing this article. These are currently only in the nightly builds of Chrome/Webkit and seem to just allow further control of the styling.
        </p>
        <pre><code>::-webkit-validation-bubble-icon {}
      ::-webkit-validation-bubble-body {}
      ::-webkit-validation-bubble-text-block {}
      ::-webkit-validation-bubble-arrow-heading {}</code></pre>
        <p>
          As of this writing and Chrome 13.0.782.112 these classes do not seem to have corresponding html elements. Only the <code>bubble-text-block</code> and <code>bubble-heading</code> contain default CSS rules and they are minimal and any CSS rules applied to these do not seem to affect any changes to the current validation error messages. We'll have to wait and see how these elements get put to use.
        </p>
        <h2>Default styles</h2>
        <p>
          A huge help when styling the messages is knowing the default styles that Chrome applies. Luckily the default Webkit CSS is available to peruse so you can see the exact properties that need to be modified. The default styles live here; <a href="http://trac.webkit.org/browser/trunk/Source/WebCore/css/html.css">http://trac.webkit.org/browser/trunk/Source/WebCore/css/html.css</a>
        </p>
        <h2>Demos</h2>
        <p>
          I set up three different demos using markup for a simple login form containing two fields. The first field is an email input that is required:
        </p>
        <pre><code>&lt;input type="email" name="email" required&gt;</pre></code>
        <p>
          The second field is a password input that is required:
        </p>
        <pre><code>&lt;input type="password" name="password" required&gt;</pre></code>
        <p>
          The rest of the markup for each demo is nearly identical, differences include; using <code>&lt;label&gt;</code> elements for the first demo and some different structural elements for each.
        </p>
        <p>
          To really work with the message styles, I wanted each demo to have a distinct style from the others. Demo 1 has a kind of standard-looking style with error messages that are big, bright and bubbly. Demo 2 has a sleeker, higher-tech, control panel-y look with a slimmed down error icon with no text. Demo 3 has an artsy, Draplin-inspired, Futura-filled look that carries over into big-ole, text only error messages.
        </p>
        <figure>
          <a href="http://lab.tylergaw.com/html5forms/errorStyles/01">
            <img src='https://tylergaw.com/articles/assets/post_image_htmlFormErrors_demo01.jpg' alt='Demo 01 style showing an error'>
          </a>
          <figcaption>Style 01; 37 Signals called, they want their form back</figcaption>
        </figure>
        <figure>
          <a href="http://lab.tylergaw.com/html5forms/errorStyles/02">
            <img src='https://tylergaw.com/articles/assets/post_image_htmlFormErrors_demo02.jpg' alt='Demo 02 style showing an error'>
          </a>
          <figcaption>Style 02; this is the login form for your Starcraft forum</figcaption>
        </figure>
        <figure>
          <a href="http://lab.tylergaw.com/html5forms/errorStyles/03">
            <img src='https://tylergaw.com/articles/assets/post_image_htmlFormErrors_demo03.jpg' alt='Demo 03 style showing an error'>
          </a>
          <figcaption>Style 03; hard to go wrong with fire engine-red Futura</figcaption>
        </figure>
        <h2>Fun with CSS animations</h2>
        <p>
          Something to keep in mind when styling the messages is that most any styles that are available in Chrome can be applied to the message bubbles, this includes animations. With demos 1 and 2 I created CSS animations to bring the messages into view. Since the message elements do not exist in the DOM until an error is triggered, the animations will not run until that time. So, in demo 1, to get that little shimmy and shake I'm using the following:
        </p>
        <pre><code>@-webkit-keyframes bounce {
      0% {
      opacity: 0.5;
      margin-left: 85px;
      }

      25% {
      margin-left: -35px;
      }

      50% {
      margin-left: 50px;
      }

      75% {
      margin-left: -22px;
      }

      100% {
      margin-left: -15px;
      opacity: 0.9;
      }
      }</pre></code>
        <p>
          The named animation is applied to the main message container;
        </p>
        <pre><code>::-webkit-validation-bubble {
      -webkit-animation-timing-function: ease;
      -webkit-animation: bounce 0.45s 1;
      ...
      }</code></pre>
        <p>
          Demo 2 uses a similar, but less complex animation to slide the serious looking icon in from the left. This demo also introduces something new; custom error message text. The message text, among many other things, can be customized through the Constraint Validation API.
        </p>
        <h2>Custom message text with the Constraint API</h2>
        <p>
          The Constraint API is a new one in the HTML Living Standard, it allows you to further customize form validation using Javascript. The API is large enough for a full article to explain its features and how to use it, here I'll just point out the code used to set a custom error message.
        </p>
        <pre><code>var inputs = document.getElementsByTagName('input'),
      len    = inputs.length,
      i      = 0,
      cur    = null,
      errMsg = '!';

      for (i; i &lt; len; i += 1) {

      cur = inputs[i];

      if (cur.getAttribute('type') !== 'submit') {
      cur.setCustomValidity(errMsg);

      cur.oninput = function () {
      if (!this.value || this.validity.typeMismatch) {
      this.setCustomValidity(errMsg);
      } else {
      this.setCustomValidity('');
      }
      }
      }
      }</code></pre>
        <p>
          This is a fairly simple block of code, we start by looking at each <code>input</code> element on the page through a <code>for</code> loop, if the <code>input</code> is not a submit button we then use the Constraint API method <code>setCustomValidity</code> to apply the custom message of "!" that we stored in the <code>errMsg</code> variable. In my opinion that should be the end of the work needed, but there is more.
        </p>
        <p>
          For some reason, when we set a custom error message with <code>setCustomValidity</code> that also removes the error checking from the input. If we were to stop here and submit the form, it would submit without triggering any errors. Starting at the line <code>cur.oninput</code> is the code needed to get around this oddity. The <code>oninput</code> event is fired each time a key is pressed that will change the contents of the input, each time the event fires we are checking to make sure the input has a value and that the value is valid according to the input type. The Constraint API makes available a <code>validity</code> property for <code>input</code> elements. This object contains a number of properties to determine the validity of the input, the one that we're using to make sure the value given is Ok is <code>typeMismatch</code>. If the input either has no value or the given value is not of the correct type, we need to set the custom message again. If a correct value has been entered we then set the custom validity to an empty string, which does a kind of reset on the validation. The input will then be validated normally again.
        </p>
        <h2>Going forward</h2>
        <p>
          This is definitely new, new stuff, and with all fun new things it will take some time before it can be used on a wide scale. I don't think that should be seen as a huge hinderance though. The Constraint API seems mature enough to be put into use right now, with the aide of a poly fill for browsers that don't yet support it. I haven't used it yet, but at least one exists already; <a href="http://afarkas.github.com/webshim/demos/demos/webforms.html">http://afarkas.github.com/webshim/demos/demos/webforms.html</a>. The error message bubbles are not really ready though. With only one browser supporting custom styles, and with no small amount of finagling needed to get them looking right, for now we'll need to keep creating our own error messages. I'll take it though, this is a huge step in the right direction and I can't wait for wider browser support and even more fun new things that will no doubt come with it.
        </p><p><i>Thanks for reading</i></p>]]>
    </description>
    <link>https://tylergaw.com/articles/fun-with-html-form-validation-styles</link>
    <guid>https://tylergaw.com/articles/fun-with-html-form-validation-styles</guid>
    <pubDate>Tue, 23 Aug 2011 09:33:55 EST</pubDate>
  </item>
  <item>
    <title>A Small Redesign with a Little Sugar</title>
    <dc:creator>Tyler Gaw</dc:creator>
    <description>
      <![CDATA[  <p>
        	<b>Take aways:</b> <em>Look at <a href="http://lab.tylergaw.com/jribbble/">this site</a> and fork <a href="https://github.com/tylergaw/jribbble.com">this code</a> if you don't feel like reading.</em>
        </p>
        <p class="entry-intro">
        	Since reading <a href="http://www.abookapart.com/products/responsive-web-design">Responsive Web Design</a> by Ethan Marcotte I've been chomping at the bit to design a site using all of the fancy things written about in that little, yellow, different book. To really take those new ideas and techniques for a ride I needed a small project to work on where I could just go crazy and try every new thing I could pull out of the book and my brain. Luckily I have a never-ending stream of personal projects that can always use a little TLC. The project that I picked to revamp is a small one page site for a jQuery plugin that I wrote last year, Jribbble. The site is quick overview of the methods that the plugin makes available, documentation of those methods and some working demos.
        </p>
        <h2>The design process</h2>
        <p>
        	Before starting a design I like to set some rules for myself. These aren't hard and fast rules, just some general thoughts that I keep in mind to guide the process and keep myself in check. The rules for this design were nothing outlandish, the design should:
        </p>
        <ul>
          <li>Use a flexible grid that remains comfortable from 320px to 1280px in width</li>
          <li>Be designed mostly in the browser with CSS, limited Photoshop work</li>
          <li>Not use any gradients</li>
          <li>Not use any Photoshop noise!</li>
          <li>Have some bits of CSS "sugar"</li>
        </ul>
        <p>
        	The last rule, "sugar", I think of as something extra, something possibly new, that you might not see that often. I'll talk about it more later.
        </p>
        <p>
        	The first graphic I envisioned for the design was the name, Jribbble. I saw the "J" and the "b"s in a large, swooping style. A style that I would not be able to accomplish with a computer. I would need to use my hands. The vision I had for it was too large for a pencil or pen or marker. I grabbed my box of acrylic paints, brushes, my 14" x 17" drawing pad and spread them out on my definitely-not-for-painting-on Ikea coffee table. With a medium sized brush and Primary Magenta I scrawled out a couple of "Jribbble"s as well as some random strokes. The toothbrush you see in the photo I used for making paint spatters and for getting paint all over the damned place.
        </p>
        <figure>
      	  <img src='https://tylergaw.com/articles/assets/post_image_jribbble_painting.jpg' alt='Jribbble painting'>
      	  <figcaption>The start of the Jribbble redesign; a hand-painted logo.</figcaption>
      	</figure>
        <p>
        	After the painting I took the "Jribbble" I liked most into Photoshop and fiddled with it a bit. I isolated the color from the background, applied the Twirl filter a few times to give it more character and added some of the random strokes and splatters around it for a more haphazard look. In Photoshop I set up a 958px, 12 column grid to work off of. For the banner and assorted page headers I selected <a href="http://typekit.com/fonts/botanika-mono-web">Botanika Mono Web from Typekit</a>. I don't own that font so I have to use a very advanced and elegant method for playing around with initial type layout. I input the text I need into Typekit's Type Tester and take screenshots of the text from there. See, fancy ain't it?
        </p>
        <figure>
      	  <img src='https://tylergaw.com/articles/assets/post_image_jribbble_design.jpg' alt='Jribbble design'>
      	  <figcaption>Getting out of Photoshop as soon as possible, this was the extent of the design accomplished with the graphics monolith.</figcaption>
      	</figure>
        <p>
        	The image above is the extent of the design accomplished with Photoshop. From that point on the rest of the design was created with good ole' CSS. Just like I have always done in Photoshop I went through quite a few style iterations; colors, type sizes, positioning, but instead of spending that time in Photoshop and only moving to the browser when it was "final" I used the medium that would be delivered to create the design. Designing in the browser, it feels good man.
        </p>
        <h2>The flexibility</h2>
        <p>
        	To accomplish my flexible grid, I started with a overall width of 80% on the content area. That number was somewhat arbitrary, I started with 100% and decreased the number until I felt there was a comfortable amount of room between the edge of the browser and the beginning of the content. I am setting a <code>max-width</code> of 1280px on the content area. Any wider than that and things started to get a little unwieldy; line-lengths were too long and in general things just didn't look good. At browser widths of 768px or narrower I wanted to gain back some of the margin so I used a media query to increase the width to 95%.
        </p>
        <pre><code>#content {
      margin: 0 auto;
      max-width: 1280px;
      width: 80%;

      @media screen and (max-width: 768px) {
      width: 95%;
      }
      }</code></pre>
        <p>
        	<em>Quick note on the media query. I'm using <a href="http://sass-lang.com/">Sass</a> which allows for nested styles. You'll see more of this in later code examples.</em>
        </p>
        <p>
        	In a number of areas of the design I use a two-column layout. To determine the widths of each column I used the oh-so-helpful formula that Ethan describes in <abbr title="Responsive Web Design">RWD</abbr>: <br>
        </p>
        <pre><code>target &divide; context = result</code></pre>
        <p>
        	For the columns I use the starting width of 958px as a context then determine a pixel value based off the width of each of my grid columns, 69px, to come up with widths like:
        </p>
        <pre><code>.method-description {
      h3 {
      float: left;
      width: 40.1878914%; /* 385 &divide; 958 = .401878914 */
      ...
      }

      p {
      float: right;
      ...
      width: 56.4718163%; /* 541 &divide; 958 = .564718163 */
      }
      }</code></pre>
        <h2>More on media queries</h2>
        <p>
        	Throughout the design I used media queries to add more fine tuning of styles that I couldn't accomplish with percentages alone. I decrease font sizes, switch to single column layouts and make other miscellaneous tweaks as the browser width decreases or increases to specified break points. For cross browser media query support I used the <a href="https://github.com/scottjehl/Respond">Respond.js</a> poly fill. It works great, media queries in IE7, you can't beat that with a stick! I needed to make some specific enhancements based on features as well so I used <a href="http://www.modernizr.com/">Modernizer</a> for feature detection. I included Respond.js in my Modernizer build.
        </p>
        <h2>The good stuff, the sweetness, the sugar</h2>
        <p>
        	How does a die-cut, scaleable background image sound? I thought it sounded pretty rad. I'll explain. I had the idea that I wanted the "Jribbble" graphic to be different colors at different times. One way to accomplish that would have been to make a number of different images in the various colors that I wanted. That would work, but I thought I could do it a more efficient way. What I did was create an image that had a set background color of <code>#ececec</code> with the text itself transparent allowing for a background color to show through.
        </p>
        <figure>
          <img src='https://tylergaw.com/articles/assets/post_image_jribbble_transparent.jpg' alt='Jribbble background image'>
          <figcaption>
            The die-cut .png used to create the color-shifting Jribbble logo.
          </figcaption>
        </figure>
        <p>
        	With die-cut taken care of, let's look at scaleable. At its base size of 1149px by 663px the logo graphic is pretty large. At medium to large browser widths, this works fine, as the browser width decreases or increases the graphic starts to lose its intended effect and also starts to cause readability issues for other page elements. <code>background-size</code> to the rescue!
        </p>
        <pre><code>#bigAssJribbble {
      background-image: url(../images/branding-die-cut.png);
      background-repeat: no-repeat;
      background-position: 15% 100%;
      -moz-background-size: 190% 140%;
      -webkit-background-size: 190% 140%;
      -o-background-size: 190% 140%;
      background-size: 190% 140%;
      padding-top: 45%;
      position: absolute;
      width: 100%;
      }</code></pre>
        <p>
        	A couple things are going on here. It's important to note that the branding-die-cut.png image is much wider than it needs to be. The graphic is flush left and the right side extends ~1049px and is filled with our background color <code>#ececec</code>. I do this so I am able to position the background image off the left side of the browser and not leave a gap on the right side. If there was a gap that the image didn't cover, any background color would show through where I didn't want. The width of the element is set to 100% so it is always the size of the browser. The <code>background-size</code> property does exactly what it says, it scales the background image to the percentages that I used. There isn't a science to those numbers, I just tweaked them until they looked right.
        </p>
        <p>
        	Another cool thing in that block of CSS is the <code>padding-top</code>. Since an element has to be taller than 0px for a background image to show and I didn't want to set an explicit height due to the changing graphic size I used the percentage padding to give the element a height. Again, no science with that number, just changed it until it worked.
        </p>
        <p>
        	Something that's not accounted for in the above CSS is the background color, the color that should show through the die-cut image. This is where I started having a lot of fun. I set up 11 media queries at different pixel max-widths from 320px to 2360px, at each of those I set a different background color for the logo as well as a number of different elements on the page. As the browser width increases or decreases the background color changes. Another little extra I use is a CSS transition to smoothly change from one color to another on each of the chameleon-like elements. An example element with a media query colorway:
        </p>
        <pre><code>#bigAssJribbble {
      -moz-transition: background-color 0.2s;
      -webkit-transition: background-color 0.2s;
      -o-transition: background-color 0.2s;
      transition: background-color 0.2s;
      }

      @media screen and (max-width: 480px) {
      #bigAssJribbble {
      background-color: #4400ff;
      }
      }</code></pre>
        <p>
        	I didn't get incredibly specific with what colors I chose. I started with a base of <code>#ff0066</code> and then apply cooler colors as the width decreases and warmer as it increases.
        </p>
        <p>
        	That's CSS sugar in my view. It's not something that everyone will see, but it's just a little extra for those that are looking and into that sort of thing.
        </p>
        <h2>That's all</h2>
        <p>
        	All in all a very fun process full of experimentation and learning for the sake of experimentation and learning. It probably took too long, so it goes. Side projects live in the off hours, the small windows of time before and after the "real" work and in the hours when you probably need to be sleeping.
        </p>
        <p>
        	Again, the completed site lives here <a href="http://lab.tylergaw.com/jribbble">http://lab.tylergaw.com/jribbble</a> and if you're interested in more of the code, fork it from its Github repo <a href="https://github.com/tylergaw/jribbble.com">https://github.com/tylergaw/jribbble.com</a>.
        </p>
        <p>
        	<i>Thanks for reading</i>
        </p>
      ]]>
    </description>
    <link>https://tylergaw.com/articles/a-small-redesign-with-a-little-sugar</link>
    <guid>https://tylergaw.com/a-small-redesign-with-a-little-sugar</guid>
    <pubDate>Sat, 09 Jul 2011 19:14:52 EST</pubDate>
  </item>
  <item>
    <title>All Talkie Talkie</title>
    <dc:creator>Tyler Gaw</dc:creator>
    <description>
      <![CDATA[<p>
  	    <figure>
  	      <a href='http://lab.tylergaw.com/whatsgoingonwithcss'>
  	        <img src='https://tylergaw.com/articles/assets/post_image_whatupcss.jpg' alt='View the presentation slides'>
  	      </a>
  	      <figcaption>
      	    View the slides
      	  </figcaption>
  	    </figure>
  	  </p>
  	  <p>
  	    On Tuesday, the 25th I presented an introduction of CSS3 features to a great group of folks at <a href="http://refreshnyc.org/">Refresh NYC</a>. The new features included in CSS3 are something that I'm really interested in and very excited about so it was a good opportunity to really dig in and learn a whole lot about them in a short amount of time. For my slides I decided to not take the Keynote route and instead decided to create the presentation as HTML, CSS, and Javascript. It turned out great, not only did I get to explore the sweetness of CSS3 for the sake of presenting the information, I also used a lot of the features I was talking about to build the presentation.
  	  </p>
  	  <p>
  	    This was the first time I had put together a presentation like this and got up in front of a crowd to present it, and I loved every minute of it. You really gain a deeper knowledge of a topic when you are trying to explain it to someone else. I'm definitely going to do this again in the near future and if you've had any interest in doing so I would highly recommend it.
  	  </p>
  	  <p>
      	<i>Thanks for reading</i>
      </p>]]>
    </description>
    <link>https://tylergaw.com/articles/all-talkie-talkie</link>
    <guid>https://tylergaw.com/articles/all-talkie-talkie</guid>
    <pubDate>Thu, 27 May 2010 11:18:00 EST</pubDate>
  </item>
  <item>
    <title>You Did What with What?</title>
    <dc:creator>Tyler Gaw</dc:creator>
    <description>
      <![CDATA[<p class="entry-intro">
        <a href='http://lab.tylergaw.com/themanfromhollywood'>
          <img src='https://tylergaw.com/articles/assets/post_image_hollywood.jpg' alt='View the demo'>
        </a>
      </p>
      <h2>Stuff You'll Most Likely Do Before Reading Any of This Post</h2>
      <p>
        <a href='http://lab.tylergaw.com/themanfromhollywood'>View the demo</a>
        <a href='http://github.com/tylergaw/themanfromhollywood'>Fork it</a><br>
        <em>You'll need Safari or Chrome to view the demo</em>
      </p>
      <p>
        There have been a ton of new, really exciting things going on with CSS over the past couple years and I've had an itch for a while to work with them. I started out just wanting to do a small project to get familiar with some of the newer, more advanced CSS3 features, and it quickly grew into a two month immersion into CSS3 land.
      </p>
      <h2>Do they speak English in what?</h2>
      <p>
        A few years back Jarrat Moody, a super talented designer, created a kinetic type <a rel='external' href='http://www.youtube.com/watch?v=HePWBNcugf8'>motion graphics piece</a> based on a scene from the movie Pulp Fiction; that I am still floored by to this day. Since I saw that I have wanted to create a similar piece and even made a few attempts using After Effects, none of which ever really materialized.
      </p>
      <h2>Let's do this!</h2>
      <p>
        So I have my excitement over a new feature set, and inspiration from Mr. Moody, time to get to it. <a href='http://lab.tylergaw.com/themanfromhollywood'>The Man From Hollywood</a> is a kinetic type animation using only HTML, CSS and Javascript. All of the animation is done using Webkit CSS transition, transforms, as well as standard CSS properties. Javascript just acts as a helper to turn CSS classnames on and off at the appropriate times. All of the content you see on the demo is HTML and CSS, no images were used. The audio clip is scene from the movie <a rel='external' href='http://www.imdb.com/title/tt0113101/'>Four Rooms</a>
      </p>
      <h2>Is it practical?</h2>
      <p>
        No, not really, but it's not really supposed to be. My goals were to teach myself the ins and outs of a new feature set, make something fun to look at, and hopefully help and/or inspire others to work on their own CSS3 awesomeness.
      </p>
      <p>
        If you enjoy it, you should <a href="http://twitter.com/home?status=@thegaw is the awesomest dude on the planet because of this: http://bit.ly/9rOtd4">Tweet it</a> or share it on your blog or just whisper the URL into a co-worker's ear. That last one would definitely be the best.
      </p>
      <p>
      	<i>Thanks for reading</i>
      </p>]]>
    </description>
    <link>https://tylergaw.com/articles/you-did-what-with-what</link>
    <guid>https://tylergaw.com/articles/you-did-what-with-what</guid>
    <pubDate>Tue, 06 Apr 2010 22:09:00 EST</pubDate>
  </item>
  <item>
    <title>I Less Than Three IE6</title>
    <dc:creator>Tyler Gaw</dc:creator>
    <description>
      <![CDATA[<p class="entry-intro">
    	  I am not a fan of the Die IE6 Campaigns, can't get behind them. You will not see me sporting some goofy <a href='http://twibbon.com/join/IE6-Must-Die' rel='external'>IE6 Must Die Twibbon</a> any time soon. I take a pretty unpopular position on IE6; I do not mind developing for IE6, working with it over the past few years has made me a better, smarter developer and I am going to miss it a bit when It's completely out of the picture. Whoa, look out, you're probably thinking I'm bat-shit crazy by now, but hear me out. I have reasons.
    	</p>
    	<p>
    	  <strong>1. It pushes you to write proper, lightweight HTML markup: </strong>
    	  IE6 has well-known issues with the box model, strange padding and margin issue, etc. One very good way to stop these potential CSS issues is to write good markup. Easy things like making sure you are writing semantic, valid markup and using <code>&lt;div&gt;</code> tags sparingly can knock out probably 95% of these issues that seem to be such a problem for so many. In my experience, most folks' CSS issues aren't CSS issues at all, they are HTML issues.
    	</p>
    	<p>
    	  <strong>2. It gives you yet another chance to get creative: </strong>
    	  So what if IE6 doesn't support transparent pngs. Find a way to make that image a transparent gif or a jpg. Doesn't look as crisp as the png? So what, this is where progressive enhancement steps in, this is why we have conditional comments. Set up an IE6 stylesheet and dump all of the not so desirable CSS in there. Chances are most of your users are not on IE6 anyway, and if they are this can be a friendly little jab at them for using it. I took this route when building my site. I wanted to let anyone viewing my site with The 6 know that they are missing out so I created a completely stripped down version just for IE6, plus a nice reminder of why. It still works, you can access all the same content, it's just not pretty.
    	</p>
    	<figure>
    	  <img src='https://tylergaw.com/articles/assets/post_image_tgawie6_thumb.jpg' alt='tylergaw.com IE6 Screenshot'>
    	  <figcaption>Users of IE6 receive a version of tylergaw.com that contains all the same content, but has a very pared-down aesthetic.</figcaption>
    	</figure>
    	<p>
    	  <strong>3. It forces you to clean up sloppy code that other browsers ignore: </strong>
    	  I've seen this a number of times when writing Javascript. Take a look at this bit of JS:
    	</p>
    	<pre><code>var someCrud = ['item01', 'item02', 'item03',];
alert(someCrud.length);</code></pre>
      <p>
        What's the length of the array? Firefox and Safari say 3. IE6 says 4. I'm siding with The 6 on this one, the comma at the end of the array is erroneous, it should not be there. IE6 sends the message that you should clean up your code by recognizing the extra item while the more advanced browsers ignore it and let you go on your sloppy way. This is just one example, I've come across a number of similar examples over the years, I'm sure others have too.
      </p>
      <p>
        <strong>4. It keeps you on your toes: </strong>
        If you're building websites you need to be flexible. Not all environments are going to be picture perfect, and knowing how to find solutions to problems that arise when working with a browser like The 6 is key. It's easy to just say that a browser is the sole reason for not getting something to function well. Another look at it may reveal that the first approach taken was maybe not the best. You just have to put on that thinking cap a bit.
      </p>
      <p>
        <strong>5. It's good to have a nemesis: </strong>
        What good is Batman without the Joker? You have to have someone or something that challenges you and makes your job harder, and sometimes downright frustrating. Web development is hard, that's why it's fun. How good a feeling is it to confront a problem in development and find some interesting way around it? I got a whole crappy article out of doing just that.
      </p>
      <p>
        This viewpoint is 100% from that of a developer. As a user, I agree using IE6 would just be a nightmare. The security issues, the lack of features that modern browsers have, etc., etc. That is one merit that the Die IE6 campaigns have. But every time I see some Johnny Developer complaining about how hard it is to build websites for IE6 and how much they hate it, I can't help but think that that person is missing out on opportunities to use their noggin. And that they are a bit of a wimp. :)
      </p>
      <p>
        After IE6 is gone some other browser will take it's place as the whipping boy. IE7? So, instead of just joining a campaign of wishing death on an inanimate object, I say learn to take away as much knowledge as you can from a less-than-ideal situation. Like Kuato said, "<a href='http://www.youtube.com/watch?v=xO1kKemcwYk' rel='external'>Open your miiinddddd.</a>"
      </p>
      <p>
        <i>Thanks for reading</i>
      </p>]]>
    </description>
    <link>https://tylergaw.com/articles/i-less-than-three-ie6</link>
    <guid>https://tylergaw.com/articles/i-less-than-three-ie6</guid>
    <pubDate>Mon, 27 Jul 2009 00:41:00 EST</pubDate>
  </item>
  <item>
    <title>Ch-ch-ch-changes!</title>
    <dc:creator>Tyler Gaw</dc:creator>
    <description>
      <![CDATA[<p class="entry-update">
        <b>Update:</b> This post was about the previous version of this site, <a href="http://v3.tylergaw.com">version 3</a>.
      </p>
    	<p class="entry-intro">
    	  I slapped a fresh coat on the old website. Well, new paint, siding, frame, foundation...who am I kidding? I just burned down the old one and built anew on its smoldering ashes.
    	</p>
    	<p>
    	  This is the third incarnation of tylergaw.com and just like the past two versions, I've had a damn good time working on this one. I've been able to take the time to work with so much amazing code that I've never really dug into before. This is the first full site I've built using the <a href='http://framework.zend.com/'>Zend Framework</a> which has just made me realize how completely rad it is working with a rock-solid framework. It also makes me kick myself a little for not picking it up a long time ago. I'm integrating with a number of different third party APIs, this is also the first time I've built a site doing that. How freaking cool is working with well-written, well-documented APIs?
    	<p>
    	<p>With the visual design, I've really tried to push it in a direction that I think is, "me". This was one of those designs that I could really see in my head before I opened Photoshop or even put one thing down on paper. The biggest inspiration came from the cover art for <a href='http://en.wikipedia.org/wiki/Women_%28novel%29'>Charles Bukowski's Women</a>. The whole time I was reading the book, I was constantly looking back at the cover. I don't know exactly what it is but it is just so damned cool. For this round, I started the design process the old fashioned way; I grabbed a six-pack, some colored pencils, a big-ass sketch book and then went to work.
    	</p>
    	<figure>
        <img class="image-vertical" src='https://tylergaw.com/articles/assets/post_image_women.jpg' alt='Bukowski - Women'>
        <figcaption>The cover of Charles Bukowski's "Women" was a main source of inspiration for the aesthetics.</figcaption>
      </figure>
      <figure>
        <img class="image-vertical" src='https://tylergaw.com/articles/assets/post_image_tgaw2009sketch.jpg' alt='tgaw2009 Sketch'>
        <figcaption>Starting a design with paper and pencil allows for quick iteration and time to really nail down the overall vision.</figcaption>
      </figure>
      <p>
        I'm a big fan of organic textures and knew that I wanted to incorporate some into the design. When I sat down in Photoshop to start turning my sketch into a working design, I realized quickly that I was going to need some real-world help getting the organic look and feel that I was going for. When it's time for real-world textures I just start grabbing <a href='https://tylergaw.com/articles/assets/post_image_art01.jpg'>anything</a>, and I mean <a href='https://tylergaw.com/articles/assets/post_image_samich.jpg'>anything</a>, and throw it on the scanner. You never know what you might use that scan of an old broken toothbrush for. Just to be sure, scan it!
      </p>
      <p>
        <i>Thanks for reading</i>
      </p>]]>
    </description>
    <link>https://tylergaw.com/articles/ch-ch-ch-changes</link>
    <guid>https://tylergaw.com/articles/ch-ch-ch-changes</guid>
    <pubDate>Thu, 18 Jun 2009 21:42:00 EST</pubDate>
  </item>
</channel>
</rss>
