Kyle Robertson

web development and design

I’m based in Vancouver, having just acquired my Bachelor of Arts degree in English Honours from the University of British Columbia. I’ve been working with web technologies for over a decade and have dabbled in the three c’s. I think testing and documenting code is a good idea, and I like Python.

github octocat Say hello or request my résumé.

Whole Note Symbol Whole Note

2012
solo development and design

When not working on her doctorate degree in comparative literature at the University of Toronto, Lara deBeyer teaches children and adults piano in her living room. She enlisted me to create a landing page for her just–launched business, Whole Note Music Studio. I prototyped the site in a couple days and we deployed it two weeks later.

github octocat Static HTML5 and CSS3 with respond.js for media-query browser support and Leaflet for an interactive OpenStreetMap.

Visit Whole Note Music Studio

style

Working with Lara’s suggestion of “something blue”, I designed a minimalist but visually engaging portfolio for Whole Note. The single–page site is fully responsive for a mobile–first browsing experience.

Subtle accents like bullet points made from eighth–notes and accidentals add playful but professional detail, as do typefaces Buena Park and Cronos Pro (both served via Adobe Typekit).

bullet and typeface examples
Example of Buena Park fontface

When I decided to start a music studio out of my home this summer and contacted Kyle about making a website, my ideas were unformed and, though hopeful, quite raw. I felt that I should have an online presence of some kind; Kyle quickly picked up the slack ends of my thoughts (I sent a knitting design as a colour template) and went to work.

The result was the best I could have hoped for.

He worked quickly and efficiently to create a distinct and interesting–looking website that both sets my business apart and appeals to my target demographic. He in­cor­por­ated everything that I asked for, consulted with me frequently, and gave thoughtful feedback. He was also kind enough to make me some flyers based on the graphics on the website.

The comments I have received about this site have been overwhelmingly positive. Most of my business comes through the site, and my clients (students and students’ parents) find it visually appealing and easy to navigate.

Lara deBeyer

Peck & Find bird Peck&Find

2008–10
solo development and design

Four years ago my sister Kailey left Canada to travel move to Australia. I designed a blog for her to keep in touch with her family back home and friends around the world. She tagged each post with her current location and from these an itinerary of her travels was plotted on a Google map.

Served by Django 1.0 via Python 2.5 and mod_wsgi on Apache 2. Browser support provided by Base2 (+DOM), Animator.js, and the Google Maps API.

Browse an archive of Peck & Find

Design

P&F’s style was intended to be whimsical yet subtle so as not to distract from Kailey’s writing. The site’s high-contrast pink and brown wordmark and argyle textures were a lot of fun to play with, as was designing the four sliding navigation icons.

Each post ends with a rule composed from silhouettes of buildings Kailey had photographed the year previous while touring the United Kingdom and mainland Europe. See if you can spot the Roman Colosseum or le moulin rouge mal famé.

blog post footer silhouettes
Building silhouettes

Django & OpenID

In place of Django’s user authentication system, OpenID was supported to encourage comments from LiveJournal and Blogger guests and to promote the consolidation of online identities.

Guest actions were audited by custom code which allowed almost every action to be undone rather than irreversibly committed. This design pattern streamlines the user interface by removing the need for scare screens.

A utility was also written to schedule queries made by Django’s ORM so as to execute them in order of their foreign key dependencies. The same data is requested as few times as possible while the relationships between model instances returned by those queries are kept intact. At best, the server is queried once for each (necessary) table in the database per HTTP request.

Excerpt from querybatch.py (Skip)
def resolve(self):
    """ Inspects pending models for relations and resolves them in the 
        order of their dependencies, such that relations with the most
        dependencies are resolved before those with fewer until there are
        no relations left.
    """
    # Inspect and clear pending models.
    pending = []
    for model, max_depth in self.models.items():
        self.weight(pending, Relation(to=model), 1, max_depth)
    self.models = {}

    # Remove duplicate relations and sort relations in order of weight
    # increasing.
    pending = list(set(pending))
    pending.sort()

    # Avoid rewriting model caches if no changes are made.
    rewrite_model_cache = False

    while pending:
        # The last relation is the heaviest, representing a field pointing 
        # to the model with the most number of dependant relations.
        relation = pending.pop()
        self.cluster.setdefault(relation.to, {})

        if self.resolved_objects(relation.to, self.get_ids(relation)):
            rewrite_model_cache = True

    if rewrite_model_cache:
        # Data has changed; rewrite model caches.
        for objects in self.cluster.values():
            map(self.write_model_cache, objects.values())

def weight(self, relations, parent, depth, max_depth):
    """ Returns 1 + the total number of children of parent (a relation
        with at least a 'to' model set) up to max_depth. Also stores child
        relations as a flat list for access by the calling method.
    """
    for field in self.inspect(parent.to):
        # Filter out fields from inherited models -- these will be caught
        # when the inherited model itself is inspected.
        if field.model != parent.to:
            continue

        child = Relation(parent=parent, field=field)

        if depth < max_depth:
            # Assign weight instead of incrementing it here.
            # (See next line.)
            child.weight = self.weight(relations, child, depth + 1,
                max_depth)

        parent.weight += child.weight

        relations.append(child)

    return parent.weight
                

Lil'Lucifer Lil’Lucifer By Holly Conrad

2007–10
solo development and joint design

During her undergraduate career at UCSB and the University of Edinburgh, Holly Conrad wrote and illustrated a comic for the web and student newspapers at those universities. The Lil’Lucifer site was kept minimalist—viewers can rate comics but not comment on them—yet highly interactive.

Originally served by Django 0.96 via Python 2.5 and flup on lighttpd. Browser support provided by Dean Edward’s event functions (circa 2005) and an early version of Animator.js.

View an archive of Lil'Lucifer

behaviour

While the site is fully functional without JavaScript, subtle animations complement CSS rollovers to make for a fun, engaging way of browsing comics without distracting from them.

Custom functionality included asynchronous requests for additional comic information in JSON format, and rating of comics using a hand-rolled XMLHttpRequest wrapper.

Excerpt from enhance.js (Skip)
// Sends a request for the comics new rating after the user has voted
onRate: function(e, href) {
  if(Request.disabled)
    return;

  e.preventDefault();

  // Prevent rating twice
  if(this.rateIndex || this.ratings[this.comics[this.index].id])
    return;

  // Send request
  this.rateIndex = this.index;
  this.ratingRequest.send("get", href.substring(0, href.indexOf("?")),
    this.onRatingChange.bind(this), null, true);
},
// Sets up rating transitions after the request has succeded
onRatingChange: function(req) {
  // Mark this comic as rated
  this.ratings[this.ratings.length] = this.comics[this.rateIndex].id;

  // Determine if the rating has changed
  var oldRating = this.comics[this.rateIndex].rating;
  var newRating = parseInt(req.responseText);
  this.toggleRatings(false);
  if(!isNaN(newRating) && newRating != oldRating) {
    this.comics[this.rateIndex].rating = newRating;

    // If the comic has changed since the request was called, we don't
    // need to make a transition
    if(this.rateIndex != this.index)
      return;

    // Set up the rating transition--text shrinks if rated down, grows if
    // rated up
    var to = (newRating > oldRating) ? 2 : 1;
    var fx = new Animator({duration: 400, transition: Animator.tx.elastic})
      .addSubject(new NumericSS(this.rating, "font-size", 1.5, to, "em"))
      .addSubject(new NumericSS(this.rating, "new-value", oldRating, newRating));
    fx.options.onStep = this.onStep.bind(this);

    fx.options.onComplete = (function() {
      // Reset callbacks
      fx.options.onComplete = function() { }
      fx.options.onStep = function() { }
      fx.seekTo(0);
    }).bind(fx);
    fx.seekTo(1);
  }
  this.rateIndex = null;
},
                

Icon Icon

2005–07
solo development and design

The Icon guild website was the first project I worked on that was actually used by other people—by 2007, over eight hundred guests were registered with the site. I quickly became embroiled in the intricacies of web development without being aware of the frameworks and tools that provide tested, best-practice solutions to banal problems (like how to separate user interface, logic, and data in a project).

Though my ignorance led me to reinvent many wheels, I gained an invaluable education designing and programming a user permissions system, blog, forum, and a couple custom applications from the ground up. I probably learned twice as much over again maintaining those components.

I was never able to “finish” Icon to my satisfaction, but it served as a vital laboratory in which to experiment with dynamic, database-driven web pages.

github octocat Deployed to a traditional LAMP stack using mod_rewrite for sane URIs. Browser support provided by Prototype 1.4 and moo.fx 1.2.3.

Check out an archive of Icon

XML + XSLT → XHTML

At the time, I found the idea of serving XML and translating it to XSLT as a templating solution appealing for its potential Ajax efficiencies. Learning XSLT and XPath introduced me to the joys of functional programming—but in verbose XML.

Icon runs an approximation of the MVC pattern: data is retrieved and modified by hand-written SQL queries rather than via an intermediary ORM; application logic is separated from view routing and XML composition.

Markup generated in PHP is translated to XHTML 1.1 server-side or, if the user agent supports it, client-side.

excerpt from fuzzdoc-xhtml11.xslt (Skip)
<x:template match="post" mode="comment">
  <div id="c{@id}">
    <x:choose>
      <x:when test="position() mod 2 = 1">
        <x:attribute name="class">odd secondary post</x:attribute>
      </x:when>
      <x:otherwise>
        <x:attribute name="class">even secondary post</x:attribute>
      </x:otherwise>
    </x:choose>
    <x:if test="@number">
      <div class="id">#<x:value-of select="@number" /></div>
    </x:if>
    <x:apply-templates select="toon" />
    <div class="meta">
      <img src="{$STATIC_URL}images/icons/64/{icon}" width="64" height="64" alt="comment icon {icon}" />
      posted by <x:apply-templates select="authored/by" /> on <x:apply-templates select="authored/at" />
      <x:if test="edited">
        <br /> edited
        <x:if test="edited/by != authored/by">
          by <x:apply-templates select="edited/by" />
        </x:if>
        on <x:apply-templates select="edited/at" />
      </x:if>
    </div>
    <div class="content {@color}"><x:apply-templates select="content" /></div>
    <x:if test="string-length(signature) &gt; 0">
      <div class="signature">
        <x:apply-templates select="signature" />
      </div>
    </x:if>
    <x:if test="not(../@preview)"><x:apply-templates select="hint" /></x:if>
  </div>
</x:template>
                

Permissions & Access Control

A flexible user rights system was designed to allow restrictions on individual content items, like news posts, articles, and forum threads. Each item is tagged with a permission “hint” which, with content type and CRUD action, indicates how a guest may interact with it.

Guests, in turn, are assigned “groups” specifying the content types with which they are able to interact, with which permission hints, and in which ways. With a single hint per item but multiple groups per guest, Icon was able to administer a fine grain of access control through a simple interface.

Icon Selector

The forums were the most active section of the site. To make them more visually interesting, posts by some guild members featured an avatar of their Warcraft character and a unique title describing their role in the guild.

Guests were also encouraged to distinguish their content by selecting an in-game icon. This double meaning of the guild name served as inspiration for the style of the site, intended to evoke Icon’s playful, often flippant, mood.

Over twenty-five hundred images from the game are indexed in the Icon Selector; they are loaded asynchronously on request.

post and comment icon selector
Icon selector

And so on

Looking for the rundown?

I markup semantic hypertext, design stylesheets, and script behaviour client-side. On the server, I program PHP or Python. I have experience with a bunch of relational databases (MySQL, PostgreSQL, and Microsoft SQL Server), various server configurations, and have worked with Adobe Photoshop for about a decade.

I know about doctypes, closures, and pointers; web standards, browser quirks, and accessibility. For revision control, I use subversion, git, or mercurial. I like JavaScript just fine on its own or supported by quality utilities developed by other talented people.

Web work continues to intrigue me because I get to do many different things. There is no dichotomy between form and function here—the tools of the designer are those of the developer.

And there is so much to learn! I want to know more about typography, load balancing, and project management. I’m curious about NoSQL databases and event-driven servers like node.js. I’d love to share my web expertise and passion for Django with other creative types. Let's make the web easier to use and less disappointing, one site at a time.

Mail me, electronically.

(loading typefaces)